kcode-k1-users 0.1.0

User facade over K1 accounts and persons
Documentation
# K1 users

`K1Users` registers an account and its permanent Person anchor as one consumer-facing user view. The caller keeps the supplied Accounts facade for authentication and authorization.

## Public API

```rust
use std::sync::Arc;
use kcode_k1_accounts::{K1Accounts, PersonId};
use kcode_k1_invites::{InviteCode, UserId};
use kcode_k1_persons::K1Persons;

pub struct NewUser;
impl NewUser {
    pub fn new(
        username: &str,
        full_name: &str,
        public_key: [u8; 32],
        tos_revision: u64,
        tos_digest: [u8; 32],
        acceptance_signature: [u8; 64],
    ) -> Result<Self, String>;
    pub fn username(&self) -> &str;
    pub fn full_name(&self) -> &str;
    pub fn public_key(&self) -> [u8; 32];
    pub fn tos_revision(&self) -> u64;
    pub fn tos_digest(&self) -> [u8; 32];
    pub fn acceptance_signature(&self) -> [u8; 64];
}

pub struct User;
impl User {
    pub fn user_id(&self) -> UserId;
    pub fn username(&self) -> &str;
    pub fn person_anchor_id(&self) -> PersonId;
    pub fn person_id(&self) -> PersonId;
    pub fn full_name(&self) -> &str;
    pub fn public_key(&self) -> [u8; 32];
    pub fn tos_revision(&self) -> u64;
    pub fn tos_digest(&self) -> [u8; 32];
    pub fn acceptance_signature(&self) -> [u8; 64];
}

pub struct K1Users;
impl K1Users {
    pub fn new(accounts: Arc<K1Accounts>, persons: Arc<K1Persons>) -> Self;
    pub fn register(&self, code: &InviteCode, user: NewUser) -> Result<User, String>;
    pub fn get(&self, user_id: UserId) -> Result<Option<User>, String>;
    pub fn find_by_username(&self, username: &str) -> Result<Option<User>, String>;
}
```

All three public types are `Send + Sync`. Their fields are private, and `NewUser` and `User` expose immutable values.

## Validation and behavior

`NewUser::new` applies the Accounts username, key, current Terms, digest, and signature validation. A full name preserves exact UTF-8, must contain 1 through 128 bytes, must contain no control character, and must contain a non-whitespace character.

Registration serializes calls through this facade. An existing username returns its current User only when every submitted Account field except the Person anchor matches; the submitted full name is then ignored and neither Persons nor Invites is mutated. A mismatch returns `username already registered` before either mutation.

A new username creates one Person with the exact full name and submits one Account registration with that anchor. There is no retry or deletion; if Account registration fails after Person creation, that Person remains orphaned.

Every returned User resolves the immutable Account anchor through a fresh Persons read. `person_anchor_id` remains the registered anchor, while `person_id` and `full_name` reflect current canonical resolution and updates. A missing anchored Person is an error rather than an absent User.

## Performance

Construction is linear in username, full-name, and fixed Terms verification work; reads use expected constant-time dependency lookups plus one name copy, while serialized registration performs at most one synchronous Person creation and one Account registration without a facade timeout or retry.