1. Introduction
The Data Portability Protocol enables users to:- Collect personal data from various platforms
- Store data under their control
- Grant third-party applications access to specific data scopes
- Revoke access at any time
- Maintain auditable records of all data access
Design principles
| Principle | Description |
|---|---|
| User sovereignty | The user controls their data and who can access it |
| Local-first | Data is stored on the user’s device by default |
| Protocol-native | Grants and data registry entries live onchain for verifiability |
| Encryption by default | Data is encrypted before upload; TLS in transit |
| Extensibility | New data sources and storage backends can be added without protocol changes |
2. Terminology
Protocol entities
| Term | Definition |
|---|---|
| User | A human who owns data and controls access. Identified by wallet address. |
| Personal Server | Protocol-recognized environment that stores user data and responds to access requests. Registered onchain. |
| Builder | Third-party application requesting data access. Registered onchain with public key and app URL. |
| Data Portability RPC (Gateway) | Service providing fast API access with eventual chain consistency. |
| Data Portability Client | Software enabling protocol interaction (e.g. Data Connect desktop app). NOT a protocol participant. |
| Passport | Client-chosen UX layer for wallet authentication. Non-protocol component. |
| Storage Backend | Service storing encrypted data blobs (Vana Storage, Google Drive, Dropbox, IPFS). |
Protocol objects
| Term | Definition |
|---|---|
| Data File | Blob containing user data for a specific scope. Immutable after write. |
| DataRegistry File Record | Onchain registry entry: fileId, URL, schemaId, permissions. |
| Grant | Signed permission allowing a builder to access specific data scopes. |
| Scope | Hierarchical identifier for a data type (e.g. instagram.profile). |
| Data Connector | Module that extracts data from a specific platform. Not part of the protocol. |
| Schema Registry | Onchain registry (DataRefinerRegistry) mapping schemaId to schema definition. |
Cryptographic primitives
| Primitive | Specification |
|---|---|
| Grant signature | EIP-712 typed data signature proving user consent |
| Master key material | Raw signature bytes from EIP-191 personal_sign over "vana-master-key-v1" |
| Scope key | HKDF-SHA256(master_key_material, "vana", "scope:{scope}") — 32 bytes |
| Data encryption | OpenPGP password-based encryption; password = hex(scope_key) (64-char hex string) |
| Request authorization | EIP-191 signature over canonicalized JSON payload (Web3Signed scheme) |
3. Protocol model
Architecture layers
Key distinction
- Desktop App: NOT a protocol participant. Does NOT register onchain. Controls the Personal Server.
- Personal Server: IS a protocol participant. MUST register onchain. Can act unattended.
4. Protocol components
4.1 Personal server
Purpose: Stores user data in plaintext, responds to authorized data requests, maintains access logs, operates unattended. Registration: MUST be registered onchain viaDataPortabilityServers. Uses EIP-712 signature-based operations.
Hosting options
| Option | URL format | Operator | Data visibility |
|---|---|---|---|
| ODL Cloud | https://server.vana.com/u/{userId} | Vana | Encrypted at rest |
| Self-hosted | https://server.alice.com | User | User’s choice |
| Desktop-bundled | Local (tunneled via {userId}.server.vana.org) | User’s device | May store unencrypted |
Implementation targets
| Target | Runtime | Activation | Availability |
|---|---|---|---|
| Desktop-bundled | Embedded in Tauri app | User opens app | While app running |
| ODL Cloud | Firecracker MicroVM (Sprites.dev) | HTTP request auto-activates | Always (~1-2s cold start) |
| Self-hosted | Docker container | Always running | User manages |
API
Data endpoints:| Method | Path | Description |
|---|---|---|
POST | /v1/data/{scope} | Create data document |
GET | /v1/data | List scopes and metadata. Query: ?scopePrefix, ?limit, ?offset |
GET | /v1/data/{scope} | Read data. Query: ?fileId, ?at (ISO 8601) |
GET | /v1/data/{scope}/versions | List versions (metadata only) |
DELETE | /v1/data/{scope} | Delete data (user-only) |
| Method | Path | Description |
|---|---|---|
GET | /v1/grants | List grants |
POST | /v1/grants | Create grant. Body: { granteeAddress, scopes, expiresAt?, nonce? } |
POST | /v1/grants/verify | Verify grant signature |
| Method | Path | Description |
|---|---|---|
GET | /v1/access-logs | Access log history |
GET | /health | Health check |
| Method | Path | Description |
|---|---|---|
POST | /v1/sync/trigger | Force sync |
GET | /v1/sync/status | Sync status |
POST | /v1/sync/file/{fileId} | Sync specific file |
Write flow (POST /v1/data/{scope})
- Look up
schemaIdfor scope via Gateway - Validate request body against schema
- Generate
collectedAttimestamp (UTC) - Construct data file envelope
- Store locally:
~/.vana/data/{scope}/{collectedAt}.json - Return
201 Created - Async: encrypt → upload to storage backend → register in DataRegistry
Authentication (builder requests)
All builder-initiated requests MUST include:| Field | Type | Required | Description |
|---|---|---|---|
aud | string | Yes | Personal Server origin |
bodyHash | string | Yes | Hash of request body (empty for GET) |
exp | number | Yes | Expiration (Unix seconds) |
grantId | string | For data reads | Onchain permissionId |
iat | number | Yes | Issued-at (Unix seconds) |
method | string | Yes | HTTP method |
uri | string | Yes | Request path and query |
aud/method/uri/timestamps, checks grant validity for data reads.
Access control
| Endpoint | Access |
|---|---|
POST /v1/data/{scope} | User only |
GET /v1/data, GET /v1/data/{scope}, GET /v1/data/{scope}/versions | User or builder with grant |
DELETE /v1/data/{scope} | User only |
POST /v1/grants, GET /v1/grants | User only |
GET /v1/access-logs | User only |
MCP server
Personal Server includes an MCP server for AI assistant integration. Resources:vana://files, vana://file/{scope}, vana://file/{scope}/metadata, vana://grants, vana://schemas, vana://schema/{schemaId}
Tools: list_files, get_file, search_files — all require EIP-191 signature verification.
Tunneling
Desktop-bundled servers use Vana-managed FRP (Fast Reverse Proxy) for internet accessibility:- FRP server:
proxy.server.vana.org - User URL:
https://{userId}.server.vana.org - Wildcard DNS with Let’s Encrypt TLS
- Tunnel starts on app open, terminates on close
Local data hierarchy
4.2 Data portability RPC (Gateway)
Purpose: Provides fast API access to protocol operations with eventual chain consistency. Trust model:| Mode | Latency | Trust |
|---|---|---|
| Gateway only | ~50ms | Trust Vana |
| Gateway + signature verification | ~50ms | Trust user signed |
| Gateway + spot-check chain | ~50ms + async | Trust but verify |
| Chain only | ~5-10s | Trustless |
Gateway API
Server operations:| Method | Path | Description |
|---|---|---|
POST | /v1/servers | Register/update Personal Server |
GET | /v1/servers/{address} | Get server info |
GET | /v1/servers/{address}/status | Confirmation status |
| Method | Path | Description |
|---|---|---|
POST | /v1/grants | Create grant |
DELETE | /v1/grants/{grantId} | Revoke grant |
GET | /v1/grants/{grantId} | Get grant details |
GET | /v1/grants?user={address} | List user grants |
GET | /v1/grants?builder={address} | List builder grants |
GET | /v1/grants/{grantId}/status | Confirmation status |
| Method | Path | Description |
|---|---|---|
POST | /v1/files | Register file record (schemaId required) |
GET | /v1/files/{fileId} | Get file record |
GET | /v1/files?user={address} | List user files |
GET | /v1/files?user={address}&since={ISO8601} | Files since timestamp |
GET | /v1/files/{fileId}/status | Confirmation status |
| Method | Path | Description |
|---|---|---|
GET | /v1/schemas/{schemaId} | Get schema metadata |
GET | /v1/schemas?scope={scope} | Look up schemaId by scope |
| Method | Path | Description |
|---|---|---|
POST | /v1/builders | Register builder |
GET | /v1/builders/{address} | Get builder info |
GET | /v1/builders/{address}/status | Confirmation status |
| Method | Path | Description |
|---|---|---|
GET | /v1/sync/status | Chain sync status |
GET | /v1/nonces?user={address}&operation={op} | Current and next nonce |
Request format
Response format
ID computation
The Gateway assigns deterministically computedbytes32 IDs:
4.3 Vana L1 (onchain contracts)
DataPortabilityServers
Address:0x1483B1F634DBA75AeaE60da7f01A679aabd5ee2c (Moksha Testnet)
Manages Personal Server registration and trust relationships.
DataPortabilityGrantees
Address:0x8325C0A0948483EdA023A1A2Fd895e62C5131234 (Moksha Testnet)
Manages builder registration.
DataPortabilityPermissions
Address:0xD54523048AdD05b4d734aFaE7C68324Ebb7373eF (Moksha Testnet)
Manages grant creation and revocation.
DataRegistry
Address:0x8C8788f98385F6ba1adD4234e551ABba0f82Cb7C (Moksha Testnet)
Stores immutable file records.
schemaId. Omitting schemaId is invalid for protocol compliance.
4.4 Session relay
Standalone service coordinating the “Connect Data” flow between builder web popup and Desktop App.Session states
API
| Method | Path | Auth | Description |
|---|---|---|---|
POST | /v1/session/init | Web3Signed | Create session |
GET | /v1/session/{sessionId}/poll | None | Poll for completion |
POST | /v1/session/claim | Secret | Claim session (Desktop App) |
POST | /v1/session/{sessionId}/approve | Secret | Approve with grant |
POST | /v1/session/{sessionId}/deny | Secret | Deny request |
5. Data formats
5.1 Scope taxonomy
5.2 Data file format
JSON envelope (v1):5.3 Grant format (EIP-712)
5.4 Access log format
5.5 App manifest
W3C Web App Manifest with customvana block. Discoverable via <link rel="manifest"> at the builder’s appUrl.
Required vana fields: appUrl, privacyPolicyUrl, termsUrl, supportUrl, webhookUrl, signature.
Signature: EIP-191 by builder address over canonical JSON of vana block (keys sorted, signature excluded).
Verification: Desktop App MUST verify manifest origin, appUrl match, and signature recovery before rendering consent.
5.6 Data connector metadata
6. Protocol operations
6.1 User registration
- User opens Desktop App
- Redirect to Passport (identity provider)
- Authenticate (social/email)
- Wallet created or retrieved
- Setup complete
6.2 Data collection
- User clicks “Connect ” in Desktop App
- Embedded browser opens; user logs in
- Data Connector scrapes data (user’s IP, user’s browser session)
- Raw data sent to Personal Server via
POST /v1/data/{scope} - Personal Server stores locally, then async: encrypt → upload → register
6.3 Connect data flow
- User clicks “Connect data” on builder’s web app
- Builder backend creates session via Session Relay (signed)
- Frontend displays deep link popup
- User opens Desktop App via
vana://connect?sessionId=...&secret=... - Desktop App claims session, fetches builder metadata, verifies manifest
- User reviews and approves grant
- Personal Server signs EIP-712 grant, submits to Gateway
- Desktop App approves session with
{ grantId, userAddress, scopes } - Builder receives grant via poll or webhook
6.4 Grant revocation
- User clicks “Revoke” in Desktop App
- Signs revocation
- Submit
DELETE /v1/grants/{grantId}to Gateway - Gateway marks revoked immediately; async chain sync
- Personal Server blocks future requests
- Builder receives
410 Grant revokedon next request
6.5 Data deletion
DataRegistry entries are immutable. Deletion is implemented as:- User requests deletion in Desktop App
- Personal Server deletes encrypted blob from storage backend
- Local decrypted copy removed
- Tombstone written via Gateway
- Other Personal Servers treat tombstoned records as non-existent (
410or404)
7. Security
7.1 Encryption
- All user data MUST be encrypted with OpenPGP password-based encryption before writing to storage backends
- Password is
hex(scope_key)where scope key is derived per section 2 - Personal Servers serve decrypted data to authorized builders over TLS
- Vana MUST NOT have access to plaintext data
7.2 Authentication
- Onchain operations MUST be signed using EIP-712 typed data
- Builder requests to Personal Servers MUST include
Authorization: Web3Signed(EIP-191) - Nonces MUST prevent replay attacks for onchain operations
7.3 Authorization
Personal Server MUST verify before serving data:- Signature valid
- Grant not revoked
- Grant not expired
- Requested scope within granted scopes
- Authorization signer matches onchain grantee
7.4 Transport
- All HTTP endpoints MUST use TLS 1.3
- Personal Servers SHOULD implement certificate pinning
- Gateway SHOULD implement rate limiting
7.5 Threat model
| Threat | Mitigation |
|---|---|
| Vana sees user data | Data encrypted before upload; Vana has no key |
| Builder exceeds granted scope | Personal Server validates scope on each request |
| Grant replay | Nonces and timestamps in grant signature |
| Malicious builder | User must explicitly approve; can revoke anytime |
| Gateway lies about grants | Grants include user signature; verifiable onchain |
| Personal Server compromised | Storage backend data remains encrypted |
8. Error handling
Error code structure
Following SMTP convention, the protocol uses a 3-digit error code system:| First digit | Meaning |
|---|---|
2xx | Success |
3xx | Intermediate (more input needed) |
4xx | Temporary failure (retry may succeed) |
5xx | Permanent failure (do not retry) |
| Second digit | Category |
|---|---|
x0x | Syntax/format |
x1x | Authentication/authorization |
x2x | Data/storage |
x3x | Grant/permission |
x4x | Protocol/network |
x5x | Rate limiting |
Error codes
| Code | Description |
|---|---|
200 | Success |
201 | Created |
301 | Redirect to Personal Server |
400 | Bad request (syntax error) |
401 | Unauthorized (invalid signature) |
403 | Forbidden (valid auth but not permitted) |
404 | Not found |
410 | Grant revoked |
411 | Grant expired |
412 | Scope not granted |
420 | Data not found at registry entry |
421 | Storage backend unavailable |
429 | Rate limited |
440 | Chain sync pending |
500 | Internal server error |
503 | Service unavailable |
Error response format
9. Extensibility
Adding data sources
- Define scope taxonomy (
{source}.{category}) - Create JSON Schema for data structure
- Register schema in
DataRefinerRegistry - Build a Data Connector (optional — not protocol)
Adding storage backends
Implement theStorageBackend interface:
Adding capabilities
Personal Servers declare capabilities in registration:10. Builder SDK
Published as@opendatalabs/connect on NPM with three entrypoints:
| Entrypoint | Purpose |
|---|---|
@opendatalabs/connect/server | Session Relay client, request signer, data client |
@opendatalabs/connect/react | Polling hook, Connect button component |
@opendatalabs/connect/core | Shared types and errors |
Server-side usage
React client usage
Appendix A: SMTP analogy
| SMTP concept | Data Portability equivalent |
|---|---|
| RFC 5321 | This specification |
| Mail User Agent (MUA) | Data Portability Client |
| Mail Transfer Agent (MTA) | Personal Server |
| Mail Delivery Agent (MDA) | Storage Backend |
| SMTP Server | Data Portability RPC (Gateway) |
| Email address | Wallet address + Personal Server URL |
| Email message | Data File |
| Mailbox | Scope |
| SMTP EHLO | Server registration |
| SMTP MAIL FROM | Grant creation |
| SMTP RCPT TO | Builder address |
| SMTP DATA | Data file upload |
| SMTP QUIT | Grant revocation |
| Spam filter | Grant approval |
| Bounce message | Error response |
Appendix B: ODL Cloud reference architecture
Sprites.dev integration
ODL Cloud uses Sprites.dev for per-user stateful MicroVMs (Firecracker):- Hardware-level isolation per user
- Stateful storage persists between activations
- HTTP auto-activation wakes sleeping VMs
- Pay-per-use billing; scales to zero when idle
- Up to 8 CPU, 16 GB RAM per Sprite
Cold start flow
- Builder calls
GET https://user-abc.server.vana.com/data - Sprites edge detects inactive Sprite → assigns compute (~300ms)
- Sprite boots with persisted filesystem
- Personal Server starts, decrypts data using delegated signature
- Request proxied to port 8080, response returned
- After idle timeout → Sprite sleeps (billing stops, data persists)
Delegated signature lifecycle
| Event | Action |
|---|---|
| User enables ODL Cloud | Sprite provisioned; user signs "vana-master-key-v1", signature encrypted and stored |
| Sprite activates | Signature decrypted in-memory; master key derived; data decrypted |
| User disables ODL Cloud | Sprite deleted; data remains in storage backend |
Cost estimates
| Usage pattern | Monthly cost per user |
|---|---|
| Light (few requests/month) | ~$0.10-0.50 |
| Medium (daily builder access) | ~$0.50-2.00 |
| Heavy (continuous access) | ~$2.00-10.00 |