Skip to main content
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.
A grant is a signed permission that lets a builder access specific scopes of a user’s data. Grants are the access-control primitive of the protocol — no builder can read data without one, and every grant can be revoked by the user at any time. The grant is scope-native onchain: the onchain record carries the scopes and an expiry directly, so the chain expresses exactly what the Context Gateway and the Personal Server enforce — without resolving file IDs off-chain. A grant is scoped to specific data types, not a whole account — a user can share 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

  1. A builder requests a set of scopes (e.g. instagram.profile, spotify.listening_history)
  2. The user reviews the request and approves it in their app
  3. The user’s wallet signs the grant (EIP-712), binding the grantee, the scopes, and an expiry
  4. The grant is recorded through the DP RPC and anchored onchain
  5. 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:
  1. The builder’s backend calls createAccessRequest, which returns an approvalUrl and a requestId.
  2. The user opens the approval surface (a browser tab), reviews the app, source, and scopes, and approves — signing the grant with their wallet.
  3. The builder polls getAccessRequestStatus(requestId) until it resolves to approved, which returns the grantId, the user’s personalServerUrl, and the granted scope.
  4. The builder reads the approved data from the Personal Server, paying the protocol fee from escrow — see Payments & fees.
For the end-to-end integration — SDK setup, backend routes, and a React button — follow the Build a Vana App guide.

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 expiresAt passes, the grant stops authorizing reads.

Verification

When a builder makes a data request, the Personal Server verifies the grant before serving data:
  1. Registered — the requester is a registered builder
  2. Not revoked — the grant has not been revoked
  3. Not expired — expiresAt is 0 or in the future
  4. Scope match — the requested scope and operation are covered by a granted scope entry (see Scope grammar)
  5. Signer match — the request recovers to the builder that matches the grant’s grantee
  6. Fee paid — the grant’s fee shows as paid (see Payments & fees)
If any check fails, the Personal Server returns an error:

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 a write: 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:
Because 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.