Skip to main content
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. Granularity is the point. A grant is scoped to specific data types, not a whole account — a user can share spotify.listening_history without exposing their messages — and is time-bounded and revocable, so consent stays narrow and reversible instead of an all-or-nothing login. The same primitive is designed to extend beyond “read this scope” to “run this specific operation over this subset of data” — authorizing a computation rather than raw access (see Operation-scoped grants below). 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.
const grantTypedData = {
  domain: {
    name: "Vana Data Portability",
    version: "2",
    chainId: 14800,
    verifyingContract: "0x...", // DataPortabilityPermissions (V2)
  },
  types: {
    Grant: [
      { name: "grantor", type: "address" },
      { name: "granteeId", type: "bytes32" },
      { name: "scopes", type: "string[]" },
      { name: "expiresAt", type: "uint256" },
      { name: "nonce", type: "uint256" },
    ],
  },
  primaryType: "Grant",
  message: {
    grantor: "0x...",                 // User's wallet address
    granteeId: "0x...",               // Builder's grantee id
    scopes: ["instagram.profile"],    // The unit of access
    expiresAt: 0,                     // 0 = no expiration
    nonce: 1,                         // Replay protection
  },
};
FieldDescription
grantorThe user granting access
granteeIdThe builder receiving access (registered in DataPortabilityGrantees)
scopesScope identifiers the grant covers — the unit of access
expiresAtUnix timestamp for expiration; 0 means it never expires
nonceMonotonically increasing value per user, prevents replay
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 revokes at any time. Enforcement has two layers: where the Context Gateway serves data, it stops serving immediately, even before onchain confirmation, while the revocation is anchored on-chain as the durable record. Integrations that bypass the Context Gateway enforce against that onchain state.
  • 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 expiredexpiresAt is 0 or in the future
  4. Scope match — the requested scope is within the granted scopes
  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:
CodeMeaning
401Invalid signature or unauthorized
403Valid auth but not permitted
410Grant revoked
411Grant expired
412Scope not granted

Operation-scoped grants

Every grant today authorizes one implicit operation: return the scope — the grantee reads the covered data in plaintext. The designed extension is a grant that instead carries an operations pipeline: the user authorizes a specific computation over their data, the Personal Server decrypts the data and executes that pipeline locally, and the grantee receives only the result — never the raw data. The consent moment changes accordingly. Instead of
“App X requests access to your Instagram posts”
the user approves
“App X requests access to your anonymized Instagram posts”
— or a summary, an aggregate, a redaction: whatever the pipeline the grant names actually produces. This is what an application should reach for when it wants a privacy-preserving operation over a user’s data — an LLM summary of a user’s chat history, say — where the application only needs the output, not the records themselves. Everything else about the grant stays as described above: the same EIP-712 format (plus the operations field), the same lifecycle, the same verification on the Personal Server, the same fee gating. The one piece expected to evolve with it is pricing — fees for an operation-scoped grant may need to be dynamic, reflecting the compute the pipeline requires, rather than a flat per-access fee.
Status. Operation-scoped grants are a design direction, not implemented — today every grant returns the scope’s data directly. Note the distinction from Confidential compute: operation-scoped grants run a computation on one user’s data, on that user’s own Personal Server; confidential compute runs jobs over pooled data from many users in a TEE network.

Onchain record

The permissions contract emits a single event capturing the grant’s substance whenever it is created or re-issued:
event PermissionSet(
    bytes32  indexed id,
    address  indexed grantorAddress,
    bytes32  indexed granteeId,
    string[] scopes,
    uint256  grantVersion,
    uint256  expiresAt
);
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. This is a deliberate choice, modelled on how public blockchains already work: just as a token transfer publicly shows this address sent this asset to that address, a grant publicly shows this address shared this kind of data with this grantee, until when — the transparency model the industry has broadly adopted. For deployments that prefer not to expose scope details on-chain, commitments or hashed scopes are available as an option.
Status. Grant signing, verification, and revocation work end to end. The scope-native permissions contract (V2, DataPortabilityPermissions) is the current model; a previous file-based permission contract remains deployed on mainnet during migration. See Core contracts for addresses.