Docs / Guides
contract authority · 50-authority.md · 51-enforcement.md · guide · card
Authority and enforcement
The problem authority solves
A caller reads stored rows on behalf of a person, and the store holds rows that person
may not see. Admission verifies the caller’s credential. A checkpoint is the
code in a command, server or gateway that performs admission and produces the verified
permissions every read and write takes (admitted-authorityauthority.verify.admitted-authorityVerification yields an admitted-authority value carrying the normalized subject tuple and its grants. Every read surface and row-landing effect takes that value as an argument; nothing downstream re-parses a credential or reads ambient state.).
Enforcement applies those permissions to each read, deciding which rows and cells
the caller receives.
What a credential says
A subject is a tuple of agent, host, principal, task, zone and incognito flag
(subject-tupleauthority.identify.subject-tupleA subject is a tuple of agent, host, on_behalf_of principal, task scope, inference zone and incognito flag. A credential binds the subset it names and admits under that combination alone.). The mint verifies only the principal,
against the identity provider (on-behalf-ofauthority.identify.on-behalf-ofon_behalf_of is verified at the mint against the issuing identity provider, and admission carries it forward as the reader's identity.).
The credential is a chain-signed token under a versioned profile that names every fact
the engine admits (delegation-profileauthority.profile.delegation-profileDelegated authority travels in one attenuable, chain-signed library format. The library owns serialization, signatures, block chaining and evaluation; a versioned profile names every fact, check and restriction the engine admits.). Its grants name actions,
table patterns and optional tenant, template and aggregate bounds
(fieldsauthority.grant.fieldsA grant names actions, table patterns, and optionally a tenant scope, aggregate constraints, a template allowlist and a row ceiling. An absent constraint leaves its dimension unconstrained; an absent allowlist confers no template.).
Getting a credential
The example reader signs into the console. The console holds no project-wide credential;
it exchanges the reader’s identity assertion for that reader’s short-lived credential
(per-readerauthority.exchange.per-readerAn embedding application holds no project-wide credential; per request it exchanges its signed-in reader's assertion for that reader's short-lived credential and reads as them.). The policy maps the reader’s role to read on
workspace/*, and the assertion’s tenant claim becomes the tenant scope acme-eu on
every grant (tenantauthority.exchange.tenantThe assertion claim tenant_claim names becomes the tenant scope of every minted grant; no subject member carries tenancy.).
A direct mint, outside the exchange, needs a credential whose grant carries admin
(unauthorized-mintauthority.issue.unauthorized-mintA mint request presenting no grant carrying admin raises IssuanceUnauthorized.).
Narrowing for a sub-agent
The reader’s agent spawns a sub-agent. A holder derives a narrower child offline by
appending a signed block, with no issuer round trip (offlineauthority.attenuate.offlineA holder derives a narrower child with no issuer round trip by appending a signed block. The parent's bytes stay unchanged, independently verifiable and independently withdrawable.).
The agent derives one child per sub-agent, bound to that sub-agent’s own key pair
(per-sub-agentauthority.attenuate.per-sub-agentAn agent derives one child per sub-agent, each bound by its confirmation claim to that sub-agent's own key pair.). A derivation that adds write fails, naming
the widened dimension (widensauthority.attenuate.widensA child broader than its parent on actions, tables, templates, aggregate constraints, or a max_rows, max_duration_ms or max_response_bytes ceiling raises AttenuationWidens, naming the dimension.).
A horizon on a grant bounds the availability time of every read its holder makes
(horizonauthority.grant.horizonA grant may carry horizon, an instant bounding the availability time and since of every read its holder makes, under inherited-bound and exceeds-horizon; an absent horizon leaves availability unbounded.).
Admission
A checkpoint holds public keys only, so admitting a request calls no outside service
(public-key-onlyauthority.verify.public-key-onlyA checkpoint holds public key material and no signing secret, and admits with no call to an issuer, identity provider or policy service.). Each request under a key-bound credential
carries a proof signed by the matching private key (possession-bindingauthority.verify.possession-bindingA confirmation claim holds a client public-key thumbprint. Each request under a credential carrying one bears a proof signed by the matching private key over method, target, body digest, issue instant and nonce; a credential carrying none requires no proof.).
Each statement start and each commit re-reads expiry, revocation and policy version
(effect-boundaryauthority.verify.effect-boundaryA statement's start and each commit are effect boundaries. Each boundary re-reads expiry, revocation and policy version against the carried value; a lapse between boundaries stops the effect at the next one.). Short lifetimes are the primary withdrawal
(short-lifetimeauthority.revoke.short-lifetimeA short lifetime is the primary withdrawal: a credential's remaining lifetime bounds its exposure after a provider sign-out or before a denylist entry propagates.).
An operator adds the sub-agent’s derivation to the denylist every checkpoint reads
(denylistauthority.revoke.denylistA checkpoint reads a denylist keyed on credential identifier. An entry ages out once its identifier verifies under no live key version.). At its next effect boundary the sub-agent’s credential
fails (revokedauthority.revoke.revokedA credential on the denylist, or carrying an epoch below the current scoped epoch, raises AuthorityRevoked at the next effect boundary.). The parent credential carries its own revocation
identifier and keeps working (revocation-idauthority.revoke.revocation-idEvery derivation carries its own revocation identifier, withdrawing one subtree without ending the root it descends from.).
Enforcement in three layers
Enforcement is three layers: removal at write time, a redistribution bound at push, and
row and column restriction at query time (composeauthority.composeThe three layers as one reference monitor, and the order a registered relation applies.).
Write-time redaction drops or transforms a value before columnar bytes exist
(write-timeauthority.redact.write-timeRedaction runs inside the writer before columnar encoding. Each rule drops its value or transforms it into a policy-defined substitute: keyed digest, token, prefix or generalized band.). It is the only layer protecting data at rest; a
column protected at query time alone is cleartext in storage.
A cleared redistribution flag withholds the whole table from the bucket at push
(withholdauthority.bound-redistribution.withholdA cleared redistribution flag withholds every object of the table at push and omits the table's name from the bucket manifest.).
The sub-agent queries workspace/documents with no tenant filter. The engine conjoins a
byte-exact tenant equality the query text omits (tenant-equalityauthority.filter-rows.tenant-equalityA tenant-scoped grant compiles a bound-parameter byte equality on the tenant column, conjoined by the engine; no consumer statement carries it.).
A statement naming acme-us fails, naming both scopes (scope-deniedauthority.refuse.scope-deniedA tenant-scoped credential reading another tenant's rows on a granted table raises EnforceScopeDenied, as wire code scope_denied, HTTP 403 or an in-band tool error, naming the table and both scopes.).
A refusal returns a typed error, not an empty result (not-emptyauthority.refuse.not-emptyA scope refusal is a typed error, never an empty result.).
A contact_email column masked by policy arrives masked where it stands
(in-placeauthority.mask.in-placeThe masked projection substitutes named columns where they stand; the caller receives the column set and order the unmasked statement produces, and expands to it.). The table declares no zone policy, so it resolves to a
fail-closed pair of zones (fail-closedauthority.place.fail-closedA table declaring no zone policy resolves to the fail-closed pair local:device and on-prem:, declared and discovered tables alike.). The response envelope
reports the zone’s removals and carries no removed value (envelopeauthority.place.envelopeA response envelope carries the zone's removals, the asserted zone and the incognito flag in restriction-block, and no removed value.).
Every step completes before the engine cuts a ranked result to size, so visible positions carry no
trace of a withheld row (before-the-cutauthority.compose.before-the-cutEvery step, zone exclusion included, completes before a ranked result is cut to its requested size; visible positions carry no trace of a withheld row.).
Injected instructions
Text read from a source sometimes holds instructions aimed at the model. An
organization-wide face serves only the read tools, and the server authors every
conversational write (read-only-faceauthority.resist.read-only-faceAn organization-wide face serves the read subset of the tool surface; conversational writes are server-authored.). A claim write holding a
credential-shaped value fails before storage (credential-shaped-valueauthority.resist.credential-shaped-valueA claim write, direct or synthesized, raises EnforceCredentialShapedValue on a field matching a credential shape, before it reaches storage.).