# Public API
```rust
use std::{path::Path, str::FromStr, sync::Arc};
use kcode_k1_invites::{
InviteCode, InviteStatus, K1Invites, Registration, RegistrationKey, UserId,
};
use kcode_k1_peering::K1Peering;
use kcode_k1_txn_ordering::{K1TxnOrdering, TxId};
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct InviteCode;
impl InviteCode {
pub fn expose(&self) -> String;
}
impl FromStr for InviteCode {
type Err = String;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum InviteStatus {
Unknown,
Unused,
Consumed,
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct UserId;
impl UserId {
pub const fn from_tx_id(txid: TxId) -> Self;
pub const fn as_tx_id(self) -> TxId;
}
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub struct RegistrationKey;
impl RegistrationKey {
pub fn from_bytes(bytes: [u8; 32]) -> Self;
pub fn as_bytes(&self) -> &[u8; 32];
}
#[derive(Clone, Eq, PartialEq)]
pub struct Registration;
impl Registration {
pub fn user_id(&self) -> UserId;
pub fn registration_key(&self) -> RegistrationKey;
pub fn data(&self) -> &[u8];
}
pub struct K1Invites;
impl K1Invites {
pub fn open(
root: &Path,
ordering: Arc<K1TxnOrdering>,
peering: Arc<K1Peering>,
) -> Result<Self, String>;
pub fn status(&self, code: &InviteCode) -> Result<InviteStatus, String>;
pub fn create(&self) -> Result<(TxId, InviteCode), String>;
pub fn consume_with_data(
&self,
code: &InviteCode,
registration_key: RegistrationKey,
data: &[u8],
) -> Result<UserId, String>;
pub fn registrations(&self) -> Result<Vec<Registration>, String>;
}
```
`InviteCode` implements `Clone`, `Copy`, `Debug`, `Eq`, `Hash`, `PartialEq`, and `FromStr<Err = String>`. Parsing accepts exactly eight canonical, case-sensitive, URL-safe unpadded Base64 characters encoding six bytes. `expose` creates an ordinary plaintext `String` owned by the caller. Invite codes are ordinary bearer data and are not zeroized.
`InviteStatus` implements `Clone`, `Copy`, `Debug`, `Eq`, and `PartialEq`. `UserId` implements `Clone`, `Copy`, `Debug`, `Eq`, `Hash`, and `PartialEq`. `RegistrationKey` implements `Clone`, `Copy`, `Eq`, `Hash`, and `PartialEq`. `Registration` implements `Clone`, `Eq`, and `PartialEq`. All public types are `Send + Sync`.
# Wire contract
Creation draws the six code bytes from the operating-system CSPRNG. The direct code enters the Issue transaction and projection; there is no verifier key, HMAC, or commitment.
Only payload version `3` is accepted. An Issue is exactly `[3, 1, code:6]`, totaling 8 bytes. A Consume is exactly `[3, 2, issue_id:12, registration_key:32, opaque_data:remaining]`, with a minimum length of 46 bytes. Consume carries no code: the projection derives the accepted registration's code from the referenced winning Issue. There is no compatibility decoder. Truncated Issues, truncated Consumes, extended Issues, other versions, and unknown kinds are structurally malformed.
# State and operations
`open` opens the durable invite projection at `root`, reads its snapshot, and registers the exact subsystem `k1-invites-subsystem` with the supplied KTO strictly after the projection checkpoint. No checkpoint means replay from genesis. The Peering handle must submit through that same live KTO. Registration completes before `open` returns.
`status` reads only accepted facade state by direct code under the brief state lock. It returns `Unknown` when no accepted winning Issue exists, `Unused` when an Issue is accepted and no Consume is accepted (including while a Consume is pending), and `Consumed` when an accepted registration exists. An unavailable instance returns an error. `status` performs no I/O, submission, or mutation.
The first canonical Issue for a code wins. `create` reserves a newly drawn code only within the instance, submits one Issue, and returns only when that Issue or an Issue accepted by its synchronous callback owns the code. A projected or same-process pre-submission collision causes a fresh draw, as does a projected collision when a successful submission returns a losing ID. A submission error is never retried, but if its synchronous callback has already established the requested Issue as accepted, `create` returns that accepted Issue ID and code as success. Otherwise the error is returned and may identify a committed transaction.
The first canonical Consume wins only when its Issue ID identifies the winning unconsumed Issue and its registration key is unused. `consume_with_data` resolves the winning Issue ID from the supplied code before submission. It rejects an unknown Invite, a registration key already accepted or pending for another Invite, and a differing key or opaque byte sequence for an already accepted or pending request. An identical accepted request returns its existing `UserId` without another transaction. Concurrent identical calls for one Invite share the submitted call's result. After a successful submission, the returned ID must equal the accepted registration's `UserId`, and the callback validates the registration's issue ID and code against the referenced Issue; a semantic loser returns an error. A submission error is never retried, but if its synchronous callback has already established the exact requested registration key and opaque bytes as the accepted registration, `consume_with_data` returns that accepted `UserId` as success. Otherwise the error is returned and may identify a committed transaction.
`registrations` reads a projection snapshot and returns accepted registrations in canonical Consume acceptance order. Opaque data is returned byte-for-byte without interpretation. Invite codes are plaintext in Issue transaction bytes and the local projection. Registration keys and opaque data are plaintext in Consume transaction bytes and the local projection. The library makes no confidentiality claim for any of them. There is no arbitrary opaque-data limit; representational length overflow or fallible reservation failure is reported before submission.
Only accepted state-changing actions are durably appended and checkpointed. New semantic losers are callback-success no-ops without projection store access or checkpoint advancement. Identical replay of an accepted action is a durable duplicate no-op that repairs a lagging checkpoint. Conflicting reuse of an accepted action ID is an error.
A structurally malformed canonical payload returns a callback error, faults the KTO registration, and makes the instance unavailable without projection or checkpoint advancement. Projection errors also make the instance unavailable. Reorganization makes the instance unavailable and discards its projection-owned state. No live instance retries, repairs, reconstructs, or migrates state; recovery requires a fresh `open`, which replays from genesis after discard.
# Concurrency, failures, and performance
One instance uses only brief facade locks for availability, indexes, and reservations. Brief hash-table reservations may occur under the facade lock, while no facade lock is held across Peering, projection I/O, waits, randomness, or opaque-byte comparison. Calls waiting on one identical pending Consume do not block unrelated facade work. The projection owns irreducible serialization of its durable root, and KTO owns same-subsystem callback order. Separate invite roots and unrelated KTO subsystem lanes do not share facade coordination. There are no workers, retries, timeouts, queues, or background tasks.
`status` performs bounded O(1) hash-table lookup work under its brief state lock. A collision-free `create` performs fixed-size randomness work plus one synchronous Peering submission; pre-submission code collisions repeat that fixed work. `consume_with_data` performs work linear in opaque bytes plus one submission and projection persistence. `registrations` performs work linear in accepted registration count and opaque bytes. `open` is linear in projection materialization and KTO replay after its checkpoint. Peering, KTO, projection storage, entropy, allocation, and related-call waits have no finite wall-clock bound.
Invite codes contain 48 bits of bearer entropy and are present in canonical Issue data. Any public endpoint must add transport security, uniform unavailable responses, and strict edge, source, and global attempt limits. A `UserId` is a durable identity seed, not an account, session, authentication proof, or authorization grant.