# K1 access
`kcode-k1-access` is the synchronous, Groups-aware facade for the logical KTO subsystem `k1-access-subsystem`. `K1AccessDriver` owns 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_many(
&self,
user: UserId,
model: ModelId,
access_ids: &[AccessId],
expected_subsystem: SubsystemId,
) -> Result<Vec<AccessCheck>, String>;
pub fn resolve_visible_targets(
&self,
user: UserId,
model: ModelId,
targets: &[Target],
expected_subsystem: SubsystemId,
) -> Result<Vec<Option<AccessId>>, String>;
pub fn list_user(
&self,
principal: RequestPrincipal,
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_user_group_targets(
&self,
principal: RequestPrincipal,
expected_subsystem: SubsystemId,
) -> Result<Vec<(AccessId, Target)>, 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.
## Batched visibility checks
`check_many` and `resolve_visible_targets` keep `user` and `model` separate at the public boundary. Each nonempty call builds its `RequestPrincipal` only for the driver call; the facade retains neither identity nor membership state.
An empty `access_ids` or `targets` slice returns `Ok(Vec::new())` immediately. It does not call Groups or the driver. There is no input-count limit, retry, cache, or per-item fallback.
For a nonempty batch, the facade calls `K1Groups::memberships(user, model)` exactly once and makes exactly one corresponding driver batch call. It passes both returned group slices. `check_many` also passes that same memberships revision, so all checks use one coherent membership snapshot.
The driver preserves positional output and duplicate inputs. `resolve_visible_targets` returns `Some(AccessId)` only when the exact requested target belongs to `expected_subsystem` and is visible to both the user and model. Unknown targets, targets in another subsystem, and inaccessible targets are all `None` in their original positions.
These batch APIs do not inspect or repair Access discovery. In particular, they do not call `discovery_missing` or `ensure_discovery`, read SQLite directly, or transform driver results.
## 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.
`list_user` derives the principal user and delegates directly. `list_group` 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`. It does not read model membership, aggregate groups, perform per-ID checks, or copy group results into personal discovery.
`list_user_group_targets` calls `groups_for_user` exactly once, then delegates exactly once with the principal user, all current human groups returned by Groups (including `ALL_USERS`), and the expected subsystem. The driver combines the personal and group discovery indexes and owns canonical ordering, deduplication, subsystem filtering, and target resolution. The facade performs no per-item authorization checks, model-membership lookup, or discovery repair.
Discovery results can contain stale entries and are not authority. A caller must use `check` or `check_many` when current authorization is required. Listing does not call `ensure_discovery` and does not submit or mutate Access transactions, storage, or wire state.
## 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 and driver failures are returned unchanged without facade retry or additional driver state changes. 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`, a successful `check`, or either nonempty batch API; it performs one Groups reverse query for `list_group` or `list_user_group_targets`; and it performs no Groups query for `create`, `list_user`, or either empty batch API. The driver and Groups scheduling have no finite wall-clock bound.
## Testing
The crate has compile-time facade signature tests for both batch methods. Empty-batch call ordering requires a real `K1Groups` and driver instance, so it is verified by the downstream full-stack testkit rather than adding a mock or test-only abstraction to this facade.