Status. Grant signing, verification, and revocation work end to end. The scope-native permissions contract (V2,
DataPortabilityPermissions) is the current model; an earlier file-based permissions contract is still deployed on mainnet, and new grants do not use it. See Core contracts for addresses.spotify.listening_history without exposing their messages — and can be time-bounded and revoked. To give an app a computed result instead of the records, see Derivative data.
Grants can also carry economics. Rather than a one-off read, a user can grant a DataDAO / DLP the right to make decisions over their contributed data, and in return receive a VRC-20 token representing proportional rights over the pooled dataset — turning a permission into an ongoing stake in how that data is used and monetized.
How grants work
- A builder requests a set of scopes (e.g.
instagram.profile,spotify.listening_history) - The user reviews the request and approves it in their app
- The user’s wallet signs the grant (EIP-712), binding the grantee, the scopes, and an expiry
- The grant is recorded through the DP RPC and anchored onchain
- On every data request, the Personal Server checks the grant before releasing data
Requesting a grant (the Connect flow)
A builder does not implement grant signing itself. It asks the Vana SDK to create an access request; the user is sent to a Vana approval surface, signs the grant there, and the builder polls for the approved result:- The builder’s backend calls
createAccessRequest, which returns anapprovalUrland arequestId. - The user opens the approval surface (a browser tab), reviews the app, source, and scopes, and approves — signing the grant with their wallet.
- The builder polls
getAccessRequestStatus(requestId)until it resolves toapproved, which returns thegrantId, the user’spersonalServerUrl, and the granted scope. - The builder reads the approved data from the Personal Server, paying the protocol fee from escrow — see Payments & fees.
Grant format (EIP-712)
Grants use EIP-712 typed data so consent is cryptographically verifiable.
A user holds one grant per builder. The grant id is derived from the user and the
granteeId, so approving the same builder again replaces the grant’s scopes and expiry instead of adding a second grant.
The example uses
expiresAt: 0 (never expires) to show the case some integrations want — standing consent for a service the user keeps connected. For most grants, prefer a real expiry: a bounded expiresAt limits how long a single approval stays live, and is the safer default.Grant lifecycle
- Create — the user signs; the grant is recorded and anchored.
- Active — the grantee may read covered scopes until expiry or revocation.
- Revoked — the user can revoke at any time. The revocation is recorded by the DP RPC, and the Personal Server checks grant state there on every request, so the next read fails. Where the Context Gateway serves data, it stops serving immediately.
- Expired — once
expiresAtpasses, the grant stops authorizing reads.
Verification
When a builder makes a data request, the Personal Server verifies the grant before serving data:- Registered — the requester is a registered builder
- Not revoked — the grant has not been revoked
- Not expired —
expiresAtis0or in the future - Scope match — the requested scope and operation are covered by a granted scope entry (see Scope grammar)
- Signer match — the request recovers to the builder that matches the grant’s grantee
- Fee paid — the grant’s fee shows as paid (see Payments & fees)
Computing over a user’s data
A grant authorizes one of two operations per scope entry, encoded by the scope grammar: read (the default) or write (the entry carries awrite: prefix).
To give an app the result of a computation instead of the records, use derivative data: the user’s Personal Server answers a question over their own records, and the app reads only the answer, under a read grant on the answer’s scope. For jobs over data pooled from many users, see Confidential compute.
Onchain record
The permissions contract emits a single event capturing the grant’s substance whenever it is created or re-issued:scopes and expiresAt live in the event itself, anyone can reconstruct who may access what, until when, directly from the chain — without resolving file IDs off-chain.
Every change to a grant’s scopes or expiry re-emits PermissionSet for the same id with a higher grantVersion, so the event with the highest grantVersion is the grant’s current onchain state. To check a single grant, call isActive(bytes32 id) on the permissions contract, or read its record with permissions(bytes32 id).
Revocations are recorded by the DP RPC, which is what Personal Servers check before serving data. They are not currently written to the permissions contract, so isActive() reflects a grant’s registration and expiry, not a revocation made through the DP RPC. To check whether a grant was revoked, query GET /v1/grants/{grantId} on the DP RPC.
The grantor, grantee, scopes and expiry of every grant are public. The data itself is not.
Related
- Personal Servers — where grants are enforced
- Payments & fees — how a grant’s fee is funded and settled
- Scopes & schemas — what scopes are and how they’re defined
- Scope grammar - how a scope entry names the operation (read by default,
write:prefix) - Write API — what a
write:grant lets an app do - Derivative data — a grant on an answer computed from scopes the app never sees
- Protocol — DP RPC — identity and the settlement path
- Build a Vana App — the end-to-end builder integration