Status: DRAFT of 1.0 (~1.0) —
published for review 2026-09-22 (the design was
approved 2026-08-12). The Custody Extension is an optional
extension to ZDDC Core: the on-disk formats that let anyone
verify, offline and decades later, who issued a
record, what exactly they issued, and what the other side
acknowledged receiving. It specifies a signed
transmittal manifest, a DSSE envelope profile, key files and
trust anchors, the link statements parties exchange, and the
conformance contract an implementation is tested against.
~1.0 uses ZDDC's own draft prefix: this is a
draft of version 1.0, and its text and vectors may still
change. Finalising drops the ~; the same address
then holds 1.0, which is immutable — corrections after that
are errata, never edits in place.
The documents themselves call this layer "custody v1"; that is this extension, version 1.0.
What conformance means here
If an implementation passes the vectors, it
interoperates. That is the entire claim. There is no
certification and no registry of approved implementations. An
implementation declares the levels it meets (conformance.md §1) —
they compose, and nothing requires all four:
- V — Verifier. Checks envelopes, anchors and statements; returns a tri-state verdict with its as-of date.
- P — Producer. Emits canonical payloads, manifests and key files.
- H — Hasher. The chunked Merkle construction and inclusion proofs alone.
- L — Ledger. Monotonic acceptance, ledger placement, currency derivation.
Everything specified here is producible and verifiable entirely client-side, with the Web Crypto API, over self-contained artifacts, with no network. That is a design constraint, not a convenience, and several requirements exist only to protect it.
Documents
| File | Contents |
|---|---|
manifest.md |
The signed transmittal manifest: fields, chunked Merkle file hashing (zddc-merkle-v1), path encoding, canonicalization, limits, extensibility |
envelope.md |
The DSSE envelope profile: payloadType registry, PAE, Ed25519, key identification, the numbered verification procedure |
keys.md |
Key formats: instance/link keys, per-DC operational keys + key-certification, the two-key anchor in the registry row, rotation/expiry posture; the server record-signing key, the personal signing key and the WebAuthn credential |
statements.md |
The seven link statement types + the two local types,
record-revision and
content-attestation: payload schemas,
signing/countersigning, on-disk placement, the receipt and
review-response flows |
conformance.md |
The runner contract, the conformance levels, and what v1 deliberately does not do — each deferral with its hook named |
vectors/ |
The executable half: README.md (conformance
contract, reason classes, requirement traceability),
gen.js (deterministic, zero-dependency
generator), and 233 fixtures under
merkle/ canonical/ envelope/ statements/ currency/
(plus keys.json and
contexts.json) |
corpora/ |
Third-party Ed25519 known-answer corpora, vendored
verbatim with provenance, licence and pinned SHA-256:
Project Wycheproof (151 cases) and
ed25519-speccheck (12). Required by [M-R12] for
the [M-E10] axes no ZDDC vector can express |
decisions.md |
Non-normative appendix. The rationale log: every wire-level choice the design memo did not pin, and how each was reviewed and closed. Read it to understand why, never to learn what |
The whole set — these sources, the vectors and the corpora
— is published as one archive, zddc-custody-1.0.tar.gz,
with its SHA-256 beside it.
Reading order and requirement ids
manifest.md → envelope.md → keys.md → statements.md → conformance.md.
Normative requirements carry stable ids: [M-C*]
canonical form, [M-M*] manifest/Merkle,
[M-E*] envelope, [M-K*] keys,
[M-S*] statements, [M-R*] the runner
contract. The key words MUST, SHOULD and MAY are used as in
RFC 2119.
vectors/README.md
§5 maps every id to the vector(s) exercising it or to an
explicit not-vector-testable note — 122 of
157 are vector-covered; the other 35
are listed with why not and what pins them instead. Those
numbers are derived from the files by the reference
implementation's test suite, which fails if a sentence and the
tree disagree.
What is normative
The normative text is manifest.md, envelope.md, keys.md, statements.md, conformance.md and vectors/README.md,
together with the vectors themselves. Where prose and a vector
disagree, that is a defect in the specification — report it;
do not pick one locally.
These specs were transcribed from ZDDC's internal design
memo, docs/design/federation-and-custody.md.
Citations of the form (memo §6.11) point into
it; the addenda cite further design notes under
docs/design/ the same way
(record-signing.md,
personal-signing.md, webauthn.md,
transmittal-producer.md). None of these is
published. They record where a rule came from; they are not
part of the rule, and nothing here requires reading them.
These documents are the contract, not the
memo ([M-R11]).
Also non-normative: decisions.md; notes
marked Repo note, RESOLVED
or CLOSED, which record how a question was
settled; and references to paths in the reference
implementation's source tree
(zddc/internal/custody/,
shared/custody*.js, peer/key.go and
the like), which point at one implementation's evidence, not
at requirements.
Reference implementations
Two implementations were built independently from
this prose, in different worlds:
zddc-server in Go (the inbound door, the ledger
reader, and since the addenda a producer of
record-revision), and the .html
tools in the browser, using Web Crypto from
file:// (the producer and the offline verifier).
Both pass the vectors. A behaviour diff then drove both
through 4 424 mutation probes over the scope approved on
2026-08-12, with zero disagreements at that
pass; every divergence found on the way is closed in decisions.md and
carries a vector that would have caught it.
That result is evidence, not proof. One further divergence
(decisions.md D-S13,
in [M-S8]) surfaced on 2026-08-24, when the first real
mid-thread statement was produced — no vector distinguished
the two readings. It is closed, with vectors on both sides.
The later addenda are carried by their own vectors. In the
reference implementation's repository the suite runs as
tests/custody-vectors.spec.js.
Both are reference implementations of these documents, never the definition of them. A third implementation that passes the vectors interoperates with both.
Changes since approval
- Published as a draft for review (2026-09-22). [M-R11] inverted: these specs, not the design memo, are authoritative — an unpublished internal document cannot bind a third-party implementation. No other requirement, no vector and no wire byte changed. This preamble replaced the working-tree status page.
- Addenda (2026-08-16/17). Additive;
nothing approved earlier changed. The local
record-revisionstatement (statements.md§11,keys.md§8, the server record-signing key); thecontent-attestationstatement and the in-band carriage rule [M-S66] (statements.md§12, §8); the personal signing key (keys.md§9); and the WebAuthn credential and assertion profile (keys.md§10,envelope.md§8). Each section carries its own dated note. - The cross-implementation passes (from
2026-08-13).
decisions.mdD-23–D-25 and "Where the two implementations can still differ": two substantive decisions, [M-C10] and [M-C11] written, [M-E23] extended, two unsatisfiable vectors repaired. None changed a byte on the wire.
Reporting defects
Specification defects, vector disputes and extension proposals go to the issue tracker: https://codeberg.org/VARASYS/ZDDC. If your implementation disagrees with a vector, the vector may be what is wrong — two were, and were repaired. Report it rather than working around it locally; a private workaround is how two implementations quietly stop interoperating.