Access Control
What the chain records about a grant today, the checks a server must pass before releasing a key, and why none of it is serving reads yet.
What this layer does. It decides whether a given consumer may read a given dataset version, and if so releases the key for it. Read this page as a design with one part live: the grant's on-chain record works, and nothing yet serves an authorised read.
What is running, and what is not
| Piece | State |
|---|---|
GrantAnchorRegistry — the on-chain grant record | Live on Base Sepolia. Not deployed on Base mainnet |
| Anchor writes, owner-called | Live on Base Sepolia, reachable through a legacy Python SDK |
| Anchor writes, delegated by signature | In the source only, deployed to no chain — and no client can produce the signature yet. Both the relaying endpoint and the owner-side signing helper are outstanding |
access-control-service — the server that would check a request and release a key | Code, deployed nowhere. It appears in no cluster manifest |
| An authorised read, end to end | Not available. No deployed service releases decryption material |
So a grant can be recorded and revoked on Base Sepolia now, by its owner. What cannot happen yet is presenting one and receiving a key. The registry's Sepolia address, and its absence on mainnet, are on Contract Addresses.
What the chain contributes
The GrantAnchorRegistry holds one anchor per grant id, and it holds deliberately little: the dataset version, a hash of the subject DID, when it was issued, an expiry — zero meaning perpetual — and a status of ACTIVE or REVOKED. No policy, no credential, no key — and no digest of the grant document and no list of permitted actions.
That omission decides the shape of everything else on this page. The grant itself is an off-chain verifiable credential, and because the anchor commits to neither its contents nor what it permits, a valid anchor is a necessary condition and never a sufficient one. A server that released a key on the strength of the anchor alone would be honouring a document the chain never saw.
Two properties the anchor does give:
- A fail-closed condition. A grant that is missing,
REVOKED, or past its expiry can be refused without consulting anything off-chain. Note that a perpetual anchor carries no expiry to fail on, so on chain revocation is its only withdrawal — the credential presented against it may still carry an expiry of its own, which is checked separately below. - Revocation at the source.
ACTIVE → REVOKEDis the only transition the registry admits, and a revoked anchor never returns to active. Withdrawal does not depend on chasing credentials already issued.
Who may write an anchor
Every direct write is gated on the caller owning the dataset version, and those are the only writes that execute on any chain today. That rules out the obvious arrangement — a service holding its own key and writing anchors on users' behalf — because no service key is the owner, and giving one that authority would mean giving it the owner's key.
The design answer is to put the authority in the signature rather than the sender: the owner would sign an EIP-712 GrantAuthorization (or GrantRevocation) offline, and any address could submit it, gaining no rights and paying only gas — an EOA or an ERC-1271 contract can sign, so a multisig-held DID works. That code is in the registry's source and on no deployed proxy, and nothing yet produces the signature, so today the owner-called methods are the only writes that execute anywhere. They keep working when the signed path arrives.
What a server must check before releasing a key
Five checks, and the anchor is only one of them. All must pass.
Three points are worth naming, because they are what the anchor's minimalism forces:
- The issuer must be the dataset version's owner on chain, not merely a valid signer. Without that tie, a subject could sign their own credential and grant themselves whatever it says.
- The credential's own expiry is checked, and the anchor's is not a substitute for it. Both exist and they are different clocks: the anchor commits to no credential, so nothing on chain makes them agree. The approval route does pin them equal when it issues a grant, but an owner can write an anchor directly — that is the only write deployed today — with any expiry it likes, including zero for perpetual, against a credential that says otherwise.
- The credential must match a digest retained when the owner approved it. Since the anchor stores no digest, the comparison target lives server-side, with the party that approved the grant.
All three are conditions the service must satisfy before it is deployed anywhere. It runs in no environment today, so the checks above describe what a release has to demonstrate, not a gate standing in front of live data.
How the key reaches the consumer
One data encryption key per dataset version, wrapped per grant. The key is held custodially on the platform side — that is the decided architecture, not a temporary stage.
This is worth stating plainly because the earlier design was the opposite, and it was rejected on 2026-09-04 rather than deferred. That design used proxy re-encryption: the owner would produce re-encryption material for one consumer and the service would transform the wrapped key without being able to open it. The engine for it is complete and the full round trip runs, so this is not an unfinished feature. It was dropped because the approval path fixed the scheme at a threshold of one — a single service instance would hold every key fragment — so it paid the whole cost of key distribution, including consumer-side key custody and a Python SDK every buyer would have to run, without buying a materially stronger trust model than custody. Its proxy-re-encryption library has also not shipped a release since 2021, and its own dependency pin holds the rest of the Python workspace back.
The consequence for a reader is direct: confidentiality here rests on custody of the key, not on the platform being unable to read it. A threshold scheme is the thing that would change that, and it is a later decision; the rejected code stays in place against it.
What is recorded
A resolved request is designed to emit an audit event naming the grant, the dataset, the subject and how the key was delivered. That is an access log: it records that a key was handed over, not what was subsequently read, how much, or for how long.
Not in this layer
No usage measurement exists anywhere in the protocol — no usage events, no pricing configuration, no consumer-facing quotas, no policy language, and no licence or payment mode that meters consumption. Metered usage is a v3 concern, under a receipt type that does not exist yet.
The Royalty Engine is paid by whoever pays it. Nothing observes a read and turns it into a charge.
Revocation's reach is limited in one way worth stating: it stops new access. Data a consumer has already decrypted cannot be recalled.
Interfaces
- In: a DID session, a grant credential issued by the dataset version's owner, and the grant's on-chain anchor.
- Out (designed): the dataset version's key for the requesting consumer, plus an audit event.
- Cross-links: datasets come from Data Assembly; the bytes themselves live in Storage & Serving; the identities on both sides are Identity.
Invariants
- An anchor authorises nothing on its own. It commits to no document and no actions, so every release also rests on off-chain checks.
- Revocation does not depend on the credential, and does not reach what is already decrypted.
- Only one status transition exists.
ACTIVE → REVOKED, never back. - No service key can write an anchor in its own name. The gate is dataset ownership, and the designed way around it is to move authority into the owner's signature so a submitter is only a relay — designed, and on no deployed proxy.
- A visible reference is not authorisation, and today it may still be enough to read. A hash or manifest URI does not grant you anything. But nothing in the storage layer encrypts and its reads are unauthenticated, so whether the bytes behind a URI are intelligible depends on how their producer uploaded them — see Storage & Serving.
- Key custody is the trust boundary, and it sits with the platform.
Last updated