Skip to content

ADR 0242: Durable receipts use a versioned DSSE-compatible envelope

Claim-projection amendment (2026-08-03; ADR 0253): The payload now keeps the structured proof claim and human presentation in separate schemas. The canonical writer projects both from the runtime receipt, so runtime telemetry cannot enter the durable contract through schema reuse. This supersedes this record's shared-Receipt payload layout and final consequence; the DSSE boundary, identifiers, subject binding, and signing decisions stand.

Launch-reader amendment (2026-09-03): only the split v1 envelope with explicit subject, presentation, and signatures is readable. Bare private-development notes were retired before v1. Readers retain the deliberate future-signing tolerance for standard or Base64url payloads with or without padding; writers emit padded standard Base64 and the required empty unsigned signature array.

Status: accepted; amends the note-body decision of ADR 0215, applies the contract discipline of ADR 0208 to a channel it deliberately excluded, and leaves the receipt derivation and relay contract of ADR 0114 / ADR 0188 unchanged. The frozen identifiers — the schema id, the payload type, and the payload's proof field — carry the artifact's successor name from ADR 0245, landed ahead of the product-wide rename sweep so the durable bytes never need a second identity; this record otherwise keeps the receipt vocabulary current at its writing.

Context

§

"Receipt" names three artifacts with different lifetimes. The gate receipt marker and standard-measurement receipt are worktree-local caches. The landed receipt note survives worktree cleanup, travels between clones, and may be read by a different discern release. It is a wire format, but it began as the bare canonical JSON of the strict 8-field runtime Receipt: no format identity, no full object id, and no signing boundary.

The first versioned design wrapped that receipt with format, subject, and optional issuer / signature fields. That reserved storage without defining a signature protocol. A signature acts on bytes, while equivalent JSON objects may differ in key order, whitespace, or escaping. Omitting the signature value before signing would also need a canonical projection. A third party could locate a signature in the wrapper but could not reconstruct the message it endorsed.

The append-only compatibility ratchet arms at the first release tag. Signing implementation, key management, and the post-launch authorship chain remain outside launch scope, but the byte boundary cannot wait without turning the first signed receipt into a format migration.

Decision

§

Acceptance records the landed receipt as a DSSE-compatible JSON envelope whose payload bytes are preserved in the note. The note body is one compact JSON object plus a trailing newline:

  • payloadType is https://discern.sh/schema/v1/discern-proof-note.schema.json#/$defs/DiscernProofNotePayload. It identifies the UTF-8 JSON payload contract and is the in-band compatibility-major identity.
  • payload is Base64 of the serialized payload bytes. Current writers use standard padded Base64; readers accept the standard and URL-safe alphabets and also tolerate omitted padding. A verifier uses the decoded bytes and never reconstructs them from parsed JSON.
  • signatures is an array of DSSE signature entries. Each entry has the required Base64 sig and optional keyid. The standard DSSE JSON envelope requires at least one signature. Discern v1.0.0 writes an empty array as its unsigned extension. Adding one or more real entries produces the standard signed form without moving the payload.

The payload contains subject, proof (the structured gate record), and the optional issuer and brief fields. subject.commit is the full object id of the validated landed commit. The writer checks it against the record's abbreviated display head; the reader requires it to equal the commit carrying the Git note.

The signature input follows DSSE protocol v1.0.2: PAE(UTF8(payloadType), decoded payload bytes). The payload type and every payload field are therefore covered, including unknown future fields, the commit subject, an issuer assertion, and a brief reference. No other outer-envelope field enters the signature input, so future authenticated claims belong inside the payload. No JSON canonicalization is involved.

issuer is an assertion inside the signed payload. A verified signature prevents alteration of that assertion and proves possession of a verification key. It does not prove that the key belongs to the named person, agent, runner, or organization. The DSSE keyid field remains an unauthenticated key-selection hint. A future signing profile and trust policy define algorithm, signature encoding, key resolution, identity binding, rotation, revocation, and acceptance rules.

The runtime writer is strict and the durable reader is tolerant. Unknown fields pass at every envelope and payload level within v1. An unrecognized payloadType yields an explicit unsupported reading. A bare 8-field receipt with no payloadType remains a legacy unsigned note. The parser currently reads signatures as opaque data and performs no cryptographic verification; valid means the payload is readable and bound to its Git commit.

The envelope and decoded payload definition publish together at schema/discern-proof-note.schema.json. The payload type points into that publication, and code generation derives the artifact from the Zod schemas. Breaking changes move to a new major path and payload type.

Explicitly not decided here: no signing command, verification verb, signing profile, trust configuration, key management, signature threshold, ledger, in-toto statement, or authorship-chain behavior. The brief field only reserves the receipt's signed reference to that future artifact.

Consequences

§
  • A signer can arrive after v1.0.0 by adding standard DSSE signature entries over the bytes already carried by every current envelope. That turns discern's unsigned extension into the standard signed form; the receipt claim does not move.
  • A third party can recover the signature input and identify the key that verified it. Calling that key a trusted human or service requires the later signing profile and the trust policy used by the verifier.
  • Unsigned notes are less pleasant to inspect directly because the receipt payload is Base64. discern status remains the human reader, and local gate output does not change.
  • Unknown compatible payload fields remain authenticated because the envelope preserves their original bytes, even when an older reader ignores them after parsing.
  • Legacy bare notes remain readable. The unlanded wrapper draft has no compatibility claim and is replaced before v1.
  • The result schema still carries the named DiscernProof definition. The note schema embeds the same generated definition, plus the payload contract and DSSE envelope.

Alternatives considered

§
  • Keep the wrapper with optional scheme / value. Rejected: it reserves field names while leaving the signed projection and byte encoding undefined. A later DSSE adoption would require a new format major.
  • Sign canonical JSON with RFC 8785. Rejected: discern would own a canonicalization dependency and a rule for removing or retaining signature metadata. DSSE preserves and signs the payload bytes directly.
  • Adopt an in-toto Statement as the payload now. Rejected: the receipt already has a published subject-and-claim contract, while the wider human-and-agent provenance model remains post-launch work. DSSE standardizes the cryptographic boundary without pre-committing that model.
  • Keep both format and payloadType. Rejected: they would duplicate the compatibility identity, and only payloadType is authenticated by DSSE.
choose openEsc close