# API
`kcode-telegram-identity` is an in-process persistent directory. It performs no
Telegram networking, Kweb mutation, root creation, or credential handling.
The main values are `Directory`, `User`, `Group`, and typed `Error` /
`ErrorKind`. Open a directory with:
```rust
Directory::open(path: &Path, bootstrap_handle: &str) -> Result<Directory>
```
Opening creates or migrates the SQLite schema and ensures the normalized
bootstrap handle is preauthorized with permission to add handles. A
preauthorized handle is unresolved and contributes no numeric Telegram ID to
the transport whitelist until a trusted caller explicitly binds it:
```rust
Directory::authorize_user_id(
&self,
handle: &str,
telegram_user_id: i64,
) -> Result<User>
```
That call is the authorization boundary. Usernames, display names, and
`IdentitySink::observe_identity` observations are untrusted metadata and never
authorize or pin a numeric account. Exact numeric-ID authorization replays
idempotently; assigning a different ID to the handle, or an ID already assigned
to another handle, returns `Conflict`. Numeric IDs must be positive.
Directory queries and root completion operations are:
```rust
Directory::provisioning_users(&self) -> Result<Vec<User>>
Directory::provisioning_groups(&self) -> Result<Vec<Group>>
Directory::user(&self, telegram_user_id: i64) -> Result<User>
Directory::group(&self, group_id: &str) -> Result<Group>
Directory::group_for_root(&self, root: NodeId) -> Result<Group>
Directory::complete_handle_root(&self, handle: &str, root: NodeId) -> Result<User>
Directory::complete_user_root(&self, user_id: i64, root: NodeId) -> Result<User>
Directory::complete_group_root(&self, group_id: &str, root: NodeId) -> Result<Group>
```
Root creation remains the caller's responsibility. Completion only records an
already-created canonical Kweb `NodeId`. Check-and-set is one SQLite
transaction across independently opened `Directory` instances: an exact
completion replays, while reassignment to a different root returns `Conflict`.
`group_for_root` resolves only completed group mappings and returns `NotFound`
for unknown or unfinished roots.
`Directory` implements `kcode_tg_kennedy_bot::IdentitySink`. Observations retain
current usernames and display names for stable numeric identities; only
explicitly authorized numeric IDs appear in `WhitelistSnapshot`. An authorized
user with `can_add_users` may preauthorize another handle, but that handle
remains unresolved until `authorize_user_id` is called by a trusted caller.
Opaque Telegram groups may be observed and later assigned roots.
Telegram's anonymous-group pseudo-user (`GroupAnonymousBot`, numeric ID
`1087968824`) is removed during migration and ignored or rejected on every
runtime identity, authorization, lookup, whitelist, and delegated-add path. It
never becomes an observed Kennedy identity or whitelist candidate.
`Directory` uses SQLite WAL mode and a 15-second busy timeout. Methods persist
their documented mutation before returning success. Storage failures surface as
`ErrorKind::Storage`; callers must not treat an untrusted Telegram update as
authority to invoke explicit numeric authorization or root completion.