kcode-k1-access 0.4.0

K1 access Groups-aware authorization facade
Documentation
# K1 access

`kcode-k1-access` is the synchronous, Groups-aware facade for the logical KTO subsystem `k1-access-subsystem`. `K1AccessDriver` owns the transaction submission, callback correlation, projection, discovery storage, and availability lifecycle. This facade owns only the Groups calls required to supply membership evidence to that driver.

## Public API

```rust
use std::{path::Path, sync::Arc};
use kcode_k1_access::{
    AccessCheck, AccessId, AccessRevision, Authorizations, GroupId, K1Access, ModelId,
    OwnerSubject, RequestPrincipal, SubsystemId, Target, TxId, UserId, ViewerSubject,
};
use kcode_k1_groups::K1Groups;
use kcode_k1_peering::K1Peering;
use kcode_k1_txn_ordering::K1TxnOrdering;

impl K1Access {
    pub fn open(
        root: &Path,
        ordering: Arc<K1TxnOrdering>,
        peering: Arc<K1Peering>,
        groups: Arc<K1Groups>,
    ) -> Result<Self, String>;
    pub fn create(
        &self,
        target: Target,
        authorizations: Authorizations,
    ) -> Result<AccessRevision, String>;
    pub fn set_authorizations(
        &self,
        principal: RequestPrincipal,
        access_id: AccessId,
        authorizations: Authorizations,
    ) -> Result<AccessRevision, String>;
    pub fn check(
        &self,
        principal: RequestPrincipal,
        access_id: AccessId,
        expected_subsystem: SubsystemId,
    ) -> Result<AccessCheck, String>;
    pub fn check_user(
        &self,
        user: UserId,
        access_id: AccessId,
        expected_subsystem: SubsystemId,
    ) -> Result<AccessCheck, String>;
    pub fn list_user(
        &self,
        principal: RequestPrincipal,
        expected_subsystem: SubsystemId,
    ) -> Result<Vec<AccessId>, String>;
    pub fn list_for_user(
        &self,
        user: UserId,
        expected_subsystem: SubsystemId,
    ) -> Result<Vec<AccessId>, String>;
    pub fn list_group(
        &self,
        principal: RequestPrincipal,
        group: GroupId,
        expected_subsystem: SubsystemId,
    ) -> Result<Vec<AccessId>, String>;
    pub fn list_group_for_user(
        &self,
        user: UserId,
        group: GroupId,
        expected_subsystem: SubsystemId,
    ) -> Result<Vec<AccessId>, String>;
}
```

The facade reexports the complete consumer type contract from `kcode-k1-access-types`, including `SubsystemId`. `open` delegates to the driver, and `create` passes the supplied target and normalized authorizations to it without grants or defaults.

## Authority and discovery

`set_authorizations` calls `K1Groups::memberships` exactly once. It passes the returned human groups to `K1AccessDriver::owner_witness`; model membership never supplies management authority. A missing witness returns `principal is not an access owner`. The driver receives the principal user as actor, the Groups revision, that witness, and the requested authorizations.

`check` first calls `discovery_missing`. A missing discovery record causes exactly one `ensure_discovery` call, which must succeed before the facade calls Groups once and delegates the check with that coherent membership snapshot. Unknown IDs and IDs in another subsystem report no missing discovery and cause no ensure. Discovery is a transaction-backed monotonic stale derived index, not authority: it is only added by the ordinary Access transaction flow and does not grant access. Driver reorganization clears its projection and invalidates the driver.

`check_user` preserves the same discovery-missing and ensure-discovery sequence. It calls `groups_for_user` exactly once and supplies only the authenticated user and returned human groups to the driver. It intentionally supplies no Groups revision evidence (`None`); user-only `AccessCheck` revision data is evidence, not authority. Model-only and model-group-only authorization cannot satisfy this user-only check.

`list_user` derives the principal user and delegates directly. `list_for_user` delegates directly to the discovered user index. `list_group` derives the principal user and uses `list_group_for_user`. `list_group_for_user` calls `groups_for_user` exactly once, requires the requested group in that result, including `ALL_USERS` when Groups returns it, and otherwise returns `access denied`. These methods do not read model membership, aggregate groups, perform per-ID checks, or copy group results into personal discovery.

## Ordering, errors, concurrency, and performance

The driver opens and registers the Access callback after its durable projection cursor. It owns operation IDs, encoding, one submission, exact callback correlation, projection faults, reorganization handling, and mutation/query errors. Its documented transaction and callback failure contract applies unchanged; recovery after driver unavailability requires a fresh `open`.

Groups failures are returned without changing driver state. There is no facade lock, retry, timeout, worker, queue, polling, background recovery, authentication, HTTP surface, deployment behavior, or live behavior. The facade performs one Groups membership query for `set_authorizations` and a successful `check`, one Groups reverse query for `check_user` and a group-qualified list, and no Groups query for `create`, `list_user`, or `list_for_user`. The driver and Groups scheduling have no finite wall-clock bound.