ADR-027: Permissions

Date: 2026-08-05 Status: Stub, except TOKATTEST. The statements are made; the phases say what is decided.

Statements

"nil is nil"

The credential does not carry the permission

"i want to be able to change it at will"

Grant fuses who the caller is with what they may do. A credential says who, a policy says what, and they are edited apart, change the policy and every credential under it changes at once, untouched.

TOKATTEST — a token that attests as itself

ATTESTOR is a token that can attest, minted by the User that owns it (ADR-031).

"each token is its own actor in the sg: by"

A node opens a namespace on the first request that names it, so a token is minted for any namespace its minter is admitted to. A token reaching several says which one a request is; a write lands somewhere definite or nowhere. TODO: find an example or source the claim by pointing at the code

27-2 — ground

Policy is declared in ground's controls and attested into the node. The mutable field from TOKATTEST leaves the credential and becomes one of those.

Blocked on: nothing in ground's evaluation path takes an actor. scopeMatches takes a cwd, evaluatePermission takes a cwd and a command, CheckFn takes a cwd and an input, and the actor on every attestation it emits is the literal ground.

TODO: So, this part is actually not done yet, but also needs to wait until the box builds ground like it does datapunt

27-3 — every part of QNTX behind it

Not conformed

The statements govern the admission path: a request is admitted, storeFor decides its namespace from that admission, and a non-ROOT write cannot land in system (storeFor refuses it without MaySeeSystem). A handler that wrote through storeIn directly, rather than through admission, sidestepped this — it wrote as the node into whatever namespace it named. Naming a namespace was ambient: storeIn was a method on the server, and every handler holds the server.

That is closed. server/namespaces holds the stores and storeIn is unexported inside it, so no handler can name a namespace — it asks at a door, and each door is a different promise about what stands behind the write.

server/store_writers_test.go reads the package and fails on a function that asks for a store it can write and is not written down with what stands behind it. Five do: storeFor, createAttestation, writeStaandDef, HandleStaand, and systemAttestor.

That last one is the residue, and no boundary removes it. auth.Handler.attest writes into system on the /auth/… routes, which are ANYONE because logging in cannot ask you to be logged in. What bounds it is not admission and not this package: the predicates are a closed vocabulary the node writes about itself, and the actor is the node's own DID.