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:

  1. The user owns an encrypted personal data store (a vault).
  2. Reads are gated by capability tokens delegated by the user, not ACLs on the vault operator.
  3. 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:

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:

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:

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
}

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:

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:

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:


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:

  1. Verify the inbound UCAN chain end-to-end before any read (signatures, audience, attenuation, expiry).
  2. Enforce path-prefix matching: the leaf's with MUST cover every path in the request.
  3. Enforce max_reads counters by capability-chain-CID, persistently — counters survive process restart.
  4. Emit the access receipt before returning bytes / proofs.
  5. For prove/v1 reads: never include plaintext in the receipt or the proof envelope's public inputs.
  6. Implement at least one revocation channel — accept vault.access/revoke capability-CIDs and reject subsequent reads bearing them. Vaults that don't support runtime revocation MUST set a low default expires_at cap.

A vault MUST NOT:

  1. Bind the read to anything outside the audience DID (no per-IP, per-User-Agent fingerprinting that's not the user's policy).
  2. Log plaintext bodies anywhere accessible to the vault operator (the encryption scheme assumes the vault operator does not hold cleartext).
  3. Return different bytes for the same (path, version) pair to different callers — the bytes are content-addressed by result_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:

8.2 Capability theft

A leaked UCAN with vault.* capability is used to drain a vault. Defended by:

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:

8.4 Long-tail predicate inference

A counterparty learns the underlying value by submitting many narrow predicates ("balance ≥ 100? ≥ 200? ≥ 300?…"). Defended by:

8.5 Coerced reads

The user is forced to delegate a wide capability under duress. Defended only by:


9. Reference implementation surface

The TS SDK ships @deos/receipt/vault exposing:

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:


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).