kcode-telegram-identity 0.1.6

Persistent Telegram identity authorization and Kmap-root directory
Documentation
# 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 the first non-anonymous, positive identity
observation with an exactly matching normalized username binds it, or a trusted
caller explicitly binds it:

```rust
Directory::authorize_user_id(
    &self,
    handle: &str,
    telegram_user_id: i64,
) -> Result<User>
```

Handle-first observation is a trust-on-first-use path. The observation and
binding occur in one immediate SQLite transaction. Observations without an
eligible exact handle match remain metadata only. Once bound, the numeric ID
remains authoritative across later username changes. An ID or handle already
bound elsewhere is never reassigned. Explicit numeric-ID authorization remains
an independent trusted administrative path, replays idempotently, and returns
`Conflict` for incompatible assignments. 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 and resolve
eligible preauthorized handles through the handle-first TOFU rule. An authorized
user with `can_add_users` may preauthorize another handle. 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 should preauthorize handles deliberately because
an exact first observation may bind the matching numeric account.