Personal Data Vault (v1)
Status: Draft v1 — 2026-05-19
Extension URI: https://projectmaya.deoscomputing.io/data/vault/v1
Maintainer: DEOS Computing (did:web:deos.computing)
Depends on: did:deos v1, UCAN tokens v1, Receipt format v1
Companions: Receipt-Rooted Reputation white paper, Personhood Assertion
1. Motivation
Maya stores everything on the user's device today: receipts, drafts, classifier state, calendar context, financial snapshots. That's the right starting point for trust — the agent runs where the user can see it — but it does not scale. The agent eventually needs to read a user's calendar, vendor history, health profile, and tax filings, most of which is too sensitive to keep as plaintext on a Mac and too large to keep as a single encrypted blob.
The manifesto's L6 layer is personal data sovereignty. The candidate primitives — Solid, Lit Protocol, Iroh, self-hosted Nextcloud, encrypted blobs over IPFS — each have their own community and overlap on three points:
- The user owns an encrypted personal data store (a vault).
- Reads are gated by capability tokens delegated by the user, not ACLs on the vault operator.
- Some reads are plaintext (relying party needs the value); some are proof-only (relying party needs a derived fact, e.g. "balance ≥ X").
This is not a new vault implementation. It defines:
- How a
did:deosadvertises a vault. - How vault reads are authorized via UCAN capabilities.
- How vault reads produce substrate-witnessed access receipts.
- The wire shape for selective-disclosure proofs over vault contents.
- The composition rules so L0–L5 can use vault data coherently.
Conforming vaults — Solid pod, Lit-encrypted blob store, in-process userspace KV, anything — implement the access surface defined here.
2. Vault advertisement
A DID document MAY include a vault field listing one or more vault endpoints. Each endpoint declares its kind, root URL, encryption scheme, and the schema namespaces it serves.
{
"@context": [
"https://www.w3.org/ns/did/v1",
"https://projectmaya.deoscomputing.io/data/vault/v1"
],
"id": "did:deos:alice",
"verificationMethod": [/* ... */],
"vault": [
{
"id": "did:deos:alice#vault-primary",
"kind": "solid",
"endpoint": "https://alice.solidcommunity.net/",
"encryption": "x25519-aead/v1",
"namespaces": ["calendar", "comms", "finance", "health"]
},
{
"id": "did:deos:alice#vault-archive",
"kind": "ipfs-encrypted",
"endpoint": "ipfs://bafy.../",
"encryption": "x25519-aead/v1",
"namespaces": ["archive"]
}
]
}
2.1 kind
Free-form vault-implementation identifier. SHOULD be one of:
solid— W3C Solid podlit— Lit Protocol encrypted storeiroh— Iroh documentnextcloud— Nextcloud Files APIs3-encrypted— S3-compatible with operator-held envelope keyipfs-encrypted— content-addressed encrypted blobslocal— userspace KV on the same machine as the agent (deos-runtime default)
Custom kinds MUST namespace under their operator's URI prefix.
2.2 endpoint
URL the agent dereferences to access the vault. Scheme is kind-specific. Vault operators MUST support either HTTPS or a libp2p-style content-addressed reference; in-memory local MAY use a process-local scheme.
2.3 encryption
The on-disk encryption scheme. v1 defines one:
x25519-aead/v1— vault contents are encrypted with per-path symmetric keys; the user holds an X25519 keypair and per-path symmetric keys are sealed to that keypair via X25519+HKDF→ChaCha20-Poly1305. The DID controller MUST be able to derive per-path keys from a single root seed.
Custom schemes are permitted but MUST publish their key-derivation rules so verifiers can independently audit access proofs.
2.4 namespaces
Logical prefixes the vault serves. These are part of the path grammar in §3.2; not all kinds need every namespace.
3. Read capability
Vault reads are authorized by UCAN tokens whose att[] contains a with of the form vault:<namespace>/<path> (or with wildcard suffix).
3.1 Capability shape
{
"with": "vault:calendar/2026-05-*",
"can": "read/v1",
"max_reads": 100,
"expires_at": 1719792000
}
with: vault path or path prefix. Wildcard*allowed only as a trailing segment terminator (calendar/2026-05-*matches all entries beginningcalendar/2026-05-;calendar/*matches everything under calendar; barevault:*is allowed but SHOULD only be granted in dev / for self-delegations).can:read/v1(plaintext) orprove/v1(proof-only, see §5).max_reads: optional cap on number of plaintext reads under this capability. Vault MUST enforce.- Standard UCAN fields (
exp,nbf,nnc,prf,s) apply.
3.2 Path grammar
<vault-path> ::= <namespace> "/" <segment> ("/" <segment>)*
<namespace> ::= <kebab-case-string>
<segment> ::= <printable, no "/", no "*", except trailing "*">
Vaults MAY layer additional restrictions (e.g. finance/2024/* is read-only after tax-year close). Such policies are vault-side, not capability-side; the capability is the floor.
3.3 Audience binding
The leaf token's aud MUST be the DID of the agent performing the read. The vault verifies the chain (per UCAN tokens v1) and confirms the requesting agent identifies as the leaf audience via a fresh signature over the request.
4. Access receipts
Every successful vault read produces a substrate receipt at action_can = vault.access/read. The user can later prove who read what when; the relying party can later prove their access was authorized.
{
"principal": "did:deos:alice",
"subject": "did:deos:agent-vendor-foo",
"issued_at": 1716000000000000000,
"capability": { "chain": [/* UCAN ending at agent-vendor-foo */] },
"action": {
"type": "vault_read",
"action_can": "vault.access/read",
"inputs_hash": "<blake3 of canonical request>",
"metadata": {
"vault_id": "did:deos:alice#vault-primary",
"paths": ["calendar/2026-05-19", "calendar/2026-05-20"],
"result_hash": "<blake3 of plaintext returned>",
"result_size_bytes": 1842,
"kind": "vault.access/read",
"visibility": "silent"
}
}
}
Two properties matter:
result_hashcommits to what was returned. The plaintext is not in the receipt — the receipt is safe to publish, the data is not.inputs_hashcommits to the exact request (paths + capability-chain digest). A vault that returns different bytes than the user expects is caught by anyone with both the plaintext (the user) and the receipt (anyone):blake3(plaintext) == result_hashwill disagree.
Vault implementations MUST emit an access receipt for every read before returning bytes. A vault that doesn't is non-conforming.
Receipt visibility defaults to silent for routine reads. High-stakes paths (financial, health) SHOULD be configured to surface or focal so the user is notified.
4.1 Failure receipts
A denied read SHOULD also produce a receipt at action_can = vault.access/denied with metadata.reason. This lets the user audit attempted accesses, not just successful ones, and lets the substrate-rooted reputation §3.2 graph (white paper) include denied-access edges.
5. Selective disclosure (proof-only reads)
A counterparty doesn't need Alice's HSA balance — it needs to know balance >= 87.99. A health provider doesn't need her medical history — it needs has_immunity(rubella) == true. Selective disclosure produces a zero-knowledge proof of the predicate without revealing the value.
5.1 Proof request
The agent sends:
{
"vault_id": "did:deos:alice#vault-primary",
"predicate": {
"type": "range",
"path": "finance/hsa/balance_cents",
"operator": ">=",
"constant": 8799
},
"audience": "did:web:counterparty.example",
"nonce": "<base64url>"
}
The capability chain for a proof-only request uses can: "prove/v1" and the leaf MAY include a disclosure_policy caveat narrowing which predicate shapes are permitted.
5.2 Predicate types
v1 defines five:
equality—path == constantrange—path</<=/>/>=constantmembership—path ∈ {a, b, c, ...}against a constant setexistence—pathexists (without revealing the value)predicate-and— conjunction of any of the above (single ZK circuit)
Custom predicates are permitted but MUST namespace and publish their circuit. Vaults SHOULD reject unrecognized predicate types with a vault.proof/unsupported error.
5.3 Proof envelope
The vault returns:
{
"vault_id": "did:deos:alice#vault-primary",
"predicate": { /* same as request */ },
"proof": {
"circuit": "bulletproof/range/v1",
"proof_b64": "<base64>",
"public_inputs": {
"commitment": "<base64 32-byte commitment to the value>",
"nonce": "<echo of request nonce>"
}
},
"anchor": {
"receipt_id": "<id of the corresponding vault.access/prove receipt>",
"log_root_at_prove": "<hex>"
},
"issued_at_ns": 1716000000000000000,
"valid_until_ns": 1716000300000000000
}
The relying party verifies the ZK proof against the proof circuit's verifier, checks the nonce echoes the request, checks valid_until_ns covers now, and verifies the substrate receipt at anchor.receipt_id is the same vault-id + audience + nonce as the envelope.
5.4 Proof-anchor receipt
Every proof-only read emits a substrate receipt at action_can = vault.access/prove. Metadata names the predicate type, audience, nonce, and proof commitment. The plaintext value never appears on the substrate. The receipt is the user-visible record of which counterparties asked which predicates against their vault.
5.5 Why not just send the ZK proof out-of-band
Two reasons:
- Auditability. The user wants a personal log of who asked what about them, and when. The
vault.access/provereceipt is that log. - Composition. A relying party needing to dispute the proof later (claim the user revoked the underlying capability mid-proof, for example) needs the receipt as the time anchor.
6. Capability composition
6.1 With personhood
A vault SHOULD reject can: "prove/v1" requests from chains whose root principal lacks a personhood assertion at tier ≥ moderate (per personhood/v1). Threat: a sybil pool harvests "balance ≥ X" proofs across many fake identities and binary-searches the threshold.
Personhood binds at the root, not at intermediate agents (per personhood/v1 §7).
6.2 With settlement
A conditional-settlement escrow (settlement/conditional/v1) MAY require a vault.access/prove receipt as part of its release_on outcome predicate — e.g. "release when Bob's courier produces a shipment.delivered/v1 receipt AND Alice's vault produces a vault.access/prove receipt for delivery_confirmed == true". This blocks the courier-colludes-with-itself failure mode.
6.3 With A2A
An A2A Task artifact MAY carry a vault proof envelope as an additional extension alongside the substrate-receipts envelope. The pattern: the agent fulfills a request that required revealing a vault-derived fact, and ships the proof in the response so the counterparty can verify.
7. Vault-side obligations
A conforming vault MUST:
- Verify the inbound UCAN chain end-to-end before any read (signatures, audience, attenuation, expiry).
- Enforce path-prefix matching: the leaf's
withMUST cover every path in the request. - Enforce
max_readscounters by capability-chain-CID, persistently — counters survive process restart. - Emit the access receipt before returning bytes / proofs.
- For
prove/v1reads: never include plaintext in the receipt or the proof envelope's public inputs. - Implement at least one revocation channel — accept
vault.access/revokecapability-CIDs and reject subsequent reads bearing them. Vaults that don't support runtime revocation MUST set a low defaultexpires_atcap.
A vault MUST NOT:
- Bind the read to anything outside the audience DID (no per-IP, per-User-Agent fingerprinting that's not the user's policy).
- Log plaintext bodies anywhere accessible to the vault operator (the encryption scheme assumes the vault operator does not hold cleartext).
- Return different bytes for the same
(path, version)pair to different callers — the bytes are content-addressed byresult_hash; a vault that branches its responses is detectable.
8. Threat model
8.1 Vault operator corruption
The operator tries to read plaintext or sell the user's data. Defended by:
- The encryption scheme: bytes at rest are sealed to the user's X25519 keypair the operator does not hold.
- For
prove/v1: the vault generates proofs in a trusted enclave or via the user's local agent (the local-agent pattern is the v1 default; remote enclave bridging is v1.1). - The access receipt: every read is on the substrate. An operator who exfiltrates and a downstream buyer who acts on it both end up with reputation-damaging receipts.
8.2 Capability theft
A leaked UCAN with vault.* capability is used to drain a vault. Defended by:
max_readscap.- Short
expires_at(default capped per vault operator). - Revocation tombstones.
- Audience binding: stealing the leaf token doesn't grant access without the audience DID's signing key.
The capability is bearer-equivalent only at the leaf-audience layer. Same threat shape as OAuth bearer tokens, but audience-binding adds a signing requirement at use time.
8.3 Replay across audiences
An agent who got a prove/v1 envelope for audience A tries to present it to B. Defended by:
- The proof envelope's
public_inputs.audience(or a tighter binding via the proof circuit, scheme-dependent). - Relying parties MUST verify the audience binding before trusting the proof.
8.4 Long-tail predicate inference
A counterparty learns the underlying value by submitting many narrow predicates ("balance ≥ 100? ≥ 200? ≥ 300?…"). Defended by:
- Rate limiting per (audience, predicate-prefix) at the vault.
- Per-audience predicate logs on the access receipts let the user detect and revoke fishing audiences.
- For high-stakes vaults, mandatory user approval on each predicate request (the silent → surface receipt visibility upgrade in §4).
8.5 Coerced reads
The user is forced to delegate a wide capability under duress. Defended only by:
- Real-world duress mitigations (deactivation tombstones from did:deos v1 §7).
- The append-only audit makes the coerced reads visible after the fact.
9. Reference implementation surface
The TS SDK ships @deos/receipt/vault exposing:
parseVaultDescriptor(didDocument, vaultId?)— extract a vault entry from a DID document.matchPathToCapability(att, requestedPath)— predicate that the spec §3.2 path grammar requires; useful both for vault implementers and for clients building chains.buildAccessRequest(opts)— canonical request shape ready for substrate inputs-hash computation.parseProofEnvelope(env)— validate the shape; the ZK proof itself is scheme-specific and NOT bundled.
The SDK does not implement a vault. Operators integrate their own backend (Solid pod, Lit, KV, etc.); the SDK gives them the wire types and the verifier-side checks that don't depend on the storage substrate.
10. Versioning
v1.0. The URI https://projectmaya.deoscomputing.io/data/vault/v1 is immutable; breaking changes go to /v2.
Planned for v1.1:
- Remote-enclave prover path for
prove/v1(today the user's local agent runs the prover). disclosure_policycaveat for narrowing acceptable predicate types in capability tokens.audience_bindZK constraint so the proof itself binds to audience, not just the envelope wrapping it.
11. Maintainer
DEOS Computing — pull requests, vault-backend integrations, and threat-model critiques to github.com/DEOS-Computing.
License: CC BY 4.0 (text), Apache-2.0 (reference code).