Identity in the Data Portability Protocol is wallet-based. Users and builders are identified by Ethereum wallet addresses. There are no usernames, emails, or centralized accounts at the protocol level — just cryptographic keys that sign grants, authorize requests, and prove ownership.
Users
A user is a person who owns data and controls access to it. Their identity is their wallet address.
Users don’t interact with the wallet directly in most cases. The Desktop App handles wallet creation and signing through a client-side abstraction called Passport (see below). From the protocol’s perspective, the user is just an address that can sign EIP-712 grants and EIP-191 messages.
What’s tied to a user’s address
| Record | Contract | What it stores |
|---|
| Trusted servers | DataPortabilityServers | Which Personal Servers the user trusts |
| Grants | DataPortabilityPermissions | Which builders have access to which scopes |
| File records | DataRegistry | Metadata for encrypted data files |
Builders
A builder is a third-party application that requests access to user data. Builders register onchain with:
- Address — The builder’s wallet address (used for signing requests)
- Public key — For cryptographic operations
- App URL — The canonical origin of the builder’s web application
Builder registration
Builders register through the Desktop App or directly via the Gateway. The registration is recorded in the DataPortabilityGrantees contract.
Steps:
- Open the Desktop App and go to Builder Registration
- Enter the
appUrl (the canonical origin of your app, e.g. https://flipboard.com)
- The app generates (or imports) a builder wallet and keypair
- The registration is submitted to the Gateway, which syncs it to the chain
- You receive: builder address, public key, and private key
Store the builder private key securely. You’ll need it to sign Session Relay requests and data access requests. Set it as VANA_APP_PRIVATE_KEY in your server environment.
DataPortabilityGrantees contract
Address: 0x8325C0A0948483EdA023A1A2Fd895e62C5131234 (Moksha Testnet)
struct GranteeInfo {
address owner;
address granteeAddress;
string publicKey;
string appUrl;
uint256[] permissionIds;
}
// Register a builder
function registerGrantee(
address owner,
address granteeAddress,
string memory publicKey,
string memory appUrl
) external returns (uint256);
// Look up by ID
function grantees(uint256 granteeId)
external view returns (GranteeInfo memory);
// Look up by address
function granteeByAddress(address granteeAddress)
external view returns (GranteeInfo memory);
// Resolve address to ID
function granteeAddressToId(address granteeAddress)
external view returns (uint256);
Gateway builder API
| Method | Path | Description |
|---|
POST | /v1/builders | Register a builder |
GET | /v1/builders/{address} | Get builder info |
GET | /v1/builders/{address}/status | Get onchain confirmation status |
App manifests
Builders must publish an app manifest at their appUrl so that the Desktop App can display human-readable consent information (name, icon, privacy policy) when a user is asked to approve a grant.
Discovery
- The Desktop App fetches
https://{appUrl}
- It resolves the manifest URL from the
<link rel="manifest" href="..."> tag in the HTML
- The manifest URL must be same-origin with
appUrl
The manifest follows the W3C Web App Manifest standard with a custom vana block:
{
"name": "Flipboard",
"short_name": "Flipboard",
"start_url": "https://flipboard.com/",
"scope": "https://flipboard.com/",
"icons": [
{
"src": "/icons/512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"vana": {
"appUrl": "https://flipboard.com",
"privacyPolicyUrl": "https://flipboard.com/privacy",
"termsUrl": "https://flipboard.com/terms",
"supportUrl": "https://flipboard.com/support",
"webhookUrl": "https://api.flipboard.com/vana/webhook",
"signature": "0x..."
}
}
Required vana fields
| Field | Description |
|---|
appUrl | Canonical app origin — must match the appUrl registered onchain |
privacyPolicyUrl | Link to the builder’s privacy policy |
termsUrl | Link to terms of service |
supportUrl | Link to support/contact page |
webhookUrl | URL for receiving grant notifications |
signature | EIP-191 signature over the vana block (see below) |
Manifest signature
The signature field is an EIP-191 signature by the builder’s registered address over the canonical JSON of the vana block. This proves the manifest was published by the registered builder, not a third party.
Canonicalization rules:
- Sort keys alphabetically at all levels
- Exclude the
signature field itself from the signed payload
Verification (performed by Desktop App):
- Fetch
https://{appUrl} and resolve the manifest URL
- Verify
vana.appUrl matches the onchain appUrl
- Recompute the canonical JSON of the
vana block (excluding signature)
- Recover the signer from
vana.signature and verify it matches the builder’s registered address
- Verify
webhookUrl matches vana.webhookUrl
If manifest discovery or signature verification fails, the Desktop App must not render the consent screen and must fail the session flow.
Personal server registration
Personal Servers are registered onchain in the DataPortabilityServers contract. Users then trust a server to act on their behalf.
DataPortabilityServers contract
Address: 0x1483B1F634DBA75AeaE60da7f01A679aabd5ee2c (Moksha Testnet)
struct ServerInfo {
uint256 id;
address owner;
address serverAddress;
string publicKey;
string url;
}
struct TrustedServerInfo {
uint256 id;
address owner;
address serverAddress;
string publicKey;
string url;
uint256 startBlock;
uint256 endBlock;
}
// Register and trust in one step (signature-based)
function addAndTrustServerWithSignature(
AddServerWithSignatureInput input,
bytes signature
) external;
// Register a server (signature-based)
function addServerWithSignature(
AddServerWithSignatureInput input,
bytes signature
) external;
// Trust an existing server
function trustServer(uint256 serverId) external;
function trustServerWithSignature(
TrustServerInput input,
bytes signature
) external;
// Untrust a server
function untrustServer(uint256 serverId) external;
function untrustServerWithSignature(
UntrustServerInput input,
bytes signature
) external;
// Update server URL
function updateServer(uint256 serverId, string memory url) external;
// Look up server
function servers(uint256 serverId)
external view returns (ServerInfo memory);
function serverByAddress(address serverAddress)
external view returns (ServerInfo memory);
// Look up user's trusted servers
function userServerValues(address userAddress)
external view returns (TrustedServerInfo[] memory);
Gateway server API
| Method | Path | Description |
|---|
POST | /v1/servers | Register or update a Personal Server |
GET | /v1/servers/{address} | Get server info |
GET | /v1/servers/{address}/status | Get onchain confirmation status |
All write operations use EIP-712 signature-based calls so the Desktop App can register servers without the user sending chain transactions directly.
Passport
Passport is a client-side, non-protocol component that handles wallet creation and authentication in the Desktop App. It is not part of the protocol specification — any client that can produce wallet signatures is compatible.
What Passport provides
- Wallet creation without seed phrase exposure
- Social login (Google, Apple, email)
- Wallet recovery via social/email
- Existing wallet import for advanced users
Reference implementation
The reference implementation uses Privy for embedded wallet management.
Authentication flow
- User clicks “Sign In” in the Desktop App
- Redirect to Passport (Privy)
- User authenticates via social login or email
- Passport creates or retrieves the user’s wallet
- Returns a JWT and wallet address to the Desktop App
From this point, the Desktop App can sign protocol messages (grants, server registration, master key derivation) on behalf of the user.
Passport is intentionally separated from the protocol. Alternative clients can use any wallet management approach — MetaMask, hardware wallets, or direct key management — as long as they can produce EIP-191 and EIP-712 signatures.