{
    "componentChunkName": "component---src-templates-protocol-protocol-tsx",
    "path": "/vaults/1.0/",
    "result": {"data":{"markdownRemark":{"html":"<h2 id=\"Summary\">Summary</h2>\n<p>Vaults 1.0 is a DIDComm-based <strong>coordination protocol</strong> that lets agents create, share, and retire <strong>end-to-end encrypted data vaults</strong> (EDV) on heterogeneous backends (e.g., S3, Arweave). It handles <strong>who gets access, for how long, and to which objects</strong>, while all actual bytes flow over the <strong>EDV HTTP API</strong> as JOSE/JWE ciphertext with <strong>encrypted indexes</strong> for equality querying. It purposely <strong>does not</strong> handle signing—pair it with your existing <strong>Signing 1.0</strong> protocol via content references (digest + location + capability). (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</p>\n<p>This draft also defines an **optional Threshold Decryption ** that allows data (or its content-encryption key) to become decryptable <strong>only after N-of-M approvals</strong>, coordinated via DIDComm and Signing 1.0.</p>\n<hr>\n<h2 id=\"Goals--Non-Goals\">Goals / Non-Goals</h2>\n<p><strong>Goals</strong></p>\n<ul>\n<li>Provision <strong>ephemeral or durable</strong> shared vaults among agents.</li>\n<li>Delegate <strong>least-privilege</strong> capabilities (time/size/path caveats).</li>\n<li>Support <strong>multi-recipient encryption</strong> and <strong>encrypted equality indexes</strong>.</li>\n<li>Coordinate <strong>N-of-M approval-gated release</strong> and <strong>true threshold decryption</strong> (cryptographic N-of-M) for decryption events.</li>\n</ul>\n<p><strong>Non-Goals</strong></p>\n<ul>\n<li>No signing, canonicalization, threshold <strong>signature</strong> orchestration, or business policy logic (defer to <strong>Signing 1.0</strong>).</li>\n<li>No duplicate CRUD—agents use the <strong>EDV HTTP API</strong> for data path. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</li>\n</ul>\n<hr>\n<h2 id=\"Roles\">Roles</h2>\n<ul>\n<li><strong>Requester</strong>: initiates a shared vault for a workflow (controller by default).</li>\n<li><strong>Participant</strong>: granted constrained access (read/write/replicate).</li>\n<li><strong>Vault Host</strong>: EDV server fronting storage (S3, Arweave, etc.).</li>\n</ul>\n<hr>\n<h2 id=\"External-References-normative\">External References (normative)</h2>\n<ul>\n<li><strong>EDV v0.1</strong>: model, sequence/OCC, encrypted indexes, HTTP API. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</li>\n<li><strong>JWE (RFC 7516)</strong>: content encryption, general JSON serialization (multi-recipient). (<a href=\"https://datatracker.ietf.org/doc/html/rfc7516\" title=\"RFC 7516 - JSON Web Encryption (JWE)\">IETF Datatracker</a>)</li>\n<li><strong>HPKE (RFC 9180)</strong>: KEM+KDF+AEAD envelopes for sealed secrets / key delivery. (<a href=\"https://www.rfc-editor.org/rfc/rfc9180.html\" title=\"RFC 9180 - Hybrid Public Key Encryption (HPKE)\">IETF Datatracker</a>)</li>\n</ul>\n<p><strong>Backend notes</strong></p>\n<ul>\n<li><strong>S3</strong>: deletable objects.</li>\n<li><strong>Arweave</strong>: immutable \"permanent\" storage; practical deletion via <strong>cryptographic erasure</strong> (destroy keys). (<a href=\"https://academy.swissborg.com/en/learn/arweave\" title=\"What is Arweave? Decentralized Permanent Storage on ...\">SwissBorg Academy</a>)</li>\n<li><strong>IPFS</strong>: decentralized storage</li>\n</ul>\n<hr>\n<h2 id=\"Data-Model\">Data Model</h2>\n<h3 id=\"VaultDescriptor\">VaultDescriptor</h3>\n<pre><code class=\"language-json\">{\n  \"vault_id\": \"urn:edv:zA1...:vault123\",\n  \"controller\": \"did:example:controller\",\n  \"base_url\": \"https://edv.example.com/edvs/vault123\",\n  \"zcap_root\": { \"...\": \"root-capability\" },\n  \"encryption\": { \"content\": \"JWE+ECDH-ES\", \"index_hmac\": \"HMAC-SHA-256\" },\n  \"retention\": { \"kind\": \"ephemeral|durable\", \"expires_at\": \"2025-10-20T00:00:00Z\" },\n  \"backend\": { \"type\": \"s3|arweave|...\", \"hints\": { \"region\": \"us-east-1\" } },\n  \"limits\": { \"max_docs\": 200, \"max_bytes\": 104857600, \"max_doc_bytes\": 10485760 },\n  \"index_schema\": [\"workflow_id\",\"digest\",\"stage\"]\n}\n</code></pre>\n<blockquote>\n<p>EDV documents carry <code>id</code>, <code>sequence</code>, and a JWE payload; searchable <strong>encrypted indexes</strong> are computed client-side. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</p>\n</blockquote>\n<h3 id=\"ContentRef-to-bridge-to-Signing-10\">ContentRef (to bridge to <strong>Signing 1.0</strong>)</h3>\n<pre><code class=\"language-json\">{\n  \"vault_id\": \"urn:edv:...:vault123\",\n  \"doc_id\": \"z19abc...\",\n  \"digest\": \"sha256-BASE64URL(...)\",\n  \"capability\": { \"...\": \"zcap granting read (and optionally write)\" }\n}\n</code></pre>\n<hr>\n<h2 id=\"Protocol-Overview\">Protocol Overview</h2>\n<p><strong>Control plane:</strong> DIDComm v2 messages for negotiation, capability distribution, lifecycle (create/join/seal/tombstone), optional replication.</p>\n<p><strong>Data plane:</strong> EDV HTTP for CRUD/queries; attach ZCAP invocation; encrypt/decrypt at the edge; use JWE general serialization for multi-recipient. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</p>\n<p><strong>Decryption events:</strong> (optional) may be <strong>approval-gated</strong> (N-of-M) or use <strong>true cryptographic threshold decryption</strong> per the in this spec. In both cases, <strong>keys are never published on immutable backends</strong>; key release or partial-decrypt shares occur off-chain via DIDComm.</p>\n<hr>\n<h2 id=\"Messages\">Messages</h2>\n<blockquote>\n<p>All messages are DIDComm v2 <strong><code>type</code></strong> = <code>https://didcomm.org/vaults/1.0/&#x3C;name></code> with <strong><code>id</code></strong>, <strong><code>pthid</code></strong> (parent thread = workflow), and optional <strong><code>thid</code></strong> threading. (<a href=\"https://identity.foundation/didcomm-messaging/spec/v2.0/\" title=\"DIDComm Messaging Specification v2.0\">identity.foundation</a>)</p>\n</blockquote>\n<h3 id=\"propose\">propose</h3>\n<p>Propose creating a shared vault.</p>\n<p><strong>Body</strong></p>\n<pre><code class=\"language-json\">{\n  \"purpose\": \"workflow:pdf-signing#42\",\n  \"participants\": [\"did:example:alice\",\"did:example:bob\"],\n  \"constraints\": {\n    \"retention\": \"ephemeral\",\n    \"ttl_seconds\": 86400,\n    \"limits\": { \"max_docs\": 200, \"max_bytes\": 104857600, \"max_doc_bytes\": 10485760 }\n  },\n  \"backend_prefs\": [\"s3\",\"arweave\"],\n  \"index_schema\": [\"workflow_id\",\"digest\",\"stage\"]\n}\n</code></pre>\n<p><strong>Expected reply</strong>: <code>offer</code> (from provisioner/host or a peer offering to host).</p>\n<p><strong>Workflow binding convention</strong>: When a vault is provisioned for a Workflow 1.0 instance, the <code>purpose</code> field SHOULD use the format <code>workflow:&#x3C;template_id>#&#x3C;instance_id></code> (e.g., <code>workflow:student-id-issuance#4b6f...</code>). This enables the vault host and participants to correlate the vault with its owning workflow. The Workflow protocol's <code>start</code> message references the resulting vault via <code>vault_ref</code>. Retention and TTL constraints SHOULD align with the expected workflow duration.</p>\n<hr>\n<h3 id=\"offer\">offer</h3>\n<p>Return a ready vault.</p>\n<p><strong>Body</strong></p>\n<pre><code class=\"language-json\">{\n  \"vault_descriptor\": { \"...\": \"VaultDescriptor\" },\n  \"provisioner\": \"did:example:host\"\n}\n</code></pre>\n<hr>\n<h3 id=\"grant-access\">grant-access</h3>\n<p>Controller delegates a capability to a participant (ZCAP-LD).</p>\n<p><strong>Body</strong></p>\n<pre><code class=\"language-json\">{\n  \"to\": \"did:example:bob\",\n  \"capability\": {\n    \"...\": \"zcap with caveats (verb=read|write|query|replicate, path=/docs/pdf-42-*, expires_at, max_bytes, non_delegable)\"\n  }\n}\n</code></pre>\n<blockquote>\n<p>Capabilities and caveats per ZCAP-LD; delegation chains MUST verify on the server. (<a href=\"https://w3c-ccg.github.io/zcap-spec/\" title=\"Authorization Capabilities for Linked Data v0.3\">w3c-ccg.github.io</a>)</p>\n</blockquote>\n<p><strong>Problem reports</strong>: <code>cap-invalid</code>, <code>cap-expired</code>, <code>cap-unauthorized</code>.</p>\n<hr>\n<h3 id=\"notify-optional\">notify (optional)</h3>\n<p>Lightweight announcement of interesting changes (new doc, sealed, replicated).</p>\n<p><strong>Body</strong></p>\n<pre><code class=\"language-json\">{\n  \"kind\": \"doc-created|doc-updated|sealed|replicated\",\n  \"doc_id\": \"z19abc...\",\n  \"indexed\": { \"workflow_id\": \"HMAC(...)\", \"stage\": \"HMAC(...)\" }\n}\n</code></pre>\n<hr>\n<h3 id=\"replicate-optional\">replicate (optional)</h3>\n<p>Ask a mirror host to replicate ciphertext to another EDV (e.g., S3 → Arweave).</p>\n<p><strong>Body</strong></p>\n<pre><code class=\"language-json\">{\n  \"target\": { \"type\": \"arweave\", \"endpoint\": \"https://edv.mirror/edvs/vaultX\" },\n  \"scope\": { \"prefix\": \"pdf-42-\", \"include_indexes\": true }\n}\n</code></pre>\n<p><strong>Reply</strong>: <code>replicate-receipt</code> with list of digests and target doc IDs.</p>\n<hr>\n<h3 id=\"seal\">seal</h3>\n<p>Rotate capabilities to read-only; freeze the workspace.</p>\n<p><strong>Body</strong></p>\n<pre><code class=\"language-json\">{ \"mode\": \"read-only\" }\n</code></pre>\n<hr>\n<h3 id=\"tombstone\">tombstone</h3>\n<p>Retire a vault.</p>\n<ul>\n<li>For <strong>S3/ordinary EDV</strong>: delete documents + indexes.</li>\n<li>For <strong>Arweave</strong>: perform <strong>cryptographic erasure</strong> (destroy keys) and emit a finalization record listing affected digests—data stays immutable but <strong>unreadable</strong> without keys. (<a href=\"https://academy.swissborg.com/en/learn/arweave\" title=\"What is Arweave? Decentralized Permanent Storage on ...\">SwissBorg Academy</a>)</li>\n</ul>\n<p><strong>Body</strong></p>\n<pre><code class=\"language-json\">{\n  \"strategy\": \"delete|destroy-keys\",\n  \"evidence_doc_id\": \"z1finalnote...\"\n}\n</code></pre>\n<hr>\n<h2 id=\"Flows\">Flows</h2>\n<h3 id=\"Capability-discovery-pre-flight\">Capability discovery (pre-flight)</h3>\n<p>Use <strong>Discover Features 2.0</strong> to check support for <code>vaults/1.0</code>, max sizes, backends, replication ability. (<a href=\"https://didcomm.org/discover-features/2.0/\" title=\"Discover Features 2.0\">didcomm.org</a>)</p>\n<h3 id=\"Two-party-ephemeral-vault-for-PDF-signing-happy-path\">Two-party ephemeral vault for PDF signing (happy path)</h3>\n<ol>\n<li>\n<p><strong>Alice → Bob + Host</strong>: <code>propose</code> (purpose, ttl, limits, backends).</p>\n</li>\n<li>\n<p><strong>Host → Alice</strong>: <code>offer</code> (VaultDescriptor + root zcap).</p>\n</li>\n<li>\n<p><strong>Alice → Bob</strong>: <code>grant-access</code> (write to <code>/docs/pdf-42-*</code>, TTL 24h).</p>\n</li>\n<li>\n<p><strong>Data path (EDV)</strong>:</p>\n<ul>\n<li>Alice <strong>PUT</strong> encrypted PDF (<code>stage=src</code>).</li>\n<li>Signing 1.0 sends <code>ContentRef</code> to Bob; Bob <strong>GET</strong> + verify digest; Bob <strong>PUT</strong> partial signature (<code>stage=partial</code>).</li>\n<li>Aggregator <strong>PUT</strong> final signature + receipt (<code>stage=final</code>).</li>\n</ul>\n</li>\n<li>\n<p><strong>Alice → All</strong>: <code>seal</code> (read-only).</p>\n</li>\n<li>\n<p><strong>(Optional)</strong> <code>replicate</code> to Arweave archive vault; then <code>tombstone</code> the working vault. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</p>\n</li>\n</ol>\n<hr>\n<h2 id=\"State-Machine-controller-view\">State Machine (controller view)</h2>\n<table>\n<thead>\n<tr>\n<th>State</th>\n<th>Event</th>\n<th>Next State</th>\n<th>Notes</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>NEW</code></td>\n<td><code>offer</code> accepted</td>\n<td><code>ACTIVE</code></td>\n<td>VaultDescriptor stored; root zcap held by controller.</td>\n</tr>\n<tr>\n<td><code>ACTIVE</code></td>\n<td><code>grant-access</code></td>\n<td><code>ACTIVE</code></td>\n<td>Participants can perform EDV ops per caveats.</td>\n</tr>\n<tr>\n<td><code>ACTIVE</code></td>\n<td><code>seal</code></td>\n<td><code>SEALED</code></td>\n<td>Rotate caps to read-only; no more writes.</td>\n</tr>\n<tr>\n<td><code>SEALED</code></td>\n<td><code>replicate</code> (opt.)</td>\n<td><code>SEALED</code></td>\n<td>Mirror ciphertext; collect receipts.</td>\n</tr>\n<tr>\n<td><code>SEALED</code></td>\n<td><code>tombstone</code></td>\n<td><code>RETIRED</code></td>\n<td>Delete or cryptographically erase.</td>\n</tr>\n</tbody>\n</table>\n<hr>\n<h2 id=\"EDV-Usage-normative\">EDV Usage (normative)</h2>\n<ul>\n<li><strong>Encryption</strong>: Agents MUST encrypt content as <strong>JWE</strong> before upload; multi-party access MAY use <strong>General JSON Serialization</strong> for multiple recipients. (<a href=\"https://datatracker.ietf.org/doc/html/rfc7516\" title=\"RFC 7516 - JSON Web Encryption (JWE)\">IETF Datatracker</a>)</li>\n<li><strong>Indexes</strong>: Agents SHOULD compute equality-only indexes via HMAC and upload with each document to enable queries with minimal leakage. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</li>\n<li><strong>Concurrency</strong>: Agents MUST respect <strong><code>sequence</code></strong> for optimistic concurrency control and retry on conflict. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</li>\n</ul>\n<hr>\n<h2 id=\"Authorization-normative\">Authorization (normative)</h2>\n<ul>\n<li>Capabilities MUST be expressed as <strong>ZCAP-LD</strong> documents, bound to vault endpoints and <strong>caveated</strong> by:\n<code>expires_at</code>, verbs (<code>read|write|query|replicate</code>), path/prefix, and size/byte limits; optionally non-delegable. (<a href=\"https://w3c-ccg.github.io/zcap-spec/\" title=\"Authorization Capabilities for Linked Data v0.3\">w3c-ccg.github.io</a>)</li>\n<li>Hosts MUST verify capability chains and caveats for each EDV request (out of band from DIDComm messages).</li>\n</ul>\n<hr>\n<h2 id=\"Threshold-Decryption-Optional\"><strong>Threshold Decryption (Optional)</strong></h2>\n<p><strong>Purpose:</strong> Make decryption happen <strong>only after N-of-M approvals</strong>. Two modes are supported:</p>\n<ol>\n<li><strong>Approval-gated release (policy-level)</strong> — N approvals collected via DIDComm / Signing 1.0; coordinator releases the CEK as a <strong>sealed secret</strong> (HPKE) to the authorized device.</li>\n<li><strong>True cryptographic threshold decryption</strong> — The CEK (or data) is encrypted under a <strong>threshold public key</strong>; any N holders produce <strong>partial decrypt shares</strong> that combine to recover the CEK (or plaintext). No single party can decrypt alone.</li>\n</ol>\n<blockquote>\n<p>This covers <strong>(2)</strong>. Mode (1) already works without changes (use Signing 1.0 threshold collection + <code>sealed-secret@1</code> delivery).</p>\n</blockquote>\n<h3 id=\"Threshold-KEM-Header-attached-to-the-object-descriptor\">Threshold KEM Header (attached to the object descriptor)</h3>\n<pre><code class=\"language-json\">{\n  \"threshold_kem\": {\n    \"scheme\": \"threshold-kem@1\",\n    \"t\": 2,\n    \"n\": 3,\n    \"pub\": \"base64url(public_key_bytes)\",\n    \"params\": {\n      \"kem\": \"ECIES-X25519\",\n      \"kdf\": \"HKDF-SHA256\",\n      \"aead\": \"AES-256-GCM\"\n    },\n    \"cipher_cek\": \"base64url(...)\",   // CEK encrypted under the threshold public key\n    \"aad\": {\n      \"session_id\": \"sess_...\",\n      \"object_id\": \"so_...\",\n      \"kem_params_hash\": \"sha-256:...\",\n      \"policy_hash\": \"sha-256:...\"    // optional but recommended\n    }\n  }\n}\n</code></pre>\n<blockquote>\n<p>If you encrypt the <strong>data directly</strong> under the threshold key, use <code>cipher_data</code> instead of <code>cipher_cek</code>. The rest of the flow is identical; the aggregator yields plaintext rather than CEK.</p>\n</blockquote>\n<h3 id=\"Message-Reuse\">Message Reuse</h3>\n<p>We reuse the existing DIDComm messages; we only define <strong>what goes in them</strong>.</p>\n<h4 id=\"request-decrypt-session-using-threshold\"><code>request</code> (decrypt session using threshold)</h4>\n<p>Use <code>vaults/1.0</code> to set up access + transport, and <strong>Signing 1.0</strong> (recommended) to orchestrate the threshold session:</p>\n<pre><code class=\"language-json\">{\n  \"type\": \"https://didcomm.org/signing/1.0/request-signing\",\n  \"body\": {\n    \"session\": { \"session_id\": \"sess_...\", \"mode\": { \"type\": \"threshold\" } },\n    \"object\": { \"...\": \"Signable (or Decryptable) Object + threshold_kem header\" },\n    \"suite\": { \"suite\": \"threshold-kem@1\" },\n    \"constraints\": {\n      \"not_before\": \"...\", \"expires_time\": \"...\",\n      \"intended_audience\": [\"did:ex:device\"], \"use_limit\": 1\n    }\n  }\n}\n</code></pre>\n<h4 id=\"partial-signature-carries-partial-decrypt-share\"><code>partial-signature</code> (carries <strong>partial decrypt share</strong>)</h4>\n<pre><code class=\"language-json\">{\n  \"type\": \"https://didcomm.org/signing/1.0/partial-signature\",\n  \"body\": {\n    \"session_id\": \"sess_...\",\n    \"object_id\": \"so_...\",\n    \"signer\": \"did:ex:holder2\",\n    \"suite\": \"threshold-kem@1\",\n    \"kind\": \"partial-decrypt\",\n    \"data\": {\n      \"share\": \"base64url(partial_decrypt_share)\",\n      \"aad_hash\": \"sha-256:...\"  // MUST equal hash(serialize(threshold_kem.aad))\n    }\n  }\n}\n</code></pre>\n<h4 id=\"combine-threshold-met\"><code>combine</code> (threshold met)</h4>\n<pre><code class=\"language-json\">{\n  \"type\": \"https://didcomm.org/signing/1.0/combine\",\n  \"body\": {\n    \"session_id\": \"sess_...\",\n    \"status\": \"threshold_met\",\n    \"aggregation_result\": {\n      \"type\": \"threshold-decrypt@1\",\n      \"n\": 2, \"m\": 3\n    }\n  }\n}\n</code></pre>\n<h4 id=\"provide-artifacts-deliver-CEK-or-plaintext\"><code>provide-artifacts</code> (deliver CEK or plaintext)</h4>\n<p><strong>CEK delivery (recommended)</strong> sealed to the device using <strong>HPKE</strong> (RFC 9180) and bound to a <strong>one-use authorization token</strong> (counter, expiry, device):</p>\n<pre><code class=\"language-json\">{\n  \"type\": \"https://didcomm.org/signing/1.0/provide-artifacts\",\n  \"body\": {\n    \"session_id\": \"sess_...\",\n    \"artifacts\": [\n      {\n        \"type\": \"sealed-secret@1\",\n        \"suite\": \"envelope-hpke@1\",\n        \"aad\": { \"ticket_digest\": \"sha-256:...\" },\n        \"ciphertext\": \"base64url(HPKE(CEK))\",\n        \"enc\": { \"kem\": \"X25519\", \"kdf\": \"HKDF-SHA256\", \"aead\": \"AES-256-GCM\", \"ek_pub\": \"...\" }\n      }\n    ],\n    \"token\": {\n      \"token\": {\n        \"typ\": \"signing-ticket\",\n        \"session_id\": \"sess_...\",\n        \"scope\": \"decrypt\",\n        \"device\": \"did:ex:device#k1\",\n        \"ctr\": 42,\n        \"exp\": \"2025-10-19T16:02:00Z\",\n        \"cap\": 1\n      },\n      \"sig\": { \"suite\":\"jws-ed25519@1\",\"kid\":\"did:ex:coord#k1\",\"value\":\"...\" }\n    }\n  }\n}\n</code></pre>\n<p><em>(Data-direct variant: stream plaintext or attach a reference after the same token checks.)</em></p>\n<h3 id=\"Registry-Additions-Threshold-Decrypt\">Registry Additions (Threshold Decrypt)</h3>\n<p><strong>Suites</strong></p>\n<table>\n<thead>\n<tr>\n<th>Registry Key</th>\n<th>Description</th>\n<th>Inputs</th>\n<th>Outputs</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>threshold-kem@1</code></td>\n<td>Threshold public-key encryption for CEK (ElGamal/ECIES/HPKE-style).</td>\n<td>KEM header (<code>cipher_cek</code> or <code>cipher_data</code>), holder’s private share, <code>aad_hash</code></td>\n<td><code>partial_decrypt_share</code></td>\n</tr>\n</tbody>\n</table>\n<blockquote>\n<p>Implementations MUST commit to <code>aad_hash = sha256(serialize(aad))</code> to prevent cross-session replay. The concrete math (curve/KEM) is pluggable; publish parameters via <code>kem_params_hash</code>.</p>\n</blockquote>\n<p><strong>Aggregators</strong></p>\n<table>\n<thead>\n<tr>\n<th>Registry Key</th>\n<th>Description</th>\n<th>Input</th>\n<th>Output</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>threshold-decrypt@1</code></td>\n<td>Combines <strong>N</strong> partial decrypt shares into CEK (or plaintext).</td>\n<td>N <code>partial-signature(kind=partial-decrypt)</code> with consistent <code>aad_hash</code> + common KEM header</td>\n<td>CEK (then <code>sealed-secret@1</code>) or plaintext</td>\n</tr>\n</tbody>\n</table>\n<p>** Problem Reports (delta)**</p>\n<ul>\n<li><code>share-invalid</code> — malformed or fails combine/validation</li>\n<li><code>share-duplicate</code> — duplicate signer/index</li>\n<li><code>threshold-not-reached</code> — insufficient valid shares</li>\n<li><code>aggregation-failed</code> — combine failed integrity checks</li>\n</ul>\n<h3 id=\"Security-Requirements-delta\">Security Requirements (delta)</h3>\n<ul>\n<li><strong>No single holder can decrypt alone</strong>; a single share reveals nothing.</li>\n<li><strong>Binding</strong>: Shares MUST commit to <code>aad_hash</code> (session/object/policy binding).</li>\n<li><strong>Replay-proof release</strong>: CEK/plaintext delivery MUST be bound to an authorization token (device DID, monotonic <code>ctr</code>, expiry).</li>\n<li><strong>Verification</strong>: Aggregator MUST verify AEAD tag (or CEK checksum) after combine; reject otherwise.</li>\n<li><strong>DKG</strong>: Secure DKG ceremony is out-of-scope for messages; deployments MUST pin <code>kem_params_hash</code>.</li>\n</ul>\n<hr>\n<h2 id=\"Security-Considerations\">Security Considerations</h2>\n<ul>\n<li><strong>Client-side encryption</strong>: Providers see only ciphertext and HMAC'd indexes; plaintext lives only at the edge. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</li>\n<li><strong>Transport</strong>: DIDComm <strong>authcrypt</strong> recommended for authenticity + confidentiality of control messages. (<a href=\"https://identity.foundation/didcomm-messaging/spec/v2.0/\" title=\"DIDComm Messaging Specification v2.0\">identity.foundation</a>)</li>\n<li><strong>Capabilities</strong>: Prefer short TTLs, tight path scoping, byte limits, and non-delegability where appropriate. Rotate caps at <strong>seal</strong>. (<a href=\"https://w3c-ccg.github.io/zcap-spec/\" title=\"Authorization Capabilities for Linked Data v0.3\">w3c-ccg.github.io</a>)</li>\n<li><strong>Leakage</strong>: Encrypted equality indexes leak <strong>existence and equality</strong>; avoid sensitive tags; no range/prefix queries. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</li>\n<li><strong>Immutability backends</strong>: For Arweave, treat \"delete\" as <strong>cryptographic erasure</strong> (destroy keys) and publish a finalization record listing affected digests. (<a href=\"https://academy.swissborg.com/en/learn/arweave\" title=\"What is Arweave? Decentralized Permanent Storage on ...\">SwissBorg Academy</a>)</li>\n<li><strong>Threshold Decrypt</strong> : Never publish keys on immutable backends. Shares, tokens, and sealed secrets travel only via DIDComm.</li>\n<li><strong>Workflow-scoped vaults</strong>: When a vault is bound to a Workflow 1.0 instance, the vault TTL SHOULD be synchronized with the workflow's expected duration. If the workflow completes, the Coordinator or Processor SHOULD trigger <code>seal</code>; if canceled, <code>tombstone</code>. Key material used for vault encryption SHOULD be wiped promptly after the vault is sealed or tombstoned.</li>\n<li><strong>Transient data</strong>: Workflow <code>$transient</code> fields (single-cycle ephemeral values) MUST NOT be stored in vaults. They exist only in volatile memory during a single workflow advance cycle.</li>\n<li><strong>Guard resolution timing</strong>: When a Workflow Processor resolves vault-backed <code>$ref</code> pointers for guard evaluation, the decrypted plaintext MUST be held only in memory for the duration of the evaluation and MUST NOT be cached, logged, or persisted.</li>\n</ul>\n<hr>\n<h2 id=\"Privacy-Considerations\">Privacy Considerations</h2>\n<ul>\n<li>Minimize metadata in DIDComm headers and EDV indexes.</li>\n<li>Consider <strong>group encryption</strong> via JWE general serialization to avoid separate per-recipient blobs. (<a href=\"https://datatracker.ietf.org/doc/html/rfc7516\" title=\"RFC 7516 - JSON Web Encryption (JWE)\">IETF Datatracker</a>)</li>\n<li><strong>Workflow field indexing</strong>: When vault documents store workflow context fields, avoid indexing field names or values that could leak the workflow's purpose or the subject's identity. Use opaque identifiers (e.g., <code>doc_id</code> based on random values, not field names) and limit encrypted indexes to structural keys like <code>workflow_id</code> and <code>stage</code>. Do not index <code>secret</code>-level field names or values.</li>\n<li><strong>Role-grouped documents</strong>: Grouping sensitive workflow fields into EDV documents by role-access pattern (rather than one document per field) reduces the number of EDV queries observable by the vault host, minimizing metadata leakage about field-level access patterns.</li>\n</ul>\n<hr>\n<h2 id=\"Interop-Notes\">Interop Notes</h2>\n<ul>\n<li>\n<p><strong>Discover Features 2.0</strong> SHOULD advertise: supported backends (<code>s3</code>, <code>arweave</code>, …), max object size, index support, replication availability, and whether <strong><code>threshold-decrypt</code></strong> is supported. (<a href=\"https://didcomm.org/discover-features/2.0/\" title=\"Discover Features 2.0\">didcomm.org</a>)</p>\n</li>\n<li>\n<p><strong>Composition with Signing 1.0</strong>:</p>\n<ul>\n<li>Use Signing 1.0 threshold sessions to collect <strong>approvals</strong> and carry <strong>partial-decrypt shares</strong>.</li>\n<li>Use Signing 1.0 <strong>sealed-secret</strong> profile (HPKE envelopes bound to tokens) for key delivery.</li>\n</ul>\n</li>\n<li>\n<p><strong>Composition with Workflow 1.0</strong>:</p>\n<ul>\n<li>Workflow templates declare a <code>sensitivity</code> map that classifies context fields. Fields with <code>storage: \"vault\"</code> are stored in a Vaults 1.0 EDV and referenced via <code>$ref</code> pointers in workflow messages.</li>\n<li>The Coordinator provisions the vault before sending Workflow <code>start</code>. The <code>propose.purpose</code> field uses the convention <code>workflow:&#x3C;template_id>#&#x3C;instance_id></code>.</li>\n<li>Vault lifecycle is bound to workflow lifecycle: <code>complete</code> triggers <code>seal</code>, <code>cancel</code> triggers <code>tombstone</code>.</li>\n<li>Agents that support both protocols SHOULD advertise <code>vault-context</code> in their Discover Features 2.0 capabilities.</li>\n</ul>\n</li>\n</ul>\n<hr>\n<h2 id=\"Worked-Example\">Worked Example</h2>\n<p><strong>Create &#x26; grant</strong></p>\n<pre><code class=\"language-json\">// Alice → Host\n{ \"type\":\"https://didcomm.org/vaults/1.0/propose\", \"body\": { \"purpose\":\"workflow:pdf-signing#42\", \"participants\":[\"did:ex:bob\"], \"constraints\":{\"ttl_seconds\":86400}, \"backend_prefs\":[\"s3\"], \"index_schema\":[\"workflow_id\",\"digest\",\"stage\"] } }\n\n// Host → Alice\n{ \"type\":\"https://didcomm.org/vaults/1.0/offer\", \"body\": { \"vault_descriptor\": { \"...\": \"VaultDescriptor\" } } }\n\n// Alice → Bob\n{ \"type\":\"https://didcomm.org/vaults/1.0/grant-access\", \"body\": { \"to\":\"did:ex:bob\", \"capability\": { \"...\": \"zcap w/ path=/docs/pdf-42-*, verb=read|write, expires_at=...\" } } }\n</code></pre>\n<p><strong>Use with Signing 1.0</strong></p>\n<pre><code class=\"language-json\">// Sign-request (separate protocol) carries a ContentRef\n{ \"type\":\"https://didcomm.org/signing/1.0/request\", \"body\": { \"content_ref\": { \"vault_id\":\"urn:edv:...:vault123\",\"doc_id\":\"pdf-42-src\",\"digest\":\"sha256-...\",\"capability\":{ \"...\": \"read-cap\" } }, \"suite\":\"PAdES\", \"policy\":{ \"...\": \"digest pinning etc.\" } } }\n</code></pre>\n<p><strong>Threshold Decrypt (CEK-wrap) — 2-of-3</strong></p>\n<pre><code class=\"language-json\">// Object descriptor carries threshold_kem header with cipher_cek\n// Requester starts a Signing 1.0 threshold session:\n{ \"type\":\"https://didcomm.org/signing/1.0/request-signing\",\n  \"body\": { \"session\": { \"session_id\": \"sess_dec1\", \"mode\": {\"type\":\"threshold\"} },\n            \"object\": { \"...\": \"includes threshold_kem\" },\n            \"suite\": { \"suite\":\"threshold-kem@1\" },\n            \"constraints\": { \"intended_audience\": [\"did:ex:alice#device\"], \"use_limit\": 1, \"expires_time\": \"...\" } } }\n\n// Two holders return partial-decrypt shares:\n{ \"type\":\"https://didcomm.org/signing/1.0/partial-signature\",\n  \"body\":{ \"session_id\":\"sess_dec1\",\"object_id\":\"so_xyz\",\"signer\":\"did:ex:holder1\",\n           \"suite\":\"threshold-kem@1\",\"kind\":\"partial-decrypt\",\"data\":{\"share\":\"...\",\"aad_hash\":\"sha-256:...\"}} }\n\n// Coordinator combines shares → CEK, then delivers as sealed secret + token:\n{ \"type\":\"https://didcomm.org/signing/1.0/provide-artifacts\",\n  \"body\": { \"session_id\":\"sess_dec1\",\n            \"artifacts\":[{\"type\":\"sealed-secret@1\",\"suite\":\"envelope-hpke@1\",\"aad\":{\"ticket_digest\":\"sha-256:...\"},\"ciphertext\":\"...\",\"enc\":{\"kem\":\"X25519\",\"kdf\":\"HKDF-SHA256\",\"aead\":\"AES-256-GCM\",\"ek_pub\":\"...\"}} ],\n            \"token\":{ \"...\": \"device-bound, ctr, exp, cap=1\" } } }\n</code></pre>\n<hr>\n<h2 id=\"Implementation-Hints\">Implementation Hints</h2>\n<ul>\n<li>\n<p>Use or adapt an existing <strong>EDV server</strong> (e.g., TrustBloc EDV) and plug different backends under it. (<a href=\"https://github.com/trustbloc/edv\" title=\"GitHub - trustbloc/edv: Encrypted data vault implementation ...\">GitHub</a>)</p>\n</li>\n<li>\n<p>Client library should expose:</p>\n<ul>\n<li><strong>EDV client</strong> (encrypt/JWE, HMAC indexes, sequence handling).</li>\n<li><strong>ZCAP</strong> issuance/delegation helpers.</li>\n<li>DIDComm handlers for <code>propose</code>, <code>offer</code>, <code>grant-access</code>, <code>seal</code>, <code>tombstone</code>, <code>replicate</code>.</li>\n<li><strong>Threshold holders</strong>: API to compute <code>partial_decrypt_share</code> given the KEM header + private share.</li>\n<li><strong>Aggregator</strong>: <code>threshold-decrypt@1</code> to combine N shares, verify integrity (AEAD/CEK checksum), and emit sealed-secret + token.</li>\n</ul>\n</li>\n<li>\n<p>For multi-party read, prefer <strong>JWE general serialization</strong> (one ciphertext, many recipients) where policy allows. For immutable archives (e.g., Arweave), keep <strong>keys off-chain</strong> and gate key release with this extension.</p>\n</li>\n</ul>\n<hr>\n<h2 id=\"Security--Compliance-Checklist\">Security &#x26; Compliance Checklist</h2>\n<ul class=\"contains-task-list\">\n<li class=\"task-list-item\"><input type=\"checkbox\" disabled> DIDComm messages <strong>authcrypt</strong>ed, with correct <code>thid/pthid</code>. (<a href=\"https://identity.foundation/didcomm-messaging/spec/v2.0/\" title=\"DIDComm Messaging Specification v2.0\">identity.foundation</a>)</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" disabled> Capabilities scoped to <strong>verb + path + TTL + size</strong>; non-delegable when needed. (<a href=\"https://w3c-ccg.github.io/zcap-spec/\" title=\"Authorization Capabilities for Linked Data v0.3\">w3c-ccg.github.io</a>)</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" disabled> EDV <strong>sequence</strong> checked on updates; retry on conflict. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" disabled> Equality indexes only; avoid sensitive attribute leakage. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" disabled> Arweave \"deletion\" via <strong>key destruction</strong> documented with finalization record. (<a href=\"https://academy.swissborg.com/en/learn/arweave\" title=\"What is Arweave? Decentralized Permanent Storage on ...\">SwissBorg Academy</a>)</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" disabled> Keys never published on immutable backends; key release/partials only via DIDComm.</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" disabled> Authorization token enforced (device, counter, expiry) for CEK/plaintext delivery.</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" disabled> <code>aad_hash</code> binding validated for all partial-decrypt shares.</li>\n</ul>\n<hr>\n<h2 id=\"Change-Log\">Change Log</h2>\n<ul>\n<li><strong>v1.0-draft</strong> — Initial publication</li>\n<li><strong>v1.0-draft+wf</strong> — Added workflow binding conventions (<code>purpose</code> format), workflow-specific security/privacy guidance, and Workflow 1.0 composition notes.</li>\n</ul>\n<hr>\n<h3 id=\"References\">References</h3>\n<p>EDV v0.1 spec; DIDComm v2; Discover Features 2.0; Message Pickup 3.0; JWE (RFC 7516); ZCAP-LD; DIDComm v2 announcement; Arweave permanence &#x26; crypto-erasure background; HPKE (RFC 9180).\n(<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</p>\n<hr>","frontmatter":{"title":"Vaults","tags":["vaults","edv","encrypted-data-vaults","storage","s3","arweave","zcap","jwe","encryption","access-control","mediator-pickup","threshold-decrypt"],"license":"MIT","publisher":"vinaysingh8866","status":"Proposed","piuri":"https://didcomm.org/vaults/1.0","summary":"A DIDComm-based coordination protocol that lets agents create, share, and retire end-to-end encrypted data vaults (EDV) on heterogeneous backends (e.g., S3, Arweave). It handles who gets access, for how long, and to which objects, while all actual bytes flow over the EDV HTTP API as JOSE/JWE ciphertext with encrypted indexes for equality querying.","authors":[{"name":"Vinay Singh","email":"vinay@verid.id"}]},"fields":{"modifiedDate":"Wed Jul 22 2026 15:46:37 GMT+0000 (Coordinated Universal Time)","avatar":"https://avatars.githubusercontent.com/u/12121665?v=4&s=48","version":"1.0"}}},"pageContext":{"matchPath":"vaults/1.0/*","id":"c51637d1-85fe-5056-8bf0-1fd514cc336d","html":"<h2 id=\"Summary\">Summary</h2>\n<p>Vaults 1.0 is a DIDComm-based <strong>coordination protocol</strong> that lets agents create, share, and retire <strong>end-to-end encrypted data vaults</strong> (EDV) on heterogeneous backends (e.g., S3, Arweave). It handles <strong>who gets access, for how long, and to which objects</strong>, while all actual bytes flow over the <strong>EDV HTTP API</strong> as JOSE/JWE ciphertext with <strong>encrypted indexes</strong> for equality querying. It purposely <strong>does not</strong> handle signing—pair it with your existing <strong>Signing 1.0</strong> protocol via content references (digest + location + capability). (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</p>\n<p>This draft also defines an **optional Threshold Decryption ** that allows data (or its content-encryption key) to become decryptable <strong>only after N-of-M approvals</strong>, coordinated via DIDComm and Signing 1.0.</p>\n<hr />\n<h2 id=\"Goals--Non-Goals\">Goals / Non-Goals</h2>\n<p><strong>Goals</strong></p>\n<ul>\n<li>Provision <strong>ephemeral or durable</strong> shared vaults among agents.</li>\n<li>Delegate <strong>least-privilege</strong> capabilities (time/size/path caveats).</li>\n<li>Support <strong>multi-recipient encryption</strong> and <strong>encrypted equality indexes</strong>.</li>\n<li>Coordinate <strong>N-of-M approval-gated release</strong> and <strong>true threshold decryption</strong> (cryptographic N-of-M) for decryption events.</li>\n</ul>\n<p><strong>Non-Goals</strong></p>\n<ul>\n<li>No signing, canonicalization, threshold <strong>signature</strong> orchestration, or business policy logic (defer to <strong>Signing 1.0</strong>).</li>\n<li>No duplicate CRUD—agents use the <strong>EDV HTTP API</strong> for data path. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</li>\n</ul>\n<hr />\n<h2 id=\"Roles\">Roles</h2>\n<ul>\n<li><strong>Requester</strong>: initiates a shared vault for a workflow (controller by default).</li>\n<li><strong>Participant</strong>: granted constrained access (read/write/replicate).</li>\n<li><strong>Vault Host</strong>: EDV server fronting storage (S3, Arweave, etc.).</li>\n</ul>\n<hr />\n<h2 id=\"External-References-normative\">External References (normative)</h2>\n<ul>\n<li><strong>EDV v0.1</strong>: model, sequence/OCC, encrypted indexes, HTTP API. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</li>\n<li><strong>JWE (RFC 7516)</strong>: content encryption, general JSON serialization (multi-recipient). (<a href=\"https://datatracker.ietf.org/doc/html/rfc7516\" title=\"RFC 7516 - JSON Web Encryption (JWE)\">IETF Datatracker</a>)</li>\n<li><strong>HPKE (RFC 9180)</strong>: KEM+KDF+AEAD envelopes for sealed secrets / key delivery. (<a href=\"https://www.rfc-editor.org/rfc/rfc9180.html\" title=\"RFC 9180 - Hybrid Public Key Encryption (HPKE)\">IETF Datatracker</a>)</li>\n</ul>\n<p><strong>Backend notes</strong></p>\n<ul>\n<li><strong>S3</strong>: deletable objects.</li>\n<li><strong>Arweave</strong>: immutable \"permanent\" storage; practical deletion via <strong>cryptographic erasure</strong> (destroy keys). (<a href=\"https://academy.swissborg.com/en/learn/arweave\" title=\"What is Arweave? Decentralized Permanent Storage on ...\">SwissBorg Academy</a>)</li>\n<li><strong>IPFS</strong>: decentralized storage</li>\n</ul>\n<hr />\n<h2 id=\"Data-Model\">Data Model</h2>\n<h3 id=\"VaultDescriptor\">VaultDescriptor</h3>\n<pre><code class=\"language-json\">{\n  \"vault_id\": \"urn:edv:zA1...:vault123\",\n  \"controller\": \"did:example:controller\",\n  \"base_url\": \"https://edv.example.com/edvs/vault123\",\n  \"zcap_root\": { \"...\": \"root-capability\" },\n  \"encryption\": { \"content\": \"JWE+ECDH-ES\", \"index_hmac\": \"HMAC-SHA-256\" },\n  \"retention\": { \"kind\": \"ephemeral|durable\", \"expires_at\": \"2025-10-20T00:00:00Z\" },\n  \"backend\": { \"type\": \"s3|arweave|...\", \"hints\": { \"region\": \"us-east-1\" } },\n  \"limits\": { \"max_docs\": 200, \"max_bytes\": 104857600, \"max_doc_bytes\": 10485760 },\n  \"index_schema\": [\"workflow_id\",\"digest\",\"stage\"]\n}\n</code></pre>\n<blockquote>\n<p>EDV documents carry <code>id</code>, <code>sequence</code>, and a JWE payload; searchable <strong>encrypted indexes</strong> are computed client-side. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</p>\n</blockquote>\n<h3 id=\"ContentRef-to-bridge-to-Signing-10\">ContentRef (to bridge to <strong>Signing 1.0</strong>)</h3>\n<pre><code class=\"language-json\">{\n  \"vault_id\": \"urn:edv:...:vault123\",\n  \"doc_id\": \"z19abc...\",\n  \"digest\": \"sha256-BASE64URL(...)\",\n  \"capability\": { \"...\": \"zcap granting read (and optionally write)\" }\n}\n</code></pre>\n<hr />\n<h2 id=\"Protocol-Overview\">Protocol Overview</h2>\n<p><strong>Control plane:</strong> DIDComm v2 messages for negotiation, capability distribution, lifecycle (create/join/seal/tombstone), optional replication.</p>\n<p><strong>Data plane:</strong> EDV HTTP for CRUD/queries; attach ZCAP invocation; encrypt/decrypt at the edge; use JWE general serialization for multi-recipient. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</p>\n<p><strong>Decryption events:</strong> (optional) may be <strong>approval-gated</strong> (N-of-M) or use <strong>true cryptographic threshold decryption</strong> per the in this spec. In both cases, <strong>keys are never published on immutable backends</strong>; key release or partial-decrypt shares occur off-chain via DIDComm.</p>\n<hr />\n<h2 id=\"Messages\">Messages</h2>\n<blockquote>\n<p>All messages are DIDComm v2 <strong><code>type</code></strong> = <code>https://didcomm.org/vaults/1.0/&lt;name&gt;</code> with <strong><code>id</code></strong>, <strong><code>pthid</code></strong> (parent thread = workflow), and optional <strong><code>thid</code></strong> threading. (<a href=\"https://identity.foundation/didcomm-messaging/spec/v2.0/\" title=\"DIDComm Messaging Specification v2.0\">identity.foundation</a>)</p>\n</blockquote>\n<h3 id=\"propose\">propose</h3>\n<p>Propose creating a shared vault.</p>\n<p><strong>Body</strong></p>\n<pre><code class=\"language-json\">{\n  \"purpose\": \"workflow:pdf-signing#42\",\n  \"participants\": [\"did:example:alice\",\"did:example:bob\"],\n  \"constraints\": {\n    \"retention\": \"ephemeral\",\n    \"ttl_seconds\": 86400,\n    \"limits\": { \"max_docs\": 200, \"max_bytes\": 104857600, \"max_doc_bytes\": 10485760 }\n  },\n  \"backend_prefs\": [\"s3\",\"arweave\"],\n  \"index_schema\": [\"workflow_id\",\"digest\",\"stage\"]\n}\n</code></pre>\n<p><strong>Expected reply</strong>: <code>offer</code> (from provisioner/host or a peer offering to host).</p>\n<p><strong>Workflow binding convention</strong>: When a vault is provisioned for a Workflow 1.0 instance, the <code>purpose</code> field SHOULD use the format <code>workflow:&lt;template_id&gt;#&lt;instance_id&gt;</code> (e.g., <code>workflow:student-id-issuance#4b6f...</code>). This enables the vault host and participants to correlate the vault with its owning workflow. The Workflow protocol's <code>start</code> message references the resulting vault via <code>vault_ref</code>. Retention and TTL constraints SHOULD align with the expected workflow duration.</p>\n<hr />\n<h3 id=\"offer\">offer</h3>\n<p>Return a ready vault.</p>\n<p><strong>Body</strong></p>\n<pre><code class=\"language-json\">{\n  \"vault_descriptor\": { \"...\": \"VaultDescriptor\" },\n  \"provisioner\": \"did:example:host\"\n}\n</code></pre>\n<hr />\n<h3 id=\"grant-access\">grant-access</h3>\n<p>Controller delegates a capability to a participant (ZCAP-LD).</p>\n<p><strong>Body</strong></p>\n<pre><code class=\"language-json\">{\n  \"to\": \"did:example:bob\",\n  \"capability\": {\n    \"...\": \"zcap with caveats (verb=read|write|query|replicate, path=/docs/pdf-42-*, expires_at, max_bytes, non_delegable)\"\n  }\n}\n</code></pre>\n<blockquote>\n<p>Capabilities and caveats per ZCAP-LD; delegation chains MUST verify on the server. (<a href=\"https://w3c-ccg.github.io/zcap-spec/\" title=\"Authorization Capabilities for Linked Data v0.3\">w3c-ccg.github.io</a>)</p>\n</blockquote>\n<p><strong>Problem reports</strong>: <code>cap-invalid</code>, <code>cap-expired</code>, <code>cap-unauthorized</code>.</p>\n<hr />\n<h3 id=\"notify-optional\">notify (optional)</h3>\n<p>Lightweight announcement of interesting changes (new doc, sealed, replicated).</p>\n<p><strong>Body</strong></p>\n<pre><code class=\"language-json\">{\n  \"kind\": \"doc-created|doc-updated|sealed|replicated\",\n  \"doc_id\": \"z19abc...\",\n  \"indexed\": { \"workflow_id\": \"HMAC(...)\", \"stage\": \"HMAC(...)\" }\n}\n</code></pre>\n<hr />\n<h3 id=\"replicate-optional\">replicate (optional)</h3>\n<p>Ask a mirror host to replicate ciphertext to another EDV (e.g., S3 → Arweave).</p>\n<p><strong>Body</strong></p>\n<pre><code class=\"language-json\">{\n  \"target\": { \"type\": \"arweave\", \"endpoint\": \"https://edv.mirror/edvs/vaultX\" },\n  \"scope\": { \"prefix\": \"pdf-42-\", \"include_indexes\": true }\n}\n</code></pre>\n<p><strong>Reply</strong>: <code>replicate-receipt</code> with list of digests and target doc IDs.</p>\n<hr />\n<h3 id=\"seal\">seal</h3>\n<p>Rotate capabilities to read-only; freeze the workspace.</p>\n<p><strong>Body</strong></p>\n<pre><code class=\"language-json\">{ \"mode\": \"read-only\" }\n</code></pre>\n<hr />\n<h3 id=\"tombstone\">tombstone</h3>\n<p>Retire a vault.</p>\n<ul>\n<li>For <strong>S3/ordinary EDV</strong>: delete documents + indexes.</li>\n<li>For <strong>Arweave</strong>: perform <strong>cryptographic erasure</strong> (destroy keys) and emit a finalization record listing affected digests—data stays immutable but <strong>unreadable</strong> without keys. (<a href=\"https://academy.swissborg.com/en/learn/arweave\" title=\"What is Arweave? Decentralized Permanent Storage on ...\">SwissBorg Academy</a>)</li>\n</ul>\n<p><strong>Body</strong></p>\n<pre><code class=\"language-json\">{\n  \"strategy\": \"delete|destroy-keys\",\n  \"evidence_doc_id\": \"z1finalnote...\"\n}\n</code></pre>\n<hr />\n<h2 id=\"Flows\">Flows</h2>\n<h3 id=\"Capability-discovery-pre-flight\">Capability discovery (pre-flight)</h3>\n<p>Use <strong>Discover Features 2.0</strong> to check support for <code>vaults/1.0</code>, max sizes, backends, replication ability. (<a href=\"https://didcomm.org/discover-features/2.0/\" title=\"Discover Features 2.0\">didcomm.org</a>)</p>\n<h3 id=\"Two-party-ephemeral-vault-for-PDF-signing-happy-path\">Two-party ephemeral vault for PDF signing (happy path)</h3>\n<ol>\n<li>\n<p><strong>Alice → Bob + Host</strong>: <code>propose</code> (purpose, ttl, limits, backends).</p>\n</li>\n<li>\n<p><strong>Host → Alice</strong>: <code>offer</code> (VaultDescriptor + root zcap).</p>\n</li>\n<li>\n<p><strong>Alice → Bob</strong>: <code>grant-access</code> (write to <code>/docs/pdf-42-*</code>, TTL 24h).</p>\n</li>\n<li>\n<p><strong>Data path (EDV)</strong>:</p>\n<ul>\n<li>Alice <strong>PUT</strong> encrypted PDF (<code>stage=src</code>).</li>\n<li>Signing 1.0 sends <code>ContentRef</code> to Bob; Bob <strong>GET</strong> + verify digest; Bob <strong>PUT</strong> partial signature (<code>stage=partial</code>).</li>\n<li>Aggregator <strong>PUT</strong> final signature + receipt (<code>stage=final</code>).</li>\n</ul>\n</li>\n<li>\n<p><strong>Alice → All</strong>: <code>seal</code> (read-only).</p>\n</li>\n<li>\n<p><strong>(Optional)</strong> <code>replicate</code> to Arweave archive vault; then <code>tombstone</code> the working vault. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</p>\n</li>\n</ol>\n<hr />\n<h2 id=\"State-Machine-controller-view\">State Machine (controller view)</h2>\n<table>\n<thead>\n<tr>\n<th>State</th>\n<th>Event</th>\n<th>Next State</th>\n<th>Notes</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>NEW</code></td>\n<td><code>offer</code> accepted</td>\n<td><code>ACTIVE</code></td>\n<td>VaultDescriptor stored; root zcap held by controller.</td>\n</tr>\n<tr>\n<td><code>ACTIVE</code></td>\n<td><code>grant-access</code></td>\n<td><code>ACTIVE</code></td>\n<td>Participants can perform EDV ops per caveats.</td>\n</tr>\n<tr>\n<td><code>ACTIVE</code></td>\n<td><code>seal</code></td>\n<td><code>SEALED</code></td>\n<td>Rotate caps to read-only; no more writes.</td>\n</tr>\n<tr>\n<td><code>SEALED</code></td>\n<td><code>replicate</code> (opt.)</td>\n<td><code>SEALED</code></td>\n<td>Mirror ciphertext; collect receipts.</td>\n</tr>\n<tr>\n<td><code>SEALED</code></td>\n<td><code>tombstone</code></td>\n<td><code>RETIRED</code></td>\n<td>Delete or cryptographically erase.</td>\n</tr>\n</tbody>\n</table>\n<hr />\n<h2 id=\"EDV-Usage-normative\">EDV Usage (normative)</h2>\n<ul>\n<li><strong>Encryption</strong>: Agents MUST encrypt content as <strong>JWE</strong> before upload; multi-party access MAY use <strong>General JSON Serialization</strong> for multiple recipients. (<a href=\"https://datatracker.ietf.org/doc/html/rfc7516\" title=\"RFC 7516 - JSON Web Encryption (JWE)\">IETF Datatracker</a>)</li>\n<li><strong>Indexes</strong>: Agents SHOULD compute equality-only indexes via HMAC and upload with each document to enable queries with minimal leakage. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</li>\n<li><strong>Concurrency</strong>: Agents MUST respect <strong><code>sequence</code></strong> for optimistic concurrency control and retry on conflict. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</li>\n</ul>\n<hr />\n<h2 id=\"Authorization-normative\">Authorization (normative)</h2>\n<ul>\n<li>Capabilities MUST be expressed as <strong>ZCAP-LD</strong> documents, bound to vault endpoints and <strong>caveated</strong> by:\n<code>expires_at</code>, verbs (<code>read|write|query|replicate</code>), path/prefix, and size/byte limits; optionally non-delegable. (<a href=\"https://w3c-ccg.github.io/zcap-spec/\" title=\"Authorization Capabilities for Linked Data v0.3\">w3c-ccg.github.io</a>)</li>\n<li>Hosts MUST verify capability chains and caveats for each EDV request (out of band from DIDComm messages).</li>\n</ul>\n<hr />\n<h2 id=\"Threshold-Decryption-Optional\"><strong>Threshold Decryption (Optional)</strong></h2>\n<p><strong>Purpose:</strong> Make decryption happen <strong>only after N-of-M approvals</strong>. Two modes are supported:</p>\n<ol>\n<li><strong>Approval-gated release (policy-level)</strong> — N approvals collected via DIDComm / Signing 1.0; coordinator releases the CEK as a <strong>sealed secret</strong> (HPKE) to the authorized device.</li>\n<li><strong>True cryptographic threshold decryption</strong> — The CEK (or data) is encrypted under a <strong>threshold public key</strong>; any N holders produce <strong>partial decrypt shares</strong> that combine to recover the CEK (or plaintext). No single party can decrypt alone.</li>\n</ol>\n<blockquote>\n<p>This covers <strong>(2)</strong>. Mode (1) already works without changes (use Signing 1.0 threshold collection + <code>sealed-secret@1</code> delivery).</p>\n</blockquote>\n<h3 id=\"Threshold-KEM-Header-attached-to-the-object-descriptor\">Threshold KEM Header (attached to the object descriptor)</h3>\n<pre><code class=\"language-json\">{\n  \"threshold_kem\": {\n    \"scheme\": \"threshold-kem@1\",\n    \"t\": 2,\n    \"n\": 3,\n    \"pub\": \"base64url(public_key_bytes)\",\n    \"params\": {\n      \"kem\": \"ECIES-X25519\",\n      \"kdf\": \"HKDF-SHA256\",\n      \"aead\": \"AES-256-GCM\"\n    },\n    \"cipher_cek\": \"base64url(...)\",   // CEK encrypted under the threshold public key\n    \"aad\": {\n      \"session_id\": \"sess_...\",\n      \"object_id\": \"so_...\",\n      \"kem_params_hash\": \"sha-256:...\",\n      \"policy_hash\": \"sha-256:...\"    // optional but recommended\n    }\n  }\n}\n</code></pre>\n<blockquote>\n<p>If you encrypt the <strong>data directly</strong> under the threshold key, use <code>cipher_data</code> instead of <code>cipher_cek</code>. The rest of the flow is identical; the aggregator yields plaintext rather than CEK.</p>\n</blockquote>\n<h3 id=\"Message-Reuse\">Message Reuse</h3>\n<p>We reuse the existing DIDComm messages; we only define <strong>what goes in them</strong>.</p>\n<h4 id=\"request-decrypt-session-using-threshold\"><code>request</code> (decrypt session using threshold)</h4>\n<p>Use <code>vaults/1.0</code> to set up access + transport, and <strong>Signing 1.0</strong> (recommended) to orchestrate the threshold session:</p>\n<pre><code class=\"language-json\">{\n  \"type\": \"https://didcomm.org/signing/1.0/request-signing\",\n  \"body\": {\n    \"session\": { \"session_id\": \"sess_...\", \"mode\": { \"type\": \"threshold\" } },\n    \"object\": { \"...\": \"Signable (or Decryptable) Object + threshold_kem header\" },\n    \"suite\": { \"suite\": \"threshold-kem@1\" },\n    \"constraints\": {\n      \"not_before\": \"...\", \"expires_time\": \"...\",\n      \"intended_audience\": [\"did:ex:device\"], \"use_limit\": 1\n    }\n  }\n}\n</code></pre>\n<h4 id=\"partial-signature-carries-partial-decrypt-share\"><code>partial-signature</code> (carries <strong>partial decrypt share</strong>)</h4>\n<pre><code class=\"language-json\">{\n  \"type\": \"https://didcomm.org/signing/1.0/partial-signature\",\n  \"body\": {\n    \"session_id\": \"sess_...\",\n    \"object_id\": \"so_...\",\n    \"signer\": \"did:ex:holder2\",\n    \"suite\": \"threshold-kem@1\",\n    \"kind\": \"partial-decrypt\",\n    \"data\": {\n      \"share\": \"base64url(partial_decrypt_share)\",\n      \"aad_hash\": \"sha-256:...\"  // MUST equal hash(serialize(threshold_kem.aad))\n    }\n  }\n}\n</code></pre>\n<h4 id=\"combine-threshold-met\"><code>combine</code> (threshold met)</h4>\n<pre><code class=\"language-json\">{\n  \"type\": \"https://didcomm.org/signing/1.0/combine\",\n  \"body\": {\n    \"session_id\": \"sess_...\",\n    \"status\": \"threshold_met\",\n    \"aggregation_result\": {\n      \"type\": \"threshold-decrypt@1\",\n      \"n\": 2, \"m\": 3\n    }\n  }\n}\n</code></pre>\n<h4 id=\"provide-artifacts-deliver-CEK-or-plaintext\"><code>provide-artifacts</code> (deliver CEK or plaintext)</h4>\n<p><strong>CEK delivery (recommended)</strong> sealed to the device using <strong>HPKE</strong> (RFC 9180) and bound to a <strong>one-use authorization token</strong> (counter, expiry, device):</p>\n<pre><code class=\"language-json\">{\n  \"type\": \"https://didcomm.org/signing/1.0/provide-artifacts\",\n  \"body\": {\n    \"session_id\": \"sess_...\",\n    \"artifacts\": [\n      {\n        \"type\": \"sealed-secret@1\",\n        \"suite\": \"envelope-hpke@1\",\n        \"aad\": { \"ticket_digest\": \"sha-256:...\" },\n        \"ciphertext\": \"base64url(HPKE(CEK))\",\n        \"enc\": { \"kem\": \"X25519\", \"kdf\": \"HKDF-SHA256\", \"aead\": \"AES-256-GCM\", \"ek_pub\": \"...\" }\n      }\n    ],\n    \"token\": {\n      \"token\": {\n        \"typ\": \"signing-ticket\",\n        \"session_id\": \"sess_...\",\n        \"scope\": \"decrypt\",\n        \"device\": \"did:ex:device#k1\",\n        \"ctr\": 42,\n        \"exp\": \"2025-10-19T16:02:00Z\",\n        \"cap\": 1\n      },\n      \"sig\": { \"suite\":\"jws-ed25519@1\",\"kid\":\"did:ex:coord#k1\",\"value\":\"...\" }\n    }\n  }\n}\n</code></pre>\n<p><em>(Data-direct variant: stream plaintext or attach a reference after the same token checks.)</em></p>\n<h3 id=\"Registry-Additions-Threshold-Decrypt\">Registry Additions (Threshold Decrypt)</h3>\n<p><strong>Suites</strong></p>\n<table>\n<thead>\n<tr>\n<th>Registry Key</th>\n<th>Description</th>\n<th>Inputs</th>\n<th>Outputs</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>threshold-kem@1</code></td>\n<td>Threshold public-key encryption for CEK (ElGamal/ECIES/HPKE-style).</td>\n<td>KEM header (<code>cipher_cek</code> or <code>cipher_data</code>), holder’s private share, <code>aad_hash</code></td>\n<td><code>partial_decrypt_share</code></td>\n</tr>\n</tbody>\n</table>\n<blockquote>\n<p>Implementations MUST commit to <code>aad_hash = sha256(serialize(aad))</code> to prevent cross-session replay. The concrete math (curve/KEM) is pluggable; publish parameters via <code>kem_params_hash</code>.</p>\n</blockquote>\n<p><strong>Aggregators</strong></p>\n<table>\n<thead>\n<tr>\n<th>Registry Key</th>\n<th>Description</th>\n<th>Input</th>\n<th>Output</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>threshold-decrypt@1</code></td>\n<td>Combines <strong>N</strong> partial decrypt shares into CEK (or plaintext).</td>\n<td>N <code>partial-signature(kind=partial-decrypt)</code> with consistent <code>aad_hash</code> + common KEM header</td>\n<td>CEK (then <code>sealed-secret@1</code>) or plaintext</td>\n</tr>\n</tbody>\n</table>\n<p>** Problem Reports (delta)**</p>\n<ul>\n<li><code>share-invalid</code> — malformed or fails combine/validation</li>\n<li><code>share-duplicate</code> — duplicate signer/index</li>\n<li><code>threshold-not-reached</code> — insufficient valid shares</li>\n<li><code>aggregation-failed</code> — combine failed integrity checks</li>\n</ul>\n<h3 id=\"Security-Requirements-delta\">Security Requirements (delta)</h3>\n<ul>\n<li><strong>No single holder can decrypt alone</strong>; a single share reveals nothing.</li>\n<li><strong>Binding</strong>: Shares MUST commit to <code>aad_hash</code> (session/object/policy binding).</li>\n<li><strong>Replay-proof release</strong>: CEK/plaintext delivery MUST be bound to an authorization token (device DID, monotonic <code>ctr</code>, expiry).</li>\n<li><strong>Verification</strong>: Aggregator MUST verify AEAD tag (or CEK checksum) after combine; reject otherwise.</li>\n<li><strong>DKG</strong>: Secure DKG ceremony is out-of-scope for messages; deployments MUST pin <code>kem_params_hash</code>.</li>\n</ul>\n<hr />\n<h2 id=\"Security-Considerations\">Security Considerations</h2>\n<ul>\n<li><strong>Client-side encryption</strong>: Providers see only ciphertext and HMAC'd indexes; plaintext lives only at the edge. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</li>\n<li><strong>Transport</strong>: DIDComm <strong>authcrypt</strong> recommended for authenticity + confidentiality of control messages. (<a href=\"https://identity.foundation/didcomm-messaging/spec/v2.0/\" title=\"DIDComm Messaging Specification v2.0\">identity.foundation</a>)</li>\n<li><strong>Capabilities</strong>: Prefer short TTLs, tight path scoping, byte limits, and non-delegability where appropriate. Rotate caps at <strong>seal</strong>. (<a href=\"https://w3c-ccg.github.io/zcap-spec/\" title=\"Authorization Capabilities for Linked Data v0.3\">w3c-ccg.github.io</a>)</li>\n<li><strong>Leakage</strong>: Encrypted equality indexes leak <strong>existence and equality</strong>; avoid sensitive tags; no range/prefix queries. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</li>\n<li><strong>Immutability backends</strong>: For Arweave, treat \"delete\" as <strong>cryptographic erasure</strong> (destroy keys) and publish a finalization record listing affected digests. (<a href=\"https://academy.swissborg.com/en/learn/arweave\" title=\"What is Arweave? Decentralized Permanent Storage on ...\">SwissBorg Academy</a>)</li>\n<li><strong>Threshold Decrypt</strong> : Never publish keys on immutable backends. Shares, tokens, and sealed secrets travel only via DIDComm.</li>\n<li><strong>Workflow-scoped vaults</strong>: When a vault is bound to a Workflow 1.0 instance, the vault TTL SHOULD be synchronized with the workflow's expected duration. If the workflow completes, the Coordinator or Processor SHOULD trigger <code>seal</code>; if canceled, <code>tombstone</code>. Key material used for vault encryption SHOULD be wiped promptly after the vault is sealed or tombstoned.</li>\n<li><strong>Transient data</strong>: Workflow <code>$transient</code> fields (single-cycle ephemeral values) MUST NOT be stored in vaults. They exist only in volatile memory during a single workflow advance cycle.</li>\n<li><strong>Guard resolution timing</strong>: When a Workflow Processor resolves vault-backed <code>$ref</code> pointers for guard evaluation, the decrypted plaintext MUST be held only in memory for the duration of the evaluation and MUST NOT be cached, logged, or persisted.</li>\n</ul>\n<hr />\n<h2 id=\"Privacy-Considerations\">Privacy Considerations</h2>\n<ul>\n<li>Minimize metadata in DIDComm headers and EDV indexes.</li>\n<li>Consider <strong>group encryption</strong> via JWE general serialization to avoid separate per-recipient blobs. (<a href=\"https://datatracker.ietf.org/doc/html/rfc7516\" title=\"RFC 7516 - JSON Web Encryption (JWE)\">IETF Datatracker</a>)</li>\n<li><strong>Workflow field indexing</strong>: When vault documents store workflow context fields, avoid indexing field names or values that could leak the workflow's purpose or the subject's identity. Use opaque identifiers (e.g., <code>doc_id</code> based on random values, not field names) and limit encrypted indexes to structural keys like <code>workflow_id</code> and <code>stage</code>. Do not index <code>secret</code>-level field names or values.</li>\n<li><strong>Role-grouped documents</strong>: Grouping sensitive workflow fields into EDV documents by role-access pattern (rather than one document per field) reduces the number of EDV queries observable by the vault host, minimizing metadata leakage about field-level access patterns.</li>\n</ul>\n<hr />\n<h2 id=\"Interop-Notes\">Interop Notes</h2>\n<ul>\n<li>\n<p><strong>Discover Features 2.0</strong> SHOULD advertise: supported backends (<code>s3</code>, <code>arweave</code>, …), max object size, index support, replication availability, and whether <strong><code>threshold-decrypt</code></strong> is supported. (<a href=\"https://didcomm.org/discover-features/2.0/\" title=\"Discover Features 2.0\">didcomm.org</a>)</p>\n</li>\n<li>\n<p><strong>Composition with Signing 1.0</strong>:</p>\n<ul>\n<li>Use Signing 1.0 threshold sessions to collect <strong>approvals</strong> and carry <strong>partial-decrypt shares</strong>.</li>\n<li>Use Signing 1.0 <strong>sealed-secret</strong> profile (HPKE envelopes bound to tokens) for key delivery.</li>\n</ul>\n</li>\n<li>\n<p><strong>Composition with Workflow 1.0</strong>:</p>\n<ul>\n<li>Workflow templates declare a <code>sensitivity</code> map that classifies context fields. Fields with <code>storage: \"vault\"</code> are stored in a Vaults 1.0 EDV and referenced via <code>$ref</code> pointers in workflow messages.</li>\n<li>The Coordinator provisions the vault before sending Workflow <code>start</code>. The <code>propose.purpose</code> field uses the convention <code>workflow:&lt;template_id&gt;#&lt;instance_id&gt;</code>.</li>\n<li>Vault lifecycle is bound to workflow lifecycle: <code>complete</code> triggers <code>seal</code>, <code>cancel</code> triggers <code>tombstone</code>.</li>\n<li>Agents that support both protocols SHOULD advertise <code>vault-context</code> in their Discover Features 2.0 capabilities.</li>\n</ul>\n</li>\n</ul>\n<hr />\n<h2 id=\"Worked-Example\">Worked Example</h2>\n<p><strong>Create &amp; grant</strong></p>\n<pre><code class=\"language-json\">// Alice → Host\n{ \"type\":\"https://didcomm.org/vaults/1.0/propose\", \"body\": { \"purpose\":\"workflow:pdf-signing#42\", \"participants\":[\"did:ex:bob\"], \"constraints\":{\"ttl_seconds\":86400}, \"backend_prefs\":[\"s3\"], \"index_schema\":[\"workflow_id\",\"digest\",\"stage\"] } }\n\n// Host → Alice\n{ \"type\":\"https://didcomm.org/vaults/1.0/offer\", \"body\": { \"vault_descriptor\": { \"...\": \"VaultDescriptor\" } } }\n\n// Alice → Bob\n{ \"type\":\"https://didcomm.org/vaults/1.0/grant-access\", \"body\": { \"to\":\"did:ex:bob\", \"capability\": { \"...\": \"zcap w/ path=/docs/pdf-42-*, verb=read|write, expires_at=...\" } } }\n</code></pre>\n<p><strong>Use with Signing 1.0</strong></p>\n<pre><code class=\"language-json\">// Sign-request (separate protocol) carries a ContentRef\n{ \"type\":\"https://didcomm.org/signing/1.0/request\", \"body\": { \"content_ref\": { \"vault_id\":\"urn:edv:...:vault123\",\"doc_id\":\"pdf-42-src\",\"digest\":\"sha256-...\",\"capability\":{ \"...\": \"read-cap\" } }, \"suite\":\"PAdES\", \"policy\":{ \"...\": \"digest pinning etc.\" } } }\n</code></pre>\n<p><strong>Threshold Decrypt (CEK-wrap) — 2-of-3</strong></p>\n<pre><code class=\"language-json\">// Object descriptor carries threshold_kem header with cipher_cek\n// Requester starts a Signing 1.0 threshold session:\n{ \"type\":\"https://didcomm.org/signing/1.0/request-signing\",\n  \"body\": { \"session\": { \"session_id\": \"sess_dec1\", \"mode\": {\"type\":\"threshold\"} },\n            \"object\": { \"...\": \"includes threshold_kem\" },\n            \"suite\": { \"suite\":\"threshold-kem@1\" },\n            \"constraints\": { \"intended_audience\": [\"did:ex:alice#device\"], \"use_limit\": 1, \"expires_time\": \"...\" } } }\n\n// Two holders return partial-decrypt shares:\n{ \"type\":\"https://didcomm.org/signing/1.0/partial-signature\",\n  \"body\":{ \"session_id\":\"sess_dec1\",\"object_id\":\"so_xyz\",\"signer\":\"did:ex:holder1\",\n           \"suite\":\"threshold-kem@1\",\"kind\":\"partial-decrypt\",\"data\":{\"share\":\"...\",\"aad_hash\":\"sha-256:...\"}} }\n\n// Coordinator combines shares → CEK, then delivers as sealed secret + token:\n{ \"type\":\"https://didcomm.org/signing/1.0/provide-artifacts\",\n  \"body\": { \"session_id\":\"sess_dec1\",\n            \"artifacts\":[{\"type\":\"sealed-secret@1\",\"suite\":\"envelope-hpke@1\",\"aad\":{\"ticket_digest\":\"sha-256:...\"},\"ciphertext\":\"...\",\"enc\":{\"kem\":\"X25519\",\"kdf\":\"HKDF-SHA256\",\"aead\":\"AES-256-GCM\",\"ek_pub\":\"...\"}} ],\n            \"token\":{ \"...\": \"device-bound, ctr, exp, cap=1\" } } }\n</code></pre>\n<hr />\n<h2 id=\"Implementation-Hints\">Implementation Hints</h2>\n<ul>\n<li>\n<p>Use or adapt an existing <strong>EDV server</strong> (e.g., TrustBloc EDV) and plug different backends under it. (<a href=\"https://github.com/trustbloc/edv\" title=\"GitHub - trustbloc/edv: Encrypted data vault implementation ...\">GitHub</a>)</p>\n</li>\n<li>\n<p>Client library should expose:</p>\n<ul>\n<li><strong>EDV client</strong> (encrypt/JWE, HMAC indexes, sequence handling).</li>\n<li><strong>ZCAP</strong> issuance/delegation helpers.</li>\n<li>DIDComm handlers for <code>propose</code>, <code>offer</code>, <code>grant-access</code>, <code>seal</code>, <code>tombstone</code>, <code>replicate</code>.</li>\n<li><strong>Threshold holders</strong>: API to compute <code>partial_decrypt_share</code> given the KEM header + private share.</li>\n<li><strong>Aggregator</strong>: <code>threshold-decrypt@1</code> to combine N shares, verify integrity (AEAD/CEK checksum), and emit sealed-secret + token.</li>\n</ul>\n</li>\n<li>\n<p>For multi-party read, prefer <strong>JWE general serialization</strong> (one ciphertext, many recipients) where policy allows. For immutable archives (e.g., Arweave), keep <strong>keys off-chain</strong> and gate key release with this extension.</p>\n</li>\n</ul>\n<hr />\n<h2 id=\"Security--Compliance-Checklist\">Security &amp; Compliance Checklist</h2>\n<ul class=\"contains-task-list\">\n<li class=\"task-list-item\"><input type=\"checkbox\" disabled /> DIDComm messages <strong>authcrypt</strong>ed, with correct <code>thid/pthid</code>. (<a href=\"https://identity.foundation/didcomm-messaging/spec/v2.0/\" title=\"DIDComm Messaging Specification v2.0\">identity.foundation</a>)</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" disabled /> Capabilities scoped to <strong>verb + path + TTL + size</strong>; non-delegable when needed. (<a href=\"https://w3c-ccg.github.io/zcap-spec/\" title=\"Authorization Capabilities for Linked Data v0.3\">w3c-ccg.github.io</a>)</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" disabled /> EDV <strong>sequence</strong> checked on updates; retry on conflict. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" disabled /> Equality indexes only; avoid sensitive attribute leakage. (<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" disabled /> Arweave \"deletion\" via <strong>key destruction</strong> documented with finalization record. (<a href=\"https://academy.swissborg.com/en/learn/arweave\" title=\"What is Arweave? Decentralized Permanent Storage on ...\">SwissBorg Academy</a>)</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" disabled /> Keys never published on immutable backends; key release/partials only via DIDComm.</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" disabled /> Authorization token enforced (device, counter, expiry) for CEK/plaintext delivery.</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" disabled /> <code>aad_hash</code> binding validated for all partial-decrypt shares.</li>\n</ul>\n<hr />\n<h2 id=\"Change-Log\">Change Log</h2>\n<ul>\n<li><strong>v1.0-draft</strong> — Initial publication</li>\n<li><strong>v1.0-draft+wf</strong> — Added workflow binding conventions (<code>purpose</code> format), workflow-specific security/privacy guidance, and Workflow 1.0 composition notes.</li>\n</ul>\n<hr />\n<h3 id=\"References\">References</h3>\n<p>EDV v0.1 spec; DIDComm v2; Discover Features 2.0; Message Pickup 3.0; JWE (RFC 7516); ZCAP-LD; DIDComm v2 announcement; Arweave permanence &amp; crypto-erasure background; HPKE (RFC 9180).\n(<a href=\"https://identity.foundation/edv-spec/\" title=\"Encrypted Data Vaults v0.1\">identity.foundation</a>)</p>\n<hr />"}},
    "staticQueryHashes": ["3000541721","3766577012"],
    "matchPath": "vaults/1.0/*"}