| Internet-Draft | Stage Receipts | September 2026 |
| Saha | Expires 12 March 2027 | [Page] |
A staged pipeline -- a document-ingestion flow, a retrieval-augmented generation chain, an agent workflow, a benchmark -- produces results that are hard to reproduce, hard to diff between two runs, and hard to localize when they go wrong. This document defines the stage receipt: a small, canonically serialized JSON record that each stage of such a pipeline emits, describing exactly what went in, what came out, under which pinned instrument, with which outcome, and linked by digest to the receipt before it. A chain of stage receipts lets a developer reproduce a run, diff two runs to the first stage that differs, and localize a fault to the stage where it entered; the same chain lets an independent party, later and offline, establish what the records assert without trusting whoever produced them. The document specifies the record, its canonical form, the chain manifest, the coverage and emission declarations that make a chain honest about its own edges, the anchoring declaration that separates consistency from originality, and the behaviour required of a conforming verifier. Golden conformance vectors -- records that must be accepted and records that must be refused, each with its reason -- are part of the specification.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 12 March 2027.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
Most pipelines that transform data through several stages keep logs. Logs are prose about what happened; they are written for the operator, in the operator's vocabulary, and they are only as complete as the operator remembered to make them. When a run produces a wrong answer, the questions that follow -- which stage changed its output since the last good run; can this run be repeated; where exactly did the bad data enter; is what is deployed the same thing that was approved -- are answered, if at all, by reading logs and guessing.¶
A stage receipt is the alternative this document proposes: a record each stage emits, in a fixed shape, containing digests of the bytes that entered and left it, the identity and pinned version of the component that ran, the constants its assertions depend on, its outcome, and the digest of the receipt that preceded it. The record is serialized in a canonical form so that its digest is the same on every platform, and it never contains its own digest, so that the chain rather than the record is the proof of its final state.¶
The immediate beneficiary is the developer. Given two chains, the first receipt whose input digests, instrument version, configuration digest or output digest differ is the stage where two runs diverged. Given one chain and the pipeline's code, the stages that declare themselves re-derivable can be run again from their recorded inputs and their outputs compared byte for byte. Given a chain with a bad result, the first receipt whose outcome is not "ok", or whose recorded output does not match what the next stage consumed, is where the fault entered. None of these operations requires trusting the producer, a network, a key, or a service.¶
The same properties serve a second reader: someone who was not present, holds only the records, and needs to establish what they assert. This document is careful about what such a reader can and cannot establish. A chain whose links all verify is consistent with itself; it is not thereby shown to be the chain that was made, because an operator holding every copy can rewrite one record and recompute every later link. Only anchoring to a commitment the operator cannot rewrite converts consistency into originality, and this document requires every record to say which of the two it offers.¶
This document specifies the receipt (Section 4), its canonical serialization and digest (Section 5), the chain manifest (Section 6), the subjects a digest may stand for (Section 7), coverage, emission and anchoring declarations (Sections 8, 9 and 10), verifier behaviour (Section 11), conformance (Section 13) and golden vectors (Appendix A).¶
This revision applies to linear chains: a run whose stages form a sequence, in which each receipt has at most one predecessor and each retry of a stage is recorded as a distinct attempt with its own receipt. Fan-out, fan-in, branches, loops, compensation and partial commits, exactly-once versus at-least-once delivery, out-of-order arrival, and an ordering model for stages whose clocks disagree are not specified here. They are a recognized requirement of the format and the largest thing it does not yet do; a producer whose pipeline has such a topology MUST declare the boundary (Section 8) rather than flatten the topology into a sequence that did not occur.¶
This document does not specify a transport, a storage system, a signature envelope, a key lifecycle, an anchoring mechanism, or a service. A receipt is bytes in a file; anything that moves or keeps those bytes is out of scope provided it does not alter them.¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
The rules in later sections follow from a small number of principles, stated here so that a case this document does not anticipate can be decided the same way.¶
A record describes itself. Every receipt names its format, its run, its stage, its instrument, the constants its assertions depend on, and the zone of every time it carries. A reader needs nothing outside the record to know what the record claims.¶
Bytes are the record. A receipt is the exact bytes of its canonical serialization, not any equivalent of them. Its digest is over those bytes. A verifier compares bytes; it does not normalize nearly-canonical bytes into canonical ones.¶
A record never contains its own digest. A record cannot contain the proof of its own final state. The digest lives in the next receipt and in the manifest.¶
Absent, declared-none and recorded are three states. A field that is missing, a field that says there is nothing, and a field that records something are distinguishable and round-trip distinguishably. A failed measurement is not a measured zero.¶
A refusal is a result. A stage that declined to produce output records that it declined and why. A format that records only successes describes a happy path.¶
What cannot be established is reported, not implied. A verifier has three outcomes, never two. A record that is not anchored says so, and a verifier repeats it.¶
Checkability depends on no one. A conforming verifier needs no network, key, account, licence, or live third party. Evidence whose checkability needs a living authority inherits that authority's lifespan.¶
The verifier does not judge the work. A well-formed record of a wrong answer verifies. Whether the pipeline was correct is a question for a different tool, whose output is written beside the records and never into them.¶
A stage receipt is a JSON [RFC8259] object with exactly the thirteen members of Table 1, serialized in canonical form (Section 5). Every one of them is REQUIRED. A receipt lacking any of them is invalid.¶
| Member | Type | Defined in |
|---|---|---|
| format | string | Section 4.2 |
| run_id | string | Section 4.3 |
| stage | object | Section 4.3 |
| prev | string or null | Section 4.4 |
| time | object | Section 4.5 |
| coverage | object | Section 8 |
| emission | object | Section 9 |
| anchor | object | Section 10 |
| instrument | object | Section 4.6 |
| inputs | array | Section 4.7 |
| outputs | array | Section 4.7 |
| assertions | object | Section 4.8 |
| outcome | object | Section 4.9 |
Member values are JSON strings, objects, arrays, or the literal null where this document permits it. JSON numbers MUST NOT appear anywhere in a receipt. Every numeric quantity -- a byte count, a stage index, a threshold, a count -- is carried as a decimal string, exactly as the producer measured or configured it, with its precision intact. This document defines no member whose value is a JSON boolean; the members that carry a truth value carry the strings "true" and "false".¶
Unknown members MAY appear. They are part of the bytes and therefore of the digest, but a verifier MUST NOT interpret them and a producer MUST NOT rely on them for any semantics this document assigns to a defined member. The member names "receipt_digest" and "receipt_id" are reserved and MUST NOT appear (Section 5).¶
The string "stage-receipt/0.1". A verifier that encounters a different value proceeds as described in Section 14.¶
"run_id" is a non-empty string chosen by the producer, unique among that producer's runs. Its structure is not specified.¶
"stage" is an object with two REQUIRED members: "index", a decimal string giving the receipt's one-based position in the chain, and "name", a non-empty string naming the stage. The name of a stage SHOULD match the name the pipeline manifest gives it, since coverage (Section 8) is computed over names. A retried stage is a new stage attempt with the next index; the producer MAY record the attempt number in "assertions" and SHOULD keep the name unchanged.¶
The digest of the exact bytes of the preceding receipt in the chain, or the literal null for the first receipt of a run. A receipt with index "1" MUST carry null; every other receipt MUST carry a digest. This member is what makes the chain a chain: editing any earlier receipt changes its digest, which breaks the link in every later receipt.¶
An object with two REQUIRED members, "started" and "ended", each a timestamp in the Internet profile of ISO 8601 [RFC3339] including its UTC offset or the designator "Z". A timestamp without its zone is half a timestamp; a verifier MUST reject it. Fractional seconds are permitted. "ended" MUST NOT precede "started".¶
These are the producer's clock readings. Nothing in a receipt makes them true; anchoring (Section 10) is what lets a reader bound when a record existed.¶
An object naming and pinning the component that ran the stage. The REQUIRED members are:¶
A record whose instrument cannot be named or pinned is not a weak record; it is not a record. The rejection vector R02 (Appendix A) exists for this case.¶
Each is an array, possibly empty, of artifact references. An empty array is a statement that the stage consumed, or produced, nothing -- the declared-none state of Section 3 -- and is distinct from the member being absent, which is invalid. The third golden receipt records a stage that produced no output and says so with an empty array and a "refused" outcome.¶
An artifact reference is an object with the REQUIRED members:¶
The trust class does not make externally-sourced content safe. It makes its origin visible to every downstream reader, human or model, which is the most a record format can promise. A consumer that feeds artifact content to a model SHOULD carry the trust class with it.¶
An output of one stage that is an input of the next SHOULD appear in both receipts with the same digest; the difference between the two, when there is one, is itself a finding (Section 12.2).¶
An object in which the producer records what the stage asserts about its work: counts, thresholds, derived quantities, references to corpus manifests, prompt digests. Its members are not enumerated by this document, with one exception: if "assertions" is non-empty, it MUST contain a member "constants", an object naming every transform constant -- tokenizer, join rule, batch size, top-k, threshold, offset unit -- on which the other assertions depend. A relation that holds only under an unstated constant tests the constant, not the relation. The empty object is permitted and means the stage asserts nothing beyond its inputs, outputs and outcome.¶
A count in "assertions" is testimony unless the receipt states how it was derived. A producer SHOULD record the derivation of any aggregate (as the fourth golden receipt does for a zero) so that a consumer can recompute it rather than believe it.¶
An object whose REQUIRED member "class" is exactly one of:¶
Other members MAY be present. The class is the only member a verifier interprets.¶
A stage that acts on the world -- sends a message, writes to an external system, places an order -- produces an effect whose state is not one of its output bytes. A receipt for such a stage SHOULD carry the OPTIONAL member "effects", an array of objects each with the REQUIRED members "target" (a string naming the external system) and "state", exactly one of "attempted", "returned-a-response", or "confirmed-by-the-external-system", and the OPTIONAL member "evidence", an artifact reference for the response or confirmation bytes.¶
These are three states, not one state with three names. "attempted" says the request left; "returned-a-response" says something came back, whose bytes may be recorded; "confirmed-by-the-external-system" says the external system's own record agrees. A consumer MUST NOT read the first as the third, and a producer MUST NOT promote a state it did not observe. Marked for re-read after the pathology fixtures: the exact evidence a confirmation requires is expected to be sharpened by them.¶
A receipt, a chain manifest, and any other object this document digests is serialized in the following canonical form. A file holding a receipt contains exactly these bytes and nothing else.¶
The digest of an object is SHA-256 [RFC6234] over its canonical bytes, written as "sha256:" followed by 64 lowercase hexadecimal digits. The digest of a file is SHA-256 over the file's bytes; for a conforming receipt these are the same thing, and a verifier establishes that they are (Section 11.3.1).¶
A serializer that reads a JSON object, sorts its members, and writes it without whitespace produces this form, provided it refuses numbers. In particular, a serializer conforming to [RFC8785] produces identical bytes for any object that satisfies rules 4 and 8. The reference canonicalizer published with the golden vectors is twenty lines and is the normative statement of this section in executable form; where prose and code disagree, the disagreement is a defect in this document and the vectors decide.¶
Line endings, byte-order marks, path length and file-system normalization are named traps. A producer that writes a receipt through a text layer that converts line endings has not written a receipt; a verifier that reads one through such a layer will report a false mismatch. Receipts are read and written as binary.¶
A run's receipts are listed by a chain manifest: a JSON object in canonical form with the REQUIRED members:¶
The manifest MAY carry other members. It is itself digested in canonical form when it is anchored or listed by a root over many chains.¶
The chain is valid when, for every receipt, "prev" equals the digest listed for the preceding receipt (null for the first), the listed digest equals the digest of the file's bytes, and "chain_head" equals the digest of the last receipt. A chain with a missing receipt is not a chain with a gap; it is a chain that fails, and the emission declaration (Section 9) is how a producer records an absence it knew about rather than leaving a verifier to discover one.¶
A digest in a receipt is only as meaningful as the rule that says which bytes it was computed over. This section fixes that rule for each kind of subject. A producer MUST digest each subject as stated and MUST record in "assertions.constants" any parameter this section says is declared.¶
"coverage" is an object with the REQUIRED members:¶
"complete" is a claim and a verifier checks it: every declared stage MUST appear among the emitting stages, and "boundaries" MUST be empty. A receipt that asserts "complete" while a declared stage is missing is lying about its own edges, and a verifier MUST fail it (vector R07), not warn.¶
Coverage is derived by the producer from the manifest and the run, never typed by hand. Its purpose is the following rule, which binds every consumer of these records: where coverage is "incomplete", no consumer may assert a root cause across an undeclared boundary. A tool that cannot see the edge of its own chart will eventually report confidently about something it never observed. The claim this document permits a producer to make of a chain is, accordingly, bounded: for the receipted portion of a workflow, an independent verifier can determine whether the executed topology and component versions matched the approved manifest -- and nothing about the unreceipted portion.¶
A run may legitimately be "incomplete": a pipeline with a non-linear region (Section 1.1) declares it as a boundary and receipts the linear portions on either side. That is the honest shape. Flattening the region into a sequence that did not run is not.¶
"emission" is an object with the REQUIRED members:¶
The policy is chosen by the operator at configuration time and MUST be declared; a producer MUST NOT default it. A policy the operator did not choose is a policy they cannot be held to.¶
Under either policy an absence is recorded as a declared gap, in the next receipt that is written and in the manifest. Fail-open does not mean that errors disappear; it means the pipeline continued and the record says where it did so unreceipted. A gap makes the run's coverage "incomplete" (Section 8) with the gap as a boundary.¶
"anchor" is an object with the REQUIRED member "state", exactly one of "anchored" or "unanchored". Absence is a failure (vector R08): "unanchored" is a state, not a silence.¶
When "state" is "unanchored", the member "reason" SHOULD say why. When "state" is "anchored", the following members are REQUIRED:¶
This section is the most important in the document, and it is short because what it says is simple. A chain whose links all verify proves that its records are consistent with one another. It does not prove that they are the records that were made: an operator who holds every copy can rewrite one, recompute every later "prev", and produce a chain that verifies. A hash proves a record is consistent with itself, never that it is the record that was made. Only a commitment the operator cannot rewrite converts consistency into originality, and only the anchor's cadence bounds how stale that conversion is.¶
This revision specifies the declaration, not a mechanism. A declaration is the part a format can require and a verifier can test. Whether a given anchor is in fact unrewritable is a property of the system that holds it, is not testable from the records, and is not claimed by any conforming producer or verifier. A verifier reports originality as NOT RUN on every unanchored record, with that reason, so that a green chain can never be read as more than it is.¶
A verifier answers one question: can a stranger, holding only these records, establish what they assert, without trusting whoever produced them? Everything in this section follows from that, including the refusals.¶
A conforming verifier:¶
These are not implementation preferences. A verifier that violates any of them has re-created the dependence the format exists to remove. The publisher of a conforming verifier is correspondingly bound: it MUST NOT operate a certificate authority, key escrow, or any service on which a record's checkability depends.¶
For every property it checks, a verifier reports exactly one of:¶
A verifier that reports only pass and fail will eventually report PASS for something it did not check. A verifier MUST NOT be silent about a property this section requires it to check.¶
The file's bytes are the canonical serialization of the object they encode -- identical, not equivalent (vector R10). No JSON number appears (R01). The object contains no reserved self-referential member (R09).¶
Every required member is present. The instrument is named and pinned (R02). Every non-empty "assertions" names its constants (R04). Every timestamp carries its zone (R03). Every artifact reference declares a permitted trust class (R05).¶
The outcome class is one of the three permitted values. An "error" outcome carries status, body and origin (R06). An empty array and an absent member are distinguished, and the verifier reports which it found.¶
"completeness" is present and is one of its two values. Where it is "complete", every declared stage is an emitting stage and there are no boundaries; a contradiction is a FAIL (R07). Where it is "incomplete", the verifier reports the boundaries and gaps it found and reports the originality of anything beyond them as NOT RUN.¶
Each receipt's "prev" equals the digest of the preceding receipt's bytes. Each digest in the manifest equals the digest of the named file's bytes. "chain_head" equals the last receipt's digest.¶
"anchor.state" is present and is one of its two values (R08). Where it is "unanchored", the verifier MUST report originality as NOT RUN with the stated reason. Where it is "anchored", the verifier reports the declaration's presence and well-formedness and MUST NOT report the anchor as established unless it has itself resolved the reference -- which, under Section 11.1, it cannot do online. A verifier MAY accept a locally supplied copy of the commitment and report against it, saying so.¶
The verifier establishes that records are what they claim. The operations in this section are what a developer does with records that have been established. They are defined here so that two tools performing them on the same chains reach the same answers; they read only the records and, for replay, the pipeline's own code.¶
Given two chains of the same pipeline, the diff is the first index at which the receipts differ in any of: the digests of their inputs, the instrument's "id", "version" or "config_digest", the "constants", or the digests of their outputs -- reported in that order, so that a changed input is not mistaken for a changed instrument. Timestamps, "run_id" and "prev" are excluded from the comparison. Two chains that differ nowhere but in those members ran the same pipeline on the same bytes and produced the same bytes.¶
Given one chain, localization walks it in order and reports the first receipt whose outcome class is not "ok"; whose declared output digest is not the input digest the next receipt records for the same artifact; or whose coverage records a gap or boundary. The report names the stage and which of the three conditions held. A localization that crosses a declared boundary stops there and says so (Section 8).¶
Given one chain and the pipeline's code at the versions the receipts pin, reproduction runs each stage whose instrument declares "rederivable" as "true" from the artifacts its receipt records as inputs, and reports per stage whether the output bytes' digest equals the recorded output digest. Stages declared not re-derivable are reported as NOT RUN with the instrument's note as the reason. A reproduction that reaches an artifact it does not hold reports NOT RUN for that stage rather than substituting. Reproduction produces a new chain; it never rewrites the old one, and a corrected run is a new version of the result, never an undo of the previous one.¶
A conforming producer emits receipts that contain exactly the required members with the value constraints of Section 4, serialized as Section 5 requires, digested as Section 7 requires, linked as Section 6 requires, with coverage derived rather than typed, with an operator-declared emission policy, and with an anchor declaration on every receipt. It refuses to emit rather than emit a receipt whose instrument it cannot name and pin. Every receipt it emits MUST verify, and it MUST NOT emit any of the ten rejection vectors' defects.¶
A conforming verifier satisfies the five constraints of Section 11.1, reports the three outcomes of Section 11.2, performs every check of Section 11.3, and does none of the things in Section 11.4. It MUST report PASS on the golden chain of Appendix A.1 with exactly the NOT RUN outcomes that chain's anchor declarations require, and it MUST refuse each of the ten rejection vectors of Appendix A.2 for the stated reason. Refusing the right file for the wrong reason is a failing test that happens to look green.¶
A verifier that requires knowledge of which framework or producer emitted a record, beyond what the instrument declaration states, does not conform. One verifier checking records from several independent producers, blind, is the bar this document is aimed at; if such a verifier cannot be written, the format has dialects and this document has failed.¶
Conformance says nothing about whether a pipeline is correct, a model good, a system fast, a record tamper-resistant, or a deployment certified. None of those is tested by anything in this document and none may be claimed on its authority.¶
The "format" member carries "stage-receipt/MAJOR.MINOR". Within a major version, a minor revision MAY add optional members, permitted values of unenumerated strings, and required checks that only fail on records the earlier minor already forbade; it MUST NOT remove a required member, change a member's type, or change the canonical form. A verifier MUST accept any minor version of the major it implements and MUST report NOT RUN, with a version reason, for any check the record's minor version did not define. A major revision MAY change required semantics.¶
A verifier that encounters a record whose "format" is a major version it does not implement -- including the pre-standard value "stage-receipt/0.0-preview", which corresponds to this document's golden chain before its format string was assigned -- MUST report NOT RUN with the version as the reason. It MUST NOT report PASS, since it did not check the record against the rules the record claims, and it MUST NOT report FAIL, since the record contradicted nothing the verifier knows.¶
This document has no IANA actions. A later revision may register the media type "application/stage-receipt+json" for receipts and "application/stage-receipt-chain+json" for chain manifests; nothing in this revision depends on such registration.¶
Consistency is not originality. The central security property of this format is stated in Section 10 and is repeated here because it is the one most likely to be misread: a chain that verifies is consistent with itself and nothing more. An operator can rewrite an unanchored chain without detection. A reader who treats verification as proof of history has been misled by the reader, not the record, and the format's defence is to require every record and every verifier to say so out loud. The anchor cadence is the tamper window; a producer choosing a cadence is choosing how long a rewrite can go unnoticed.¶
Digests are commitments. A published digest of a secret, a credential, or a personal record is a commitment to bytes that can never be withdrawn; anyone who later obtains the bytes can prove they existed at anchoring time. Artifacts MUST be reviewed as publishable before their digests are anchored, and secrets are never digested into anything (Section 7). Low-entropy artifacts -- a short answer, a yes-or-no field, a small set of candidates -- can be recovered from their digests by enumeration; a producer that needs to record such a value without disclosing it MUST use a keyed or salted construction outside this document and record the construction's identity in "constants".¶
Records are data, not instructions. Artifact contents, names, notes and reasons in a receipt are written by the producer and, through externally-sourced inputs, by parties the producer does not control. A consumer that feeds any of them to a model MUST treat them as untrusted content and SHOULD carry the trust class alongside them. The trust class makes origin visible; it does not make content safe.¶
The verifier is a target. A verifier parses attacker-controlled bytes. It MUST bound the size of what it reads, MUST NOT follow paths in a manifest outside the manifest's directory, and MUST NOT execute, fetch, or resolve anything a record names. The constraint that it requires no network is also a defence: a verifier that cannot reach anything cannot be made to.¶
Signatures are not specified. This revision binds no record to an actor. A signature envelope and a key lifecycle are recognized requirements, sequenced behind actor identity (Appendix B). Until they exist, a receipt's "instrument" and a manifest's "anchor" say what ran and where the head was committed, not who vouches for either.¶
What is not defended. The format is tamper-evident to the extent of its anchoring and not tamper-resistant at all. It does not prevent a producer from emitting false records; it makes a false record cost a consistent lie across every later record and every anchor, and it makes the absence of an anchor visible. Deployment isolation between tenants, the integrity of the host, and the honesty of the operator are outside what any record can establish.¶
A receipt carries digests, sizes, names, media types, timestamps and operator-written notes; it does not carry artifact content. Names and notes are free text and a producer MUST NOT place personal data in them. Timestamps at millisecond precision, combined with stage names, can identify a person's interaction with a system; a producer that publishes chains SHOULD consider whether the run identifiers and times it publishes are themselves personal data.¶
An anchored digest of an artifact that contains personal data is a permanent commitment to that data's existence, which may conflict with an obligation to erase it. Erasure of the artifact does not erase the commitment. Producers subject to such obligations SHOULD NOT anchor digests of artifacts that may need to be erased, or SHOULD digest them through a keyed construction whose key can be destroyed, recording the construction in "constants" so that the record remains self-describing.¶
The files in this appendix are part of the specification. Long lines are folded as described in [RFC8792]; a receipt's bytes are recovered by unfolding, and the SHA-256 of the recovered bytes is given in each figure's title so that the unfolding can be checked before anything else is. The same objects were published on 2026-09-11, before this document, as a preview under the format string "stage-receipt/0.0-preview"; that edition differs from this one only in the format strings and, consequently, in every digest.¶
One run of a four-stage pipeline: retrieve, rerank, generate, verify. The third receipt records a refusal -- the generator produced no claim that could be bound to a retrieved passage -- and the fourth records zero claims examined and states how the zero was derived. Every receipt declares itself unanchored, so a conforming verifier reports PASS for every other property and NOT RUN, with that reason, for originality: on this chain, 103 PASS, 0 FAIL, 4 NOT RUN. The artifacts the digests reference are not published; the structure is the point.¶
========== NOTE: '\' line wrapping per RFC 8792 ===========
{"anchor":{"reason":"preview sample; epoch anchoring is a declared r\
equirement and is not implemented","state":"unanchored"},"assertions\
":{"candidate_count":"1500","constants":{"join":"blank-line","tokeni\
zer":"unicode-nfkc-lower","top_k":"1500"},"corpus_manifest":"sha256:\
b3d5f7a9c1e3d5b7f9a1c3e5d7b9f1a3c5e7d9b1f3a5c7e9d1b3f5a7c9e1d3b5"},"\
coverage":{"boundaries":[],"completeness":"complete","declared_stage\
s":["retrieve","rerank","generate","verify"],"emitting_stages":["ret\
rieve","rerank","generate","verify"]},"emission":{"gaps":[],"policy"\
:"fail-closed"},"format":"stage-receipt/0.1","inputs":[{"bytes":"57"\
,"digest":"sha256:9c56cc51b374c3ba189210d5b6d4bf57790d351c96c47c0219\
0ecf1e430635ab","media_type":"text/plain","name":"question","trust_c\
lass":"externally-sourced"}],"instrument":{"config_digest":"sha256:2\
a7d9e4b1c8f0356ae2d4b6c8f1a3e5d7b9c0f2a4e6d8b0c2f4a6e8d0b2c4f6a","id\
":"bm25+dense-hybrid","kind":"retriever","manifest_digest":"sha256:6\
f1b1c0d4a2e9d7c3b58ae0f21d4c6b98e7a5f30c1d2e4b6a8093f5c7e1d2b4a","re\
derivable":"true","version":"1.4.0"},"outcome":{"class":"ok"},"outpu\
ts":[{"bytes":"184213","digest":"sha256:0d5c1a8f3e7b2946d0c8a1f5e39b\
7d2c460a8e1f3b5d7c9a0e2f4b6d8c0a2e4f","media_type":"application/json\
","name":"candidates","trust_class":"operator-authored"}],"prev":nul\
l,"run_id":"run-2026-09-11-0001","stage":{"index":"1","name":"retrie\
ve"},"time":{"ended":"2026-09-11T09:14:02.774+02:00","started":"2026\
-09-11T09:14:02.118+02:00"}}
========== NOTE: '\' line wrapping per RFC 8792 ===========
{"anchor":{"reason":"preview sample; epoch anchoring is a declared r\
equirement and is not implemented","state":"unanchored"},"assertions\
":{"chance_floor":"3.88","constants":{"batch_size":"32","max_input_t\
okens":"512","score_order":"descending","truncation":"auto"},"scored\
_pairs":"1500"},"coverage":{"boundaries":[],"completeness":"complete\
","declared_stages":["retrieve","rerank","generate","verify"],"emitt\
ing_stages":["retrieve","rerank","generate","verify"]},"emission":{"\
gaps":[],"policy":"fail-closed"},"format":"stage-receipt/0.1","input\
s":[{"bytes":"184213","digest":"sha256:0d5c1a8f3e7b2946d0c8a1f5e39b7\
d2c460a8e1f3b5d7c9a0e2f4b6d8c0a2e4f","media_type":"application/json"\
,"name":"candidates","trust_class":"operator-authored"}],"instrument\
":{"config_digest":"sha256:5e7c9a1f3d5b7e9c1a3f5d7b9e1c3a5f7d9b1e3c5\
a7f9d1b3e5c7a9f1d3b5e7c","id":"reranker-a","kind":"cross-encoder","m\
anifest_digest":"sha256:233902d2f4a6c8e0b2d4f6a8c0e2b4d6f8a0c2e4b6d8\
f0a2c4e6b8d0f2a4c6e8","rederivable":"true","version":"pinned"},"outc\
ome":{"class":"ok"},"outputs":[{"bytes":"41120","digest":"sha256:7a9\
c1e3f5b7d9a1c3e5f7b9d1a3c5e7f9b1d3a5c7e9f1b3d5a7c9e1f3b5d7a9c","medi\
a_type":"application/json","name":"ranked","trust_class":"operator-a\
uthored"}],"prev":"sha256:3a3e67631b62fbf8107a12b989a97f16072b487d54\
b18e64ac0eabbcb54fe673","run_id":"run-2026-09-11-0001","stage":{"ind\
ex":"2","name":"rerank"},"time":{"ended":"2026-09-11T09:14:02.774+02\
:00","started":"2026-09-11T09:14:02.118+02:00"}}
========== NOTE: '\' line wrapping per RFC 8792 ===========
{"anchor":{"reason":"preview sample; epoch anchoring is a declared r\
equirement and is not implemented","state":"unanchored"},"assertions\
":{"claims":[],"constants":{"prompt_digest":"sha256:3b5d7f9a1c3e5b7d\
9f1a3c5e7b9d1f3a5c7e9b1d3f5a7c9e1b3d5f7a9c1e3b5d"}},"coverage":{"bou\
ndaries":[],"completeness":"complete","declared_stages":["retrieve",\
"rerank","generate","verify"],"emitting_stages":["retrieve","rerank"\
,"generate","verify"]},"emission":{"gaps":[],"policy":"fail-closed"}\
,"format":"stage-receipt/0.1","inputs":[{"bytes":"41120","digest":"s\
ha256:7a9c1e3f5b7d9a1c3e5f7b9d1a3c5e7f9b1d3a5c7e9f1b3d5a7c9e1f3b5d7a\
9c","media_type":"application/json","name":"ranked","trust_class":"o\
perator-authored"},{"bytes":"12904","digest":"sha256:e5c7a9f1d3b5e7c\
9a1f3d5b7e9c1a3f5d7b9e1c3a5f7d9b1e3c5a7f9d1b3e5c7","media_type":"tex\
t/plain","name":"evidence_passages","trust_class":"externally-source\
d"}],"instrument":{"config_digest":"sha256:8d0f2a4c6e8b0d2f4a6c8e0b2\
d4f6a8c0e2b4d6f8a0c2e4b6d8f0a2c4e6b8d0f","id":"instrument-b","kind":\
"generator","manifest_digest":"sha256:c1e3a5f7d9b1c3e5a7f9d1b3c5e7a9\
f1d3b5c7e9a1f3d5b7c9e1a3f5d7b9c1e3","rederivable":"false","rederivab\
le_note":"generation is not bit-reproducible; the instrument is decl\
ared, the output is recorded, replay is not promised","version":"pin\
ned"},"outcome":{"class":"refused","detail":"the generator produce\
d no claim that could be bound to a retrieved passage; a refusal i\
s recorded, not an empty answer","reason":"contract: no claim surviv\
ed evidence binding"},"outputs":[],"prev":"sha256:0765685137f1d693a9\
532e91e0bf691d258977c688b2a941eaa5da35b65cdf76","run_id":"run-2026-0\
9-11-0001","stage":{"index":"3","name":"generate"},"time":{"ended":"\
2026-09-11T09:14:02.774+02:00","started":"2026-09-11T09:14:02.118+02\
:00"}}
========== NOTE: '\' line wrapping per RFC 8792 ===========
{"anchor":{"reason":"preview sample; epoch anchoring is a declared r\
equirement and is not implemented","state":"unanchored"},"assertions\
":{"claims_contradicted":"0","claims_examined":"0","claims_supported\
":"0","constants":{"contradiction_threshold":"0.60","support_thresho\
ld":"0.30"},"derivation":"counts recomputed from stage 3 outcome; ze\
ro claims were emitted, therefore zero were examined"},"coverage":{"\
boundaries":[],"completeness":"complete","declared_stages":["retriev\
e","rerank","generate","verify"],"emitting_stages":["retrieve","rera\
nk","generate","verify"]},"emission":{"gaps":[],"policy":"fail-close\
d"},"format":"stage-receipt/0.1","inputs":[],"instrument":{"config_d\
igest":"sha256:f3a5c7e9b1d3f5a7c9e1b3d5f7a9c1e3b5d7f9a1c3e5b7d9f1a3c\
5e7b9d1f3a5","id":"claim-binder","kind":"verifier","manifest_digest"\
:"sha256:a1c3e5b7d9f1a3c5e7b9d1f3a5c7e9b1d3f5a7c9e1b3d5f7a9c1e3b5d7f\
9a1c3","rederivable":"true","version":"0.3.1"},"outcome":{"class":"o\
k","note":"nothing to verify is a result, not an absence"},"outputs"\
:[],"prev":"sha256:f1e74907f1c47ff905f2860d9213d00f084c1632d652580f8\
bde036ff80522b0","run_id":"run-2026-09-11-0001","stage":{"index":"4"\
,"name":"verify"},"time":{"ended":"2026-09-11T09:14:02.774+02:00","s\
tarted":"2026-09-11T09:14:02.118+02:00"}}
========== NOTE: '\' line wrapping per RFC 8792 ===========
{"anchor":{"reason":"preview sample; epoch anchoring is a declared r\
equirement and is not implemented","state":"unanchored"},"chain":[{"\
digest":"sha256:3a3e67631b62fbf8107a12b989a97f16072b487d54b18e64ac0e\
abbcb54fe673","file":"receipts/01-retrieve.json","index":"1","stage"\
:"retrieve"},{"digest":"sha256:0765685137f1d693a9532e91e0bf691d25897\
7c688b2a941eaa5da35b65cdf76","file":"receipts/02-rerank.json","index\
":"2","stage":"rerank"},{"digest":"sha256:f1e74907f1c47ff905f2860d92\
13d00f084c1632d652580f8bde036ff80522b0","file":"receipts/03-generate\
.json","index":"3","stage":"generate"},{"digest":"sha256:1549e9c8509\
1af54d7fce8c2d2832427be9eeda26ed5a0b7af49352d44a74e1a","file":"recei\
pts/04-verify.json","index":"4","stage":"verify"}],"chain_head":"sha\
256:1549e9c85091af54d7fce8c2d2832427be9eeda26ed5a0b7af49352d44a74e1a\
","format":"stage-receipt-chain/0.1","note":"Golden chain of draft-s\
aha-stage-receipts-00. Digests are over each file's exact bytes.","r\
un_id":"run-2026-09-11-0001"}
Each of the following is the first golden receipt with one defect introduced. A conforming verifier MUST refuse each one and MUST state the reason given. The exact bytes of each vector are obtained by applying the stated change to the first golden receipt and re-serializing in canonical form (except R10, whose defect is its serialization); the SHA-256 of the resulting file is given so that an implementer can confirm the vector was reconstructed correctly.¶
Change: "assertions.candidate_count" becomes the JSON number 1500.¶
Reason: a JSON number where a decimal string is required ('500.00' and '500' must stay distinct).¶
SHA-256 of the file: a331ddb798be868f0769ffcfecb9e50b 5979ed57be92b3ff9dab62934b4f8ee7 (one string; shown with a space for line width).¶
Change: "instrument.manifest_digest" becomes the empty string.¶
Reason: instrument cannot be named or pinned; refusal is required, not a blank field.¶
SHA-256 of the file: c1cc9258fa8f4c6e1969278a5dea85f9 db6c3afb1ffbe94e3e810051824ea1bb (one string; shown with a space for line width).¶
Change: "time.started" becomes "2026-09-11T09:14:02.118", with no offset.¶
Reason: a timestamp without its zone is half a timestamp.¶
SHA-256 of the file: ff99a81cd7c8320b840efbb1496f91cd 29c8cdd40643a290800bd859e182eb38 (one string; shown with a space for line width).¶
Change: "assertions.constants" is removed.¶
Reason: an assertion that does not name its transform constants.¶
SHA-256 of the file: 8f87aa87ca7731c5d131076f98d648da a5b01eddcee687e348e413594f0cd476 (one string; shown with a space for line width).¶
Change: "inputs[0].trust_class" becomes "unknown".¶
Reason: an input with no declared trust class.¶
SHA-256 of the file: 37be84ab9b559f0a919205964bf48b4e a8d7472cbbbeb007d4ba4df72e24785a (one string; shown with a space for line width).¶
Change: "outcome" becomes {"class":"error","status":"403"}.¶
Reason: an HTTP error without its body is a status code wearing an explanation.¶
SHA-256 of the file: fdddfe162027eac5fb57b0751a7cdf1e 1ad9738f3570c66c928472641d19ad0a (one string; shown with a space for line width).¶
Change: "coverage.emitting_stages" becomes ["retrieve","rerank"].¶
Reason: 'complete' asserted while declared stages are missing from the emitting list.¶
SHA-256 of the file: 6e678e5da16a5e3e7cd00bc9e69500c5 793e47e04f18e9451898df9fbbd2576c (one string; shown with a space for line width).¶
Change: "anchor" is removed.¶
Reason: anchor state absent rather than declared; unanchored is a state, not a silence.¶
SHA-256 of the file: a5ae3bd9148df44e1848a31d3b3aa81d 6aaaf209ef451eff710d7feb58d30429 (one string; shown with a space for line width).¶
Change: a member "receipt_digest" with the value "sha256:00" is added.¶
Reason: a record may not contain the proof of its own final state.¶
SHA-256 of the file: 9a068cc45f41a94981da22d8db97218f b34d9eedbd88bdd0e5144b97119037d4 (one string; shown with a space for line width).¶
Change: the object is serialized with two-space indentation and newlines instead of in canonical form.¶
Reason: pretty-printed; file bytes are not the canonical serialization.¶
SHA-256 of the file: 891a20d03391b01112ade85dea864478 99e139d6908a71cd49111b77457d77f4 (one string; shown with a space for line width).¶
A format with no rejection vectors has not specified anything; it has described a happy path and left every implementer to guess at the edges. Implementers are asked to publish the results of their verifier against both appendices, including any disagreement with the reference implementation. A disagreement between two independent verifiers is the most valuable report this document can receive, because it means the specification is ambiguous and the ambiguity was found before anyone depended on it.¶
The format was specified against a register of thirteen requirements, each raised by a defect or a reviewer, and the register is published with the format's validation battery. This revision satisfies some, declares some, and leaves some standing. Naming the last group is the point of this appendix.¶
| # | Requirement | This revision |
|---|---|---|
| 1 | Merkle corpus manifest | Root construction specified (Section 7); inclusion proofs and a tampered-member vector deferred. |
| 2 | Epoch anchoring | Declaration specified (Section 10); mechanism not specified. |
| 3 | Tenant isolation | Not specified; a tenant identifier is expected in a later revision. |
| 4 | Signature envelope | Not specified; sequenced behind actor identity. |
| 5 | Per-receipt dependency record | Not specified; intended to align with existing bill-of-materials formats. |
| 6 | Signing-key lifecycle | Not specified; follows 4. |
| 7 | Receipt topology | Linear chains and retries as attempts (Section 1.1); everything else declared as a boundary. The largest open item. |
| 8 | Disclosure layer | Partially met: a chain verifies with artifact contents withheld, since receipts carry digests only. |
| 9 | Coverage attestation | Specified (Section 8). |
| 10 | Content trust class | Specified (Section 4.7). |
| 11 | Emission failure semantics | Specified (Section 9). |
| 12 | Schema evolution | Specified minimally (Section 14). |
| 13 | External effect records | Specified as an optional member (Section 4.10). |
This section will be removed before publication as an RFC.¶
The format was specified after its validation battery and before its reference implementation, in that order deliberately: the exam is published before the code it judges. The sections most likely to change when the battery's pathology fixtures are written against real code are Section 4.10, the error body requirements in Section 4.9, the boundary object in Section 8, and the gap object in Section 9. Each is marked in the text. Nothing in the canonical form, the required members, or the rejection vectors is expected to change.¶
The preview verifier published with the samples predates this document and does not yet perform every check this document requires: it does not check that "complete" implies an empty "boundaries", that "ended" does not precede "started", that "prev" is null exactly when "index" is "1", that "rederivable" is one of its two values, or that the manifest's "run_id" equals every receipt's. A conforming verifier performs all of them. Rejection vectors for each are expected in the next revision, since a requirement without a vector that fails is a requirement nobody has tested.¶
Three independent reviewers read the requirement register and the battery before this document was written. The coverage requirement and the wording of the bounded claim in Section 8 came from two of them separately; the topology requirement, the external-effect states and the observation that a format nobody wants on a Tuesday will not be installed came from the third. The defects that motivated the rejection vectors were the author's own.¶