spacedb-access
SpaceDB Layer 5 — identity & access.
The consent layer, and the AI-age differentiator: inaccessible by default, accessible by mID-gated consent. Every read / write / compute is authorized by a signed, scoped, expiring, revocable capability issued by an owner's identity to a bearer — a human, or an AI agent with its own identity.
Part of SpaceDB. Dual-licensed MIT OR Apache-2.0.
[]
= "0.5"
The model
use ;
let now = 1_700_000_000;
let owner = generate.unwrap;
let directory = new;
directory.publish.unwrap;
let agent = from;
// Grant an AI agent read+write on one collection, expiring, with a spend cap.
let cap = grant.unwrap
.with_expiry
.with_budget // micro-$MATA it may spend
.with_delegation_depth; // it may sub-grant once, never broader
let signed = sign.unwrap;
let scope = Document ;
let request = AccessRequest ;
let decision = authorize.unwrap;
assert!;
authorize is the single chokepoint. It enforces, in order: issuer key
resolves → signature verifies → not revoked → bearer matches → scope covers the
request → op is granted → not expired. Every rejection is a named DenyReason
(UnknownIssuer, BadSignature, BearerMismatch, OutOfScope, OpNotGranted,
Expired, Revoked, Delegation(..), EmptyChain, NoCapability,
NotAccountable) — never a bare false.
Scopes and ops
Scope is Collection(name) (covers every document in it), Document { collection, doc_id }, or Function(name) for compute. Ops is a bitset of
READ / WRITE / COMPUTE, with contains and is_subset_of.
Delegation that can only narrow
delegate(&parent_chain, sub_capability, &delegator) appends a link to a
CapabilityChain; authorize_chain walks it. A link is rejected unless the
sub-grant's issuer is the link above it's bearer, the parent is delegable, the
depth decreases, and the scope and ops are a subset of the parent's. Scope
escalation is structurally impossible, not merely discouraged.
Revocation
RevocationSet::revoke(capability_id) kills a capability and, through
authorize_chain, everything delegated beneath it.
Human vs AI policy
AccessPolicy + gate(..) express the stance a deployment takes:
roster humans read freely; an AI agent with no grant is denied NoCapability;
requiring_accountable_agents() additionally requires an agent's chain to root
at an accountable roster member (NotAccountable otherwise).
Tamper-evident audit
AuditLog::record(..) appends a hash-chained entry per decision (who, what
scope, which op, which capability, allow/deny) signed by the node identity;
verify(node_public_key) re-walks the chain and fails on any edit, reorder, or
deletion.
Seams an operator fills
KeyDirectory— DID → published key.MemKeyDirectoryships for self-hosting and tests; MATA resolvesdid:mataover IAMHUMAN.
Identities are ECDSA P-256 / ES256, the same primitive as mID — so MATA's real mIDs verify here identically, with no MATA dependency in this crate.
Testing
The workspace defaults to wasm32; this crate is native. Test on your host
triple:
Suites: authorize.rs, delegation.rs, revocation.rs, policy.rs,
audit.rs, consent_flow.rs.
License
MIT OR Apache-2.0, at your option. See LICENSE-MIT and LICENSE-APACHE.