<?xml version="1.0" encoding="utf-8"?>
<!-- name="GENERATOR" content="github.com/mmarkdown/mmark Mmark Markdown Processor - mmark.miek.nl" -->
<rfc version="3" ipr="trust200902" docName="draft-noa-scitt-ai-agent-receipt-01" submissionType="IETF" category="std" xml:lang="en" xmlns:xi="http://www.w3.org/2001/XInclude" indexInclude="true">

<front>
<title abbrev="SCITT AI-Agent Action Receipts">A SCITT Profile for AI-Agent Action Receipts</title><seriesInfo value="draft-noa-scitt-ai-agent-receipt-01" stream="IETF" status="standard" name="Internet-Draft"></seriesInfo>
<author initials="T." surname="Toraman" fullname="Tora Toraman"><organization>NordenSoft</organization><address><postal><street></street>
<country>Denmark</country>
</postal><email>hello@ordeliya.com</email>
</address></author><date year="2026" month="August" day="15"></date>
<area>Security</area>
<workgroup>SCITT</workgroup>
<keyword>SCITT</keyword>
<keyword>COSE</keyword>
<keyword>AI agent</keyword>
<keyword>receipt</keyword>
<keyword>provenance</keyword>
<keyword>policy</keyword>
<keyword>attestation</keyword>
<keyword>accountability</keyword>

<abstract>
<t>This document profiles the IETF SCITT (Supply Chain Integrity, Transparency, and Trust)
architecture for AI-agent action receipts: tamper-evident, signed, offline-verifiable records of
what an autonomous agent was recorded as doing at the governed boundary, under which recorded
principal class, with what recorded verdict, and -- where the issuer records one -- under which
policy identity. Each receipt is a signed record over a canonical JSON payload, hash-chained so
that each record commits to its predecessor, and presented either bare -- the payload with its own
native signature -- or enveloped in a COSE_Sign1. This revision specifies how such a receipt is carried as a SCITT Signed Statement, with
the protected claims a Transparency Service requires, so that a receipt can be registered. Registration
obtains a Transparency Service's signed proof that the statement was registered in its log --
a property a self-signed chain cannot provide alone. It does not, by itself, give an offline
holder non-equivocation: that requires consistency proofs and monitoring of the log, which this
profile does not specify. The
profile makes a deliberately narrow, checkable claim: this is an issuer-authenticated,
signature-verifiable, tamper-evident record of the action, the recorded principal class, the
recorded verdict, and any policy identity the receipt carries. It explicitly does not claim that the agent was correct, safe,
or wise, that the recorded inputs were true or complete, that a named approver authorized this
exact action before it ran, that a downstream controller succeeded, or that any physical effect
occurred. This revision separates those last three as distinct claims with independent failure
behaviour, states the boundary of a shared action digest, and keeps a deterministic offline
policy-replay capability out of scope.</t>
</abstract>

</front>

<middle>

<section anchor="introduction"><name>Introduction</name>
<t>An AI agent books the flight, moves the money, or tells a machine on a factory floor to move an
object. Afterwards someone asks a plain question: <em>what actually happened, and who can prove it?</em></t>
<t>Today the answer usually comes from the party that ran the agent: its own logs, its own summary,
its own word. This creates a first-party evidence problem when an action is disputed.</t>
<t>This document defines a <strong>receipt</strong>: one small, signed record per governed agent action, describing what was
done, under which recorded principal class, with what verdict, and -- when the issuer records it --
under which policy. A bare receipt can be checked by anyone holding the issuing key; an enveloped
one is checked against that key <strong>and</strong> the key that signed the envelope, which need not be the same
(<xref target="payload-binding"></xref>). Either way the check needs no access to the agent, the vendor, or any running
service.
It is deliberately <em>narrow</em>. A receipt proves that this record was issued by that key and has not
been altered since. It does not prove that the agent was right, that its inputs were true, or that
the world changed the way the record suggests.</t>
<t>Being narrow is the point. A record that promises more than it can support is worse than no record,
because a relying party cannot tell where the support ends. This profile therefore states, in
normative language, the things a receipt does <strong>not</strong> establish -- and, where
the industry needs those things established, points at the separate documents that do it.</t>
<t><strong>Three documents, one system.</strong> This revision is written as one part of a shipped, measured
architecture, and a reader should be able to see the whole shape from here:</t>

<ul spacing="compact">
<li><strong>This profile -- the record.</strong> One signed record per action, hash-chained (each record carries
a fingerprint of the one before it, so a deletion <em>between two presented records</em> is visible --
a deletion from the end is not, which is what <xref target="chain-axes"></xref> is about), and carriable as a
Signed Statement for registration in a transparency log (<xref target="signed-statement"></xref>).</li>
<li><strong>The action digest <xref target="I-D.toraman-noa-action-digest"></xref>, published 14 August 2026 -- the binding.</strong>
A fixed, public way to compute one value from an approval and its exact execution attempt, so
that records made by parties who share no schema can be shown to commit to the same action
projection.</li>
<li><strong>Settlement evidence <xref target="I-D.toraman-noa-settlement-evidence"></xref>, published 14 August 2026 -- the
independent witness.</strong> For payments, a signed side artifact that lets a relying party check the
effect against a public ledger's own record, instead of against the paying party's assurance.</li>
</ul>
<t>The division of labour is the argument: the record says what the governed boundary decided or
dispatched, the digest says which action a later document is about, and the witness says what an
outside system observed. Each fails
on its own (<xref target="three-claims"></xref>), and no one of them is asked to carry the others' weight.</t>
<t>The gap this fills is a specification gap, not only a product one. Protocol specifications for agent
connectivity and orchestration, and the agent runtimes built on them, leave the <em>accountability</em>
layer -- a standardized, tamper-evident, issuer-authenticated record of an agent's actions -- open.
OWASP's MCP Top 10 lists &quot;Lack of Audit and Telemetry&quot; as MCP08:2025 <xref target="OWASP-MCP"></xref>. Several recent
efforts emit signed PERMIT/DENY <em>decision</em> receipts for
agent actions; what is missing is a single, SCITT-native, COSE-based profile for <strong>per-action</strong>
receipts that is (a) carriable as a Signed Statement for registration in a Transparency Service --
a log a third party can check for consistency -- and (b) honest about exactly what a receipt does
and does not prove.</t>
<t>This profile does not define a new signature container. It defines an application payload profile
carried in a COSE_Sign1 <xref target="RFC9052"></xref>, so the outer signature can be verified by conforming COSE
implementations that support the fully-specified Ed25519 algorithm identifier used by this profile.
<xref target="signed-statement"></xref> specifies the additional protected claims that make one an RFC 9943 Signed
Statement, and states plainly which of those the reference implementation does not yet emit.</t>
<t><strong>What this revision adds.</strong> Review on the SCITT mailing list in July 2026 found that the single
phrase &quot;real-world outcome&quot; in -00 hides three claims that a consumer must be able to fail
separately: whether a named person authorized this exact action <em>before</em> it ran, what a downstream
machine <em>reported</em> afterwards, and whether the intended <em>physical</em> effect actually occurred.
<xref target="external"></xref> and <xref target="authorization"></xref> pull them apart and require each to be reported as a result a
verifier renders, not a caveat a reader has to remember. <xref target="digest-boundary"></xref> draws the boundary
around the value that lets records about one action be joined at all. <xref target="changes"></xref> tells the whole
story of this revision -- what was asked, what was built and measured, and what the document now
guarantees -- and every change is listed there.</t>
<t>The base record <tt>noa.receipt/0.1</tt> is not modified: no member is added, removed, or re-typed. What
that guarantees is byte-level and nothing wider: every receipt, hash chain and checkpoint produced
under -00 still hashes to the same value, its signatures are still signatures over the same bytes,
and no linkage is invalidated. It does <strong>not</strong> follow that a -00 receipt is accepted by a -01
verifier -- acceptance is a separate question, answered in the next paragraph.</t>
<t>It does <strong>not</strong> guarantee that every -00 receipt is <em>valid</em> under -01, and the difference is worth
enumerating rather than glossing. This revision states validity conditions -00 did not: the verdict,
risk-class and governance-mode vocabularies are closed with a MUST-reject (<xref target="terminology"></xref>); the
object is closed against unknown members (<xref target="payload"></xref>); a timestamp must be a valid RFC 3339
date-time and not merely shape-correct (<xref target="optional-members"></xref>); <tt>id</tt> is bounded at 128 code points,
seven members must be non-empty strings and a present <tt>governance.compliance.verdict</tt> must be
<tt>ALLOW</tt> or <tt>DENY</tt> (<xref target="field-semantics"></xref>); <tt>sig.alg</tt> must be the exact literal <tt>ed25519</tt>
(<xref target="payload-binding"></xref>); and the cross-field rules of
<xref target="field-semantics"></xref> refuse combinations -00 permitted. A receipt carrying an out-of-vocabulary
verdict, an unknown member, an impossible instant, an empty or over-long identifier, or
contradictory simulation markers would have
passed a permissive -00 verifier and is malformed here. A verifier's NFC configuration moves the
verdict on non-NFC strings in both directions (<xref target="optional-members"></xref>).</t>
<t>For a document adopting this profile's chain convention by reference
(<xref target="relationship-to-other-work"></xref>), the practical statement is therefore: the <em>construction</em> is
unchanged; the <em>acceptance rules</em> are stricter.</t>

<section anchor="scope-and-the-adjacent-slot"><name>Scope and the adjacent slot</name>
<t>Two related SCITT-AI drafts occupy <em>different</em> slots: an EU AI Act Article 50 <em>disclosure</em> profile
(what content was generated and disclosed) and a <em>session-archive</em> format (a portable bundle of a
whole agent session). This profile is orthogonal to both: it defines the <strong>per-action signed
receipt</strong> -- one signed record per agent action, carried bare or in a COSE_Sign1 (<xref target="cose"></xref>), and
holding the action, the recorded principal class, the recorded verdict, and any policy identity the
issuer chose to record.</t>
<t>Three slots adjacent to this one are named here because this revision defines its boundaries
against them rather than against prose: a <em>reconciliation</em> layer that admits heterogeneous
attestations about one action and reports per-source verdicts <xref target="I-D.hillier-scitt-arp"></xref>; an
<em>executor-side admission boundary</em> that decides whether an action may be consumed
<xref target="I-D.schrock-action-evidence-boundary"></xref>; and a <em>correlation value</em> that lets records produced by
parties with no shared schema commit to the same action projection
<xref target="I-D.toraman-noa-action-digest"></xref>. None of the three is a dependency of this profile, and this
profile is not a dependency of them.</t>
</section>

<section anchor="relationship-to-other-work"><name>Relationship to other work</name>

<ul spacing="compact">
<li><strong>SCITT Architecture</strong> <xref target="RFC9943"></xref>: this is a Signed Statement profile; it adds AI-agent-action
semantics, not new transparency machinery. (-00 cited this as a work-in-progress draft; it was
published as an RFC in June 2026.)</li>
<li><strong>Signed decision receipts for machine-to-machine access control</strong> <xref target="ACTA"></xref>: a custom-JSON
receipt field catalogue covering the same ground in a custom JSON envelope; this profile is
SCITT-native (COSE_Sign1) where that format requires an adapter to register. A field-by-field
mapping between them changes no requirement here and is left to whoever needs to port one.</li>
<li><strong>draft-dawkins-scitt-ai-article50</strong> <xref target="AI-ART50"></xref>, <strong>draft-stone-aivs</strong> <xref target="AIVS"></xref>,
<strong>draft-marques-asqav-compliance-receipts</strong> <xref target="ASQAV"></xref>,
<strong>draft-nivalto-agentroa-route-authorization</strong> <xref target="AGENTROA"></xref>: adjacent work, none of it a
SCITT-native per-action receipt profile. The first two target regulatory disclosure and session
archives; the third is a compliance profile <em>of</em> signed action receipts, which overlaps this one
in subject and not in mechanism -- it is not a COSE/SCITT profile and defines no registration
path; the fourth authorizes agent routes rather than recording actions.</li>
<li><strong>draft-kamimura-scitt-vcp</strong> <xref target="SCITT-VCP"></xref>: a domain SCITT profile (algorithmic-trading audit)
whose structure this document follows.</li>
<li><strong>draft-wilder-scitt-physical-site-engage-receipt</strong> <xref target="PSER"></xref>: a physical-site engagement profile
that adopts the hash-chain convention of <xref target="hash-chaining-and-completeness"></xref> by reference. That
section's number, title and anchor are unchanged from -00. <strong>Two sentences of its text were
factually wrong in -00 and are corrected in this revision</strong>; <xref target="changes"></xref> gives the before and after,
and an implementer of <xref target="PSER"></xref> should read that entry before relying on -00's wording. Its new
subsection <xref target="chain-axes"></xref> adds Chain-Verifier-class requirements that -00 did not state: a document
that adopts Section 5's convention to <em>build</em> receipts is unaffected, while a <em>verifier</em> claiming
conformance to -01 takes on those obligations.</li>
<li><strong>The action digest and one of its consumers</strong> <xref target="I-D.toraman-noa-action-digest"></xref>,
<xref target="I-D.toraman-noa-settlement-evidence"></xref>, both published 14 August 2026: a domain-separated
correlation value -- one value computed under a fixed public rule, so two parties can tell they
mean the same action -- and a profile that carries it into an external system. Both are
referenced, neither is required; <xref target="digest-boundary"></xref> states what a receipt does and does not
contribute to such a construction.</li>
</ul>
</section>

<section anchor="requirements-language"><name>Requirements Language</name>
<t>The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;,
&quot;RECOMMENDED&quot;, &quot;NOT RECOMMENDED&quot;, &quot;MAY&quot;, and &quot;OPTIONAL&quot; in this document are to be interpreted
as described in BCP 14 <xref target="RFC2119"></xref> <xref target="RFC8174"></xref> when, and only when, they appear in all capitals, as
shown here.</t>
</section>

<section anchor="classes"><name>Actors</name>
<t>Requirements in this document are written in one of two forms. Most <strong>name their actor</strong> directly,
and most sections address a single actor and say so in their first sentence; where a section carries
requirements for more than one, each requirement names its own and the section preamble lists them.
The rest are written <strong>about an artifact</strong> -- &quot;a receipt MUST contain no member other than those
shown&quot;, &quot;<tt>sig.value</tt> MUST be canonical base64&quot;, &quot;the <tt>kid</tt> SHOULD be carried in the protected
header&quot; -- a shorthand used deliberately for schema and encoding constraints, because writing each
of them twice would bury them.</t>
<t>An artifact-form sentence binds the <strong>Producer</strong> that builds the artifact, at the strength written.
It does not, on its own, bind the verifier that receives it, and the two strengths are not
symmetric: a Producer MAY is a permission, and the corresponding verifier duty is to ACCEPT what
that permission allows, not to enjoy the same latitude. <xref target="cose"></xref> shows the shape — <tt>kid</tt> MAY be
carried in the unprotected header, and a verifier MUST resolve it from either bucket.</t>
<t>A verifier's disposition is therefore stated explicitly wherever the profile requires one: an
artifact-form MUST is paired with the rejection it implies, and an artifact-form SHOULD or MAY is
paired with the acceptance a verifier owes a conforming artifact that exercises the permission. An
artifact-form SHOULD is never, by itself, a ground for a verifier to reject. Where this document
states a Producer SHOULD and assigns no verifier disposition, none is required: the receiver's
behaviour is unconstrained and conformance is not tested on it.</t>
<t>A sentence that fits neither form is an editorial error rather than a hidden obligation. Two kinds of sentence deliberately carry no keyword and no obligation: the blocks marked
non-normative, and statements about what a <em>future revision</em> might do. Seven actors appear: three verifiers of increasing scope, and four parties that make, operate, register and
consume. A section states its actor in its first sentence where the whole
section is addressed to one; where a section mixes actors, each requirement names its own.</t>
<table>
<thead>
<tr>
<th>Actor</th>
<th>Holds / is given</th>
<th>Is required to</th>
</tr>
</thead>

<tbody>
<tr>
<td><strong>Producer</strong> (Issuer)</td>
<td>the signing key, and the facts it records</td>
<td>build receipts to <xref target="payload"></xref>, <xref target="field-semantics"></xref> and <xref target="canon-params"></xref>, and sign them per <xref target="payload-binding"></xref></td>
</tr>

<tr>
<td><strong>Receipt Verifier</strong></td>
<td>one receipt; a keyring; optionally an identity manifest</td>
<td>check that one object: shape, canonical form, the recomputed <tt>chain.hash</tt>, the signatures it is given, and -- when a manifest is supplied -- attribution</td>
</tr>

<tr>
<td><strong>Chain Verifier</strong></td>
<td>a presented set of receipts; a keyring; optionally a checkpoint and an identity manifest</td>
<td>everything above for each member, plus the set-level conditions of <xref target="chain-axes"></xref></td>
</tr>

<tr>
<td><strong>Evidence Verifier</strong> (not a conformance role in this revision)</td>
<td>all of the above, plus the separately signed artifacts of <xref target="external"></xref> and <xref target="authorization"></xref>, a trust configuration, and the consumer intent it was asked to answer</td>
<td>everything above, plus authorization, outcome, signer relationship and sufficiency for that intent -- described here, but NOT testable against this document, because those artifacts, that configuration and the result representation are all left undefined (<xref target="not-defined-here"></xref>). Nothing in this revision can be conformant or nonconformant <em>as an Evidence Verifier</em>; the passages addressed to it are analysis, and a future revision or companion must define its inputs and outputs before the role can carry conformance</td>
</tr>

<tr>
<td><strong>Transparency Service</strong></td>
<td>a submitted Signed Statement, and a means of resolving the registrant's verification key</td>
<td>apply the Registration Policy of <xref target="registration"></xref>, which states which of its checks are conditional on what it can resolve</td>
</tr>

<tr>
<td><strong>Relying Party</strong></td>
<td>a verifier's result, and any receipts it chose to keep</td>
<td>read that result within the limits <xref target="external"></xref> states; where it compares, indexes or alerts on receipt fields itself, apply the byte-comparison and parsing rules of <xref target="field-semantics"></xref> and <xref target="security-considerations"></xref></td>
</tr>

<tr>
<td><strong>Deployment</strong> (Operator)</td>
<td>the running system: producers, verifiers, keys, and what is presented or registered</td>
<td>choose the configurations this profile leaves open -- identifier syntax, NFC mode, resource limits, checkpoint issuance and freshness, what is registered -- and present complete chains for verification</td>
</tr>
</tbody>
</table><t>Three rules govern the table, and each closes a way of misreading it.</t>

<ul spacing="compact">
<li><strong>Inputs are part of the obligation, and an input may be empty.</strong> A requirement addressed to an
actor assumes the inputs in its row. Where a requirement needs an input that may be absent -- a
checkpoint, an identity manifest -- it is written conditionally (&quot;when a checkpoint is supplied
... MUST&quot;), and an actor that was not given the input has not failed the requirement. The keyring
is different from those two and the difference matters: it is a listed input of both verifier
roles, and an implementation that accepts it but is handed an <strong>empty</strong> one is still that actor,
still conformant, and simply cannot authenticate anything. That is the state <xref target="chain-axes"></xref> means
by &quot;holds no keyring&quot; -- an accepted input with nothing in it, not an implementation that refuses
the input and thereby declines the role.</li>
<li><strong>A wider verifier inherits the narrower one's checks, and nothing else.</strong> A Chain Verifier
performs every Receipt-Verifier check on each receipt in the set, because the set-level
conditions are meaningless over objects that are not individually valid. It does not inherit
Evidence-Verifier obligations, and no verifier inherits Producer obligations.</li>
<li><strong>One role per claim of conformance.</strong> An implementation conforms to the role whose obligations it
performs. Verifying only receipts is Receipt-Verifier conformance and nothing more; a Receipt
Verifier that cannot detect a chain-level condition is conformant, because it was never given the
set.</li>
</ul>
</section>

<section anchor="not-defined-here"><name>What this profile does not define</name>
<t>Several requirements in <xref target="who-signed"></xref>, <xref target="external"></xref>, <xref target="authorization"></xref> and <xref target="digest-boundary"></xref> are
addressed to the <strong>Evidence Verifier</strong> over objects whose wire formats are <strong>not</strong> specified here. (The attribution
rule of <xref target="identity-binding"></xref> is not among them: it is a Receipt-Verifier requirement, conditional on
a manifest being supplied.) This
section lists them once, so that no reader mistakes a boundary for an omission and no implementer
mistakes an unimplementable MUST for an interoperability requirement:</t>

<ul spacing="compact">
<li>the <strong>identity manifest</strong> (binding <tt>agent.id</tt> to authorized key identifiers): its encoding,
distribution, signing, versioning and revocation rules;</li>
<li>the <strong>approval artifacts</strong> of <xref target="authorization"></xref> -- hold, decision, execution grant, consumption
and uncertainty records -- and therefore grant identifiers, single-use nonces, audiences and
expiries;</li>
<li>the <strong>outcome artifacts</strong> of <xref target="external"></xref> -- controller-reported outcome and physical observation;</li>
<li>the <strong>trust configuration</strong> from which a signer relationship is derived, and the digests that
identify it;</li>
<li>a <strong>shared action digest</strong>, defined separately in <xref target="I-D.toraman-noa-action-digest"></xref>;</li>
<li>the <strong>result schema</strong> in which a verifier reports any of the above. This revision names result
<em>tokens</em> in several places -- the six chain conditions and the two named checkpoint-axis
outcomes, the four absence results, the
relationship vocabulary, the intents, <tt>UNTRUSTED</tt>, and the per-receipt outcomes <tt>VALID</tt>,
<tt>MALFORMED</tt> and <tt>TAMPERED</tt> -- so that implementations describing the same finding use the same
word. <strong>Two</strong> results are deliberately <strong>not</strong> tokenised in this revision: the conflict result of
<xref target="purpose"></xref> and the unproven-authorization result of <xref target="authorization"></xref>. A third case that might look
like one is not: the indeterminate comparison of <xref target="digest-boundary"></xref> is reported with
<xref target="absence"></xref>'s <tt>INDETERMINATE</tt>, which is a token this document defines. An implementation MAY report
the two untokenised results under a name of its own; what it MUST NOT do is present that name as
this profile's vocabulary, since a second implementation reading this document will not have
chosen the same one. Whether they should be tokenised here is left to the working group
(<xref target="open-questions"></xref>). This revision also does not define how any
of them are carried, so two implementations will not be wire-compatible in their outputs until
something does.</li>
</ul>
<t>Where such an object is absent, the corresponding requirement is not a requirement a conforming
implementation can fail: it has nothing to evaluate, and <xref target="external"></xref> states what a bare receipt
cannot establish. Deployments that need these claims need those artifact definitions, which live
in the companion documents and in the reference implementation cited in
<xref target="implementation-status"></xref> -- not here.</t>
</section>
</section>

<section anchor="terminology"><name>Terminology</name>
<t><em>Definitions bind every actor. Where an entry carries a requirement it names its own actor: the
entries below address the <strong>Producer</strong> (what it MUST emit), the <strong>Receipt Verifier</strong> (what it MUST
reject), the <strong>Relying Party</strong> (what it MUST NOT read into a value) and the <strong>Deployment</strong> (what it
selects).</em></t>

<ul spacing="compact">
<li><strong>Action Receipt:</strong> the application-layer record defined by this document -- a signed record
attesting one agent action, presented either <strong>bare</strong> (the canonical JSON payload plus its native
signature) or <strong>enveloped</strong> in a COSE_Sign1 (<xref target="cose"></xref>). Both presentations are defined here. It
becomes an RFC 9943 <em>Signed Statement</em> in the enveloped form, when that envelope also carries the
protected claims of <xref target="signed-statement"></xref> -- necessary conditions, with the boundary noted there.
<strong>An Action Receipt is NOT a SCITT Receipt.</strong> The two documents that define the other object use it
at different widths, and the difference matters. <xref target="RFC9942"></xref>, Section 3 defines a <em>Receipt</em>
generically: a COSE Single Signer Data Object carrying one or more verifiable-data-structure
proofs -- inclusion, consistency, non-inclusion -- with no Transparency Service, Signed Statement
or registration event required. <xref target="RFC9943"></xref> applies that object to the SCITT case, where the proof
is issued by a Transparency Service over a registered Signed Statement. This document calls the
SCITT case a <strong>Transparency Receipt</strong> wherever it appears (<xref target="registration"></xref>), meaning RFC 9943's use
of RFC 9942's object rather than the general class. The two are different objects with
different producers: a Transparency Service issues the Transparency Receipt; the agent's governed
boundary issues the Action Receipt. Where this document says &quot;receipt&quot; without qualification
inside a SCITT context, it means the Action Receipt, and it says so.</li>
<li><strong>Principal class:</strong> the <em>category</em> of authority the issuer recorded the action as being taken on
behalf of. <tt>agent.principal</tt> carries one of exactly four enumerated values -- <tt>HUMAN</tt>, <tt>SERVICE</tt>,
<tt>POLICY</tt>, <tt>SANDBOX_SIM</tt> -- and nothing else. It is a class marker, not an identity: it names no
person, service or key, and a verifier MUST NOT read an identity out of it. <tt>SANDBOX_SIM</tt> marks a
simulated action.</li>
<li><strong>Lifecycle verdict:</strong> the value of <tt>governance.verdict</tt>, drawn from a closed set of exactly seven
uppercase tokens. (-00 called this a <em>terminal</em> verdict. It is not: <tt>DEFERRED</tt> records that no
decision has been made, and <tt>ALLOWED</tt> records a permission a later attempt may still consume. The
name is corrected here rather than carried forward.) The tokens are: <tt>ALLOWED</tt>, <tt>BLOCKED</tt>, <tt>DEFERRED</tt>, <tt>EXECUTED</tt>, <tt>FAILED</tt>, <tt>ROLLED_BACK</tt>,
<tt>SIMULATED</tt>. The comparison is exact and case-sensitive; no other value is valid, and a verifier
MUST reject a receipt whose verdict is outside this set as malformed rather than treating it as
an unrecognized extension. Two of the seven carry the normative weight used elsewhere in this
document: <tt>ALLOWED</tt> records that the action was permitted at the governed boundary, and is the
verdict from which an authorization descends -- an Evidence Verifier MUST NOT accept a receipt
carrying any other verdict as the authorization for an action (<xref target="authorization"></xref>); <tt>EXECUTED</tt> records that
the action was dispatched or attempted there, and nothing about what happened downstream
(<xref target="three-claims"></xref>). <tt>EXECUTED</tt> covers both &quot;dispatched&quot; and &quot;attempted&quot;: those are different
states, this field does not separate them, and neither means the action took effect. The
remaining five record a refusal (<tt>BLOCKED</tt>), a decision not yet made (<tt>DEFERRED</tt>), a failure at
the governed boundary (<tt>FAILED</tt>), an action the issuer records as compensated (<tt>ROLLED_BACK</tt> --
the profile can check neither the original effect nor the compensation), and a simulation
(<tt>SIMULATED</tt>).</li>
<li><strong>Simulation markers, and the rule that keeps them honest:</strong> three members can say something
about simulation -- <tt>agent.principal == SANDBOX_SIM</tt>, <tt>governance.verdict == SIMULATED</tt>, and
<tt>governance.sandboxed</tt>. A producer MUST make them agree: if any one of them marks the action as
simulated, <tt>governance.sandboxed</tt> MUST be <tt>true</tt>, and a verifier MUST reject a receipt in which
<tt>governance.sandboxed</tt> is <tt>false</tt> while either of the other two marks a simulation. The converse
is deliberately permitted -- a sandboxed run may carry an ordinary lifecycle verdict -- because a
deployment may exercise the real decision path inside a sandbox. What the rule buys is exactly
one thing: a receipt cannot say both at once. An issuer that consistently marks a real action as
simulated, or a simulation as real, produces a receipt no verifier can contradict -- the record is
the issuer's account of its own run, and this profile has no external truth source for it
(<xref target="external"></xref>). Without the rule even the internal contradiction would pass, and a reader could not
tell which of two markers to believe.</li>
<li><strong>Risk class:</strong> the value of <tt>action.riskClass</tt>, drawn from a closed set of exactly five uppercase
tokens: <tt>LOW</tt>, <tt>MEDIUM</tt>, <tt>HIGH</tt>, <tt>CRITICAL</tt>, <tt>IRREVERSIBLE</tt>. The comparison is exact and
case-sensitive, and a verifier MUST reject a receipt carrying any other value as malformed rather
than treating it as an unrecognized extension. The value is issuer-assigned: it records the class
the issuing boundary applied to this action, not an independent assessment of danger, and a
relying party MUST NOT read it as one. A <strong>Deployment</strong> that gates approval authority on risk -- for
example requiring a higher approver tier for <tt>CRITICAL</tt> and <tt>IRREVERSIBLE</tt> than for <tt>HIGH</tt> --
selects that tier from this field, which is why an issuer that understates it weakens its own
approval requirement rather than the verifier's checks (<xref target="authorization"></xref>).</li>
<li><strong>Governance mode:</strong> the value of <tt>governance.mode</tt>, drawn from a closed set of exactly four
lowercase tokens: <tt>off</tt>, <tt>shadow</tt>, <tt>approvals_on</tt>, <tt>on</tt>. The same exactness applies: the tokens
are lowercase, the comparison is case-sensitive, and any other value MUST be rejected as
malformed. The field records which governance posture the issuing boundary was operating under
when the receipt was produced. This profile assigns it no authorization semantics: a verifier
MUST NOT infer from any value that an approval was obtained, that a policy was enforced, or that
an action was permitted -- those are <tt>governance.verdict</tt> and the artifacts of <xref target="authorization"></xref>.</li>
<li><strong>Policy:</strong> a decision rule set with a stable content hash (<tt>policyHash</tt>) used as its published
identity. A deployment will normally want it to be deterministic and side-effect-free -- that is
what makes a commitment to it worth anything -- but this profile specifies no way to evaluate a
policy and therefore no way for a verifier to check either property; the words are a description
of intended practice, not a requirement it can test. A receipt carries that hash only when the optional
commitment of <xref target="optional-members"></xref> is present; <tt>governance.ruleId</tt> alone is a label, not a
commitment.</li>
<li><strong>Keyring:</strong> the set of verification keys a verifier trusts for this evaluation, each addressed
by a key identifier. It is supplied to the verifier out of band; this profile defines neither its
encoding nor how a verifier comes to trust it, and every statement here about a &quot;trusted key&quot;
means &quot;a key in the keyring the verifier was given&quot;.</li>
<li><strong>Identity manifest:</strong> an out-of-band, operator-vouched binding of <tt>agent.id</tt> to its
authorized key identifier(s) (<tt>kid</tt>), used to authenticate <em>which agent</em> acted, not merely
that a trusted key signed.</li>
<li><strong>Genesis-bound chain:</strong> a per-scope hash chain whose checkpoint authority is bound to the
<em>opener</em> of the chain -- precisely, to the keys an identity manifest authorizes for the <tt>agent.id</tt>
of the receipt at sequence 0 (<xref target="checkpoint"></xref>), which is a set rather than one key -- so that a
re-heading attacker cannot substitute a foreign checkpoint. Without a manifest there is no such
binding and the property does not hold.</li>
<li><strong>Transparency Service (TS):</strong> as defined by the SCITT Architecture <xref target="RFC9943"></xref>.</li>
<li><strong>Pre-execution authorization:</strong> a statement, made <em>before</em> an action runs, that a named,
accountable principal approved <em>that exact action</em> under stated bounds. It is a statement about
what may happen.</li>
<li><strong>Post-execution evidence:</strong> a statement, made <em>after</em> an action was dispatched, about what some
party observed. It is a statement about what did happen, under that party's own semantics.</li>
<li><strong>Controller-reported outcome:</strong> the terminal status a downstream system reported for a request,
under that system's own semantics. It is evidence about a <em>report</em>, not about an effect.</li>
<li><strong>Physical completion:</strong> whether the intended real-world effect actually occurred, as observed by
an identified witness under a criterion fixed before execution.</li>
<li><strong>Consumer intent (purpose):</strong> the question a relying party is asking of a presented record -- for
example &quot;is this an authorization?&quot;, &quot;is this independent outcome proof?&quot;. Cryptographic validity
is evaluated against the artifact; sufficiency is evaluated against the intent, and the two
results are separate (<xref target="purpose"></xref>).</li>
<li><strong>Authorization signer / outcome signer:</strong> the key that signed the authorization record, and the
key that signed an outcome statement about the same action. Their <em>relationship</em> is a property a
verifier derives, not a property either signer may assert about itself (<xref target="who-signed"></xref>).</li>
<li><strong>Shared action digest:</strong> a value, computed under a pinned construction, that lets records
produced by different parties be joined as committing to the same <em>action projection</em>.
Correlation only, and never a statement that the action was authorized; see <xref target="digest-boundary"></xref>.</li>
</ul>
</section>

<section anchor="payload"><name>Receipt structure (payload)</name>
<t><em>Every requirement below names its actor: construction rules bind the <strong>Producer</strong>, validation
rules the <strong>Receipt Verifier</strong>, the tenant rule of <xref target="optional-members"></xref> the <strong>Chain Verifier</strong>, and
the comparison rules for identifiers and timestamps the <strong>Relying Party</strong> (<xref target="classes"></xref>).</em></t>
<t>The receipt payload is the JSON Canonicalization Scheme (JCS) <xref target="RFC8785"></xref> serialization of a JSON
object. A Producer MUST emit integers only (no floating-point), and a Receipt Verifier MUST reject a
payload carrying a non-integer number. String normalization is specified in
<xref target="canon-params"></xref>: producers MUST emit NFC, and verifiers MUST NOT normalize. A verifier MAY be
configured to <em>refuse</em> a non-NFC receipt, but a verifier that accepts one MUST NOT repair it, and
in either mode a <strong>Relying Party</strong> MUST NOT infer that a verified receipt's strings are NFC
(<xref target="optional-members"></xref>). The fields:</t>

<artwork><![CDATA[{
  "spec":   "noa.receipt/0.1",
  "id":     "<receipt id>",
  "ts":     "<RFC 3339 date-time>",
  "scope":  { "tenant": "<id>"?, "chain": "<chain id>" },
  "agent":  { "id": "<agent id>",
              "model": "<vendor/model|null>"?,
              "principal": "HUMAN|SERVICE|POLICY|SANDBOX_SIM" },
  "action": { "id": "<tool/action id>",
              "canonical": "<risk-table key>",
              "riskClass": "LOW|MEDIUM|HIGH|CRITICAL|IRREVERSIBLE",
              "paramsHash": "sha256:<hex>|hmac-sha256:<hex>",
              "reversible": <bool>,
              "rollbackRef": "<id>|null"? },
  "governance": { "mode": "<governance mode>",
                  "verdict": "<lifecycle verdict>",
                  "sandboxed": <bool>,
                  "ruleId": "<id>|null"?,
                  "approval": { "by": "<approver>",
                                "at": "<RFC 3339 date-time>" }|null?,
                  "compliance": { "policyHash": "sha256:<hex>",
                                  "readSetHash": "sha256:<hex>",
                                  "inputsHash": "sha256:<hex>",
                                  "verdict": "ALLOW|DENY"? }|null? },
  "chain":  { "seq": <int>,          ; >= 0
              "prevHash": "sha256:<hex>|null",
              "hash": "sha256:<hex>" },
  "sig":    { "alg": "ed25519",
              "kid": "<key id>",
              "value": "<base64 Ed25519 signature>" }
}
]]></artwork>
<t><strong>The object is closed.</strong> A receipt MUST contain no member other than those shown here, at any
level; a verifier MUST reject an unknown member rather than ignore it. Extension by adding members
is therefore not possible in <tt>noa.receipt/0.1</tt> -- that is what makes the hash construction of
<xref target="payload-binding"></xref> reproducible across implementations, and it is why <xref target="separate-artifacts"></xref> carries
new claims in separate artifacts instead.</t>
<t>A <tt>?</tt> marks a member that MAY be absent; every other member is REQUIRED, including <tt>sig</tt>. No member
of this record is added, removed, or re-typed by this revision; -00's figure omitted <tt>sig</tt> and did
not mark the optional members, and both omissions are corrected here to match the shipped schema
(<xref target="optional-members"></xref>).</t>
<t><strong>A Producer MUST NOT place raw prompts, tool arguments or secrets in any member</strong>, and the
commitments this record carries are hashes rather than values. That is a rule about what a producer
may write, not a property the format enforces, and its limit is worth stating precisely -- several
members (<tt>id</tt>, <tt>agent.id</tt>, <tt>agent.model</tt>,
<tt>action.id</tt>, <tt>action.canonical</tt>, <tt>governance.ruleId</tt>, <tt>governance.approval.by</tt>,
<tt>action.rollbackRef</tt>) are constrained in length and emptiness at most (<xref target="field-semantics"></xref>) and in
content not at all, so a producer can put anything in them and no verifier can detect it. The prohibition binds producers; it is not a property a verifier can check,
and <xref target="privacy-considerations"></xref> says what follows. <tt>paramsHash</tt> MAY be <tt>hmac-sha256:&lt;hex&gt;</tt> with a
tenant-scoped key where a plain SHA-256 over a low-entropy value (an amount, an id, a boolean)
would be guessable.</t>
<t><strong>These commitments are per-producer, and this profile does not make them comparable.</strong> Neither
<tt>action.paramsHash</tt> nor the three hashes of <tt>governance.compliance</tt> has a preimage construction
defined here: what is hashed, in what serialization, under what domain separation, is the
producer's own contract with whoever will later open the commitment. Two conforming producers given
identical parameters can therefore compute different values -- by design rather than by omission.
<xref target="digest-boundary"></xref> explains why a cross-producer join key needs a construction identifier before
any two parties may compare digests, and names the companion that defines one. A verifier MUST NOT
compare these values across producers, and MUST NOT treat equality of them as evidence of anything.
For the keyed form the same reasoning cuts harder: the key is a tenant secret, so an offline third
party cannot check an <tt>hmac-sha256</tt> commitment at all, and this profile defines no key identifier,
rotation rule or distribution mechanism for it. A producer that rotates that key can still open
earlier commitments for as long as it retains the retired key -- and cannot once it destroys it.
Which of those happens is a deployment's key-management decision, not something this profile
requires or can check; what it means is that the openability of a keyed commitment depends on a
practice outside the record.</t>
<t><tt>chain.hash</tt> is <tt>&quot;sha256:&quot;</tt> followed by the lowercase hex of SHA-256 over the JCS serialization of
the receipt with <tt>chain.hash</tt> and <tt>sig.value</tt> removed; <xref target="payload-binding"></xref> pins that construction
byte-exactly, together with the signature computed over the same digest. Receipts in one <tt>scope.chain</tt> are linked by <tt>prevHash</tt>; <tt>prevHash</tt> is <tt>null</tt> only at the
genesis receipt (<tt>seq == 0</tt>).</t>

<section anchor="field-semantics"><name>Field semantics and cross-field rules</name>
<t><em>The cross-field rules address the <strong>Receipt Verifier</strong>; the emission obligations they imply address
the <strong>Producer</strong>. The tenant rule of <xref target="optional-members"></xref> addresses the <strong>Chain Verifier</strong>.</em></t>
<t>The figure above shows shapes; this subsection fixes the meanings a verifier may rely on, and the
combinations it must refuse. Where a member's meaning is the issuer's to choose, that is said
plainly rather than left to be inferred.</t>
<t><strong>Notation.</strong> In the figure, <tt>null</tt> denotes the JSON null literal, never the four-character string
<tt>&quot;null&quot;</tt>; a quoted alternative such as <tt>&quot;sha256:&lt;hex&gt;|null&quot;</tt> means &quot;either that string form or JSON
null&quot;. Angle brackets mark a value the issuer supplies.</t>
<t><strong>The figure's types are normative.</strong> A Receipt Verifier MUST reject a receipt in which a member's
JSON type differs from the one shown, rather than coerce it: <tt>&lt;bool&gt;</tt> means the JSON literals <tt>true</tt>
or <tt>false</tt> and never the strings <tt>&quot;true&quot;</tt>/<tt>&quot;false&quot;</tt> or the numbers 1/0; <tt>&lt;int&gt;</tt> means a JSON number
that is an integer within the range <xref target="canon-params"></xref> fixes; every other member is a string except
where <tt>null</tt> is shown as an alternative. Three shapes are fixed exactly, because a later comparison
depends on them:</t>

<ul spacing="compact">
<li><tt>chain.seq</tt> MUST be an integer and MUST NOT be negative;</li>
<li><tt>chain.hash</tt>, <tt>chain.prevHash</tt> when not <tt>null</tt>, and each of <tt>governance.compliance</tt>'s
<tt>policyHash</tt>, <tt>readSetHash</tt> and <tt>inputsHash</tt> MUST be <tt>&quot;sha256:&quot;</tt> followed by exactly 64 lowercase
hexadecimal digits;</li>
<li><tt>action.paramsHash</tt> MUST be that same form or <tt>&quot;hmac-sha256:&quot;</tt> followed by exactly 64 lowercase
hexadecimal digits.</li>
</ul>
<t>Uppercase hexadecimal is not an accepted spelling of any of them. These are byte-level rules, not
presentation preferences: the digest is inside the signed payload, so two spellings of one digest
are two different receipts.</t>
<t><strong><tt>spec</tt>.</strong> The exact string <tt>noa.receipt/0.1</tt>. A verifier MUST reject any other value rather than
attempt a best-effort parse: this profile defines no version negotiation, no downgrade rule, and no
forward-compatibility contract, and a future wire version will carry a different <tt>spec</tt> and its own
signing domain (<xref target="separate-artifacts"></xref>).</t>
<t><strong><tt>id</tt>, and identifiers generally.</strong> <tt>id</tt> identifies this receipt within its issuer's own naming.
This profile defines no namespace, no uniqueness requirement across issuers, and no collision
procedure -- and the same is true of <tt>scope.chain</tt>, <tt>agent.id</tt>, <tt>action.id</tt>, <tt>action.canonical</tt>,
<tt>governance.ruleId</tt>, <tt>action.rollbackRef</tt> and <tt>governance.approval.by</tt>. Two issuers may use the same
string for different things. A relying party that treats any of them as globally meaningful MUST
supply the namespace itself, typically by scoping to the issuing key; equality of these strings
across receipts from different issuers establishes nothing (<xref target="security-considerations"></xref>).</t>
<t><strong>Two syntactic bounds are pinned, because every implementation already enforces them and no
document said so.</strong> Both bind the <strong>Producer</strong> on construction and the <strong>Receipt Verifier</strong> on
validation:</t>

<ul spacing="compact">
<li><tt>id</tt> MUST be at most 128 Unicode code points, and a verifier MUST reject a longer one. The unit
is code points, not UTF-16 code units: an identifier of 65 astral characters is 65 code points
and 130 code units, so a verifier counting units rejects a receipt a verifier counting points
accepts, on identical signed bytes.</li>
<li>Seven members MUST be non-empty strings, and a verifier MUST reject a receipt in which any of
them is the empty string: <tt>id</tt>, <tt>scope.chain</tt>, <tt>agent.id</tt>, <tt>action.id</tt>, <tt>action.canonical</tt>,
<tt>sig.kid</tt> and <tt>sig.value</tt>. <tt>governance.approval.by</tt> is deliberately not among them -- it is an
opaque label this profile never authenticates (<xref target="who-signed"></xref>), and forbidding one of its values
would suggest the others mean something. The checkpoint object of <xref target="checkpoint"></xref> carries the same
rule for three further members, which is ten string boundaries across the two objects.</li>
</ul>
<t><strong><tt>ts</tt>.</strong> The time the <em>issuer recorded the receipt</em>. It is not the time a decision was taken, nor
the time an action was attempted, nor an observation time, and it is signer-chosen: nothing in this
profile makes it checkable (<xref target="optional-members"></xref>).</t>
<t><strong><tt>action.canonical</tt>.</strong> A key into the issuer's own risk table -- the stable name under which the
issuer classifies this kind of action. The table is not defined here, has no registry, and is not
comparable across issuers; <tt>action.id</tt> names the specific tool or operation invoked.</t>
<t><strong>Cross-field rules a verifier MUST enforce.</strong> These are the combinations that would let one record
say two things:</t>

<ul spacing="compact">
<li><tt>governance.sandboxed</tt> MUST be <tt>true</tt> whenever <tt>agent.principal</tt> is <tt>SANDBOX_SIM</tt> or
<tt>governance.verdict</tt> is <tt>SIMULATED</tt> (<xref target="terminology"></xref>);</li>
<li><tt>action.rollbackRef</tt> MUST be absent or <tt>null</tt> unless <tt>action.reversible</tt> is <tt>true</tt>, and a
<tt>governance.verdict</tt> of <tt>ROLLED_BACK</tt> MUST be accompanied by <tt>action.reversible: true</tt>;</li>
<li>where <tt>governance.compliance.verdict</tt> is present it records the policy evaluation's own result,
which is a different question from <tt>governance.verdict</tt>; the two MUST NOT be read as one, and a
verifier MUST NOT resolve a disagreement between them by preferring either -- it reports both. The
member is a closed two-value set: where present it MUST be exactly <tt>ALLOW</tt> or <tt>DENY</tt>, and a
verifier MUST reject any other value rather than carry an unrecognized string through into a
report a relying party will read.</li>
</ul>
<t><strong>What no rule constrains, stated so nobody assumes it.</strong> <tt>governance.approval</tt> may accompany any
lifecycle verdict, and its <tt>at</tt> may be later than <tt>ts</tt>: this profile places no temporal invariant on
them, because a signer-chosen timestamp cannot establish that an approval preceded an action
(<xref target="who-signed"></xref>). A verifier MUST NOT infer ordering from these fields.</t>
</section>

<section anchor="canon-params"><name>Canonicalization parameters</name>
<t>A digest is only interoperable if the parameters that produced it are pinned. Naming a
canonicalization (&quot;canonical JSON&quot;) without pinning its parameters is the failure mode that does
not announce itself: two implementations each behave correctly under their own reading and
produce different bytes, or -- worse -- the same bytes for inputs that should differ. This section
therefore pins every parameter of the construction above, normatively and exhaustively <strong>for the
receipt object as a whole</strong>. One carve-out belongs next to the claim rather than three sections
away: the encoding of the <tt>action</tt> sub-object is not fixed <em>for a party computing its own digest
over that sub-object</em>, because an optional member may be absent or present-as-null and the two
produce different bytes (<xref target="digest-boundary"></xref>). Inside a receipt that choice is signed and therefore
settled; across producers computing an independent digest it is not.</t>
<table>
<thead>
<tr>
<th>Parameter</th>
<th>Value for <tt>noa.receipt/0.1</tt></th>
</tr>
</thead>

<tbody>
<tr>
<td>Serialization</td>
<td>JCS <xref target="RFC8785"></xref></td>
</tr>

<tr>
<td>Member sort</td>
<td>UTF-16 code unit (RFC 8785); NOT Unicode code point -- the two diverge for astral characters, which encode as surrogates <tt>D800</tt>-<tt>DBFF</tt> and sort BEFORE <tt>E000</tt>-<tt>FFFF</tt></td>
</tr>

<tr>
<td>Number rendering</td>
<td>RFC 8785; integers only, <tt>abs(n) &lt;= 2^53 - 1</tt>; non-integer and non-finite values MUST be rejected</td>
</tr>

<tr>
<td>Unicode normalization</td>
<td>NONE applied by the canonicalizer, ever. Producers MUST emit NFC (<xref target="UAX15"></xref>); verifiers MUST NOT normalize before hashing. A verifier MAY refuse a non-NFC receipt outright (see <xref target="optional-members"></xref>); what it MUST NOT do is silently repair one</td>
</tr>

<tr>
<td>String escaping</td>
<td>RFC 8785: <tt>&quot;</tt> (U+0022) and <tt>\</tt> (U+005C) escaped; the short forms <tt>\b</tt> <tt>\t</tt> <tt>\n</tt> <tt>\f</tt> <tt>\r</tt> used where defined; every other control character below U+0020 escaped as <tt>\u00xx</tt>; every other code point emitted literally as UTF-8</td>
</tr>

<tr>
<td>Unpaired surrogates</td>
<td>MUST be rejected (they would collapse to U+FFFD at the UTF-8 hashing step, mapping distinct inputs onto one digest)</td>
</tr>

<tr>
<td>Duplicate member names</td>
<td>MUST be rejected at the parse boundary, not resolved last-wins</td>
</tr>

<tr>
<td>Forbidden member names</td>
<td><tt>__proto__</tt>, <tt>prototype</tt> and <tt>constructor</tt> MUST be rejected at the parse boundary, in every JSON document a verifier parses -- receipt, keyring, identity manifest, checkpoint -- and not filtered after parsing</td>
</tr>

<tr>
<td>Digest</td>
<td>SHA-256 (<xref target="FIPS180-4"></xref>), lowercase hex, <tt>sha256:</tt> prefixed. <tt>action.paramsHash</tt> MAY instead be HMAC-SHA-256 (<xref target="RFC2104"></xref>) under a tenant-scoped key, written <tt>hmac-sha256:&lt;hex&gt;</tt></td>
</tr>

<tr>
<td>Timestamps</td>
<td>RFC 3339 <xref target="RFC3339"></xref> <tt>date-time</tt>, with <tt>Z</tt> or a numeric offset -- the accepted spellings are pinned in <xref target="optional-members"></xref>, which permits both. A verifier MUST reject a value that is not a valid RFC 3339 <tt>date-time</tt>, and valid syntax still does not make a timestamp truthful</td>
</tr>

<tr>
<td>Domain separation</td>
<td>the receipt signature is over the UTF-8 bytes of <tt>&quot;NOA-Receipt-v0.1-sig:&quot;</tt> followed by the raw SHA-256 of the hash input, pinned byte-exactly in <xref target="payload-binding"></xref>; the checkpoint signature uses a distinct tag (<xref target="checkpoint"></xref>), so neither can be replayed as the other</td>
</tr>
</tbody>
</table><t>The forbidden-name rule is a parse rule rather than a schema rule, and the difference is the whole
point of it: in several host languages, materializing a member with one of those names mutates the
object being built, so a document carrying one can change how the verifier's own checks behave
before any schema runs. A receipt could not carry them in any case, since the object is closed
(<xref target="payload"></xref>) -- but the keyring, identity manifest and checkpoint a verifier also parses are not
closed by this profile, and the parse boundary is where all four documents meet the same rule.</t>
<t>On the outer COSE path, verifiers MUST NOT normalize, re-order, or re-render a received payload
before verifying it: the bytes as received are the bytes that were signed. On the derived path of
<xref target="payload-binding"></xref> the verifier does re-serialize, by construction -- it must, since two members
are omitted -- and this table is exactly the rule that makes that re-serialization reproduce the
producer's bytes.</t>
<t><tt>action.paramsHash</tt> is a per-producer commitment to that producer's own parameter set. It is NOT
a cross-producer correlation key and MUST NOT be treated as a shared action digest: it does not
commit to the tenant, chain, authorization, execution attempt, or nonce, and it may legitimately
repeat across retries of the same action. <xref target="digest-boundary"></xref> states what a construction must carry
before any two parties may compare digests at all.</t>
</section>

<section anchor="optional-members"><name>Optional members, and what their absence means</name>
<t>Seven members of <xref target="payload"></xref> MAY be absent: <tt>scope.tenant</tt>, <tt>agent.model</tt>, <tt>action.rollbackRef</tt>,
<tt>governance.ruleId</tt>, <tt>governance.approval</tt>, <tt>governance.compliance</tt>, and -- inside a present
<tt>compliance</tt> -- its <tt>verdict</tt>. Absence is a producer's version or configuration choice; it is never,
by itself, evidence of tampering, and a verifier MUST NOT convert it into one. Five of them --
<tt>agent.model</tt>, <tt>action.rollbackRef</tt>, <tt>governance.ruleId</tt>, <tt>governance.approval</tt> and
<tt>governance.compliance</tt> -- may also be present-as-<tt>null</tt>, which a verifier MUST treat as the value
<tt>null</tt> and not as absence: the two encodings produce different canonical bytes and therefore
different hashes and signatures.</t>
<t>The two whose absence changes what a verifier may conclude are singled out below; the rest are
ordinary optional fields carrying no security consequence beyond that rule.</t>

<ul spacing="compact">
<li><strong><tt>scope.tenant</tt></strong> MAY be absent, and the security-bearing rule is stated first (this one is
<strong>Chain-Verifier-class</strong>, since it compares receipts to one another): <strong>a conforming Chain
Verifier MUST reject a transition between two present, unequal <tt>scope.tenant</tt> values within
one <tt>scope.chain</tt>.</strong> One chain belongs to one tenant, so a change of tenant inside it is a splice
of two histories rather than a version change. Absence then carries forward rather than
resetting: a verifier MUST NOT treat an absent <tt>scope.tenant</tt> as a fresh start, so
<tt>A -&gt; absent -&gt; B</tt> is the same cross-tenant splice as <tt>A -&gt; B</tt> and MUST receive the same verdict,
while <tt>A -&gt; absent -&gt; A</tt> (the same tenant resuming) and <tt>absent -&gt; A</tt> (enrichment) remain valid.
Without the carry-forward rule, an optional field becomes a splice channel.</li>
<li><strong><tt>governance.compliance</tt></strong> MAY be present. When present it carries a commitment to the policy's
published identity (<tt>policyHash</tt>), to the policy's closed read-set (<tt>readSetHash</tt>), and to the
recorded decision inputs (<tt>inputsHash</tt>) -- hashes only, never the inputs themselves. Its presence
does NOT make the receipt policy-replayable under this profile: this document does not specify
the evaluation grammar, operator set, or evaluation order that a re-derivation would need, and
<xref target="policy-replay-is-out-of-scope-for-this-revision"></xref> still governs. Its presence also does not establish that the referenced policy was
adequate or in force.</li>
</ul>
<t><strong>On rejecting non-NFC input.</strong> A verifier MAY offer a mode that refuses any receipt carrying a
non-NFC string, reporting it as malformed. That mode is conformant and is a reasonable default for
a relying party that indexes on these fields; what is NOT conformant is normalizing a received
payload before hashing it, in any mode. A verifier's default SHOULD be stated in its documentation,
because the two modes give different answers on the same bytes and a caller cannot tell which it
got from a boolean.</t>
<t><strong>On timestamps.</strong> <tt>ts</tt> and <tt>governance.approval.at</tt> are RFC 3339 <xref target="RFC3339"></xref> <tt>date-time</tt> values,
and a conforming verifier <strong>MUST reject a value that is not a valid RFC 3339 <tt>date-time</tt></strong>. A
digit-and-separator shape check is not sufficient: <tt>2026-13-45T99:99:99Z</tt> matches the shape while
lying outside the grammar's own numeric ranges, and a receipt carrying it is malformed.</t>
<t>Because these values are inside the hashed payload, the <em>spelling</em> is part of the signed bytes, and
two spellings of one instant are two different receipts with two different hashes. The profile
therefore pins what is accepted rather than leaving it to each implementation:</t>

<ul spacing="compact">
<li>the <tt>T</tt> and <tt>Z</tt> separators MAY be upper or lower case, as RFC 3339 permits;</li>
<li>a numeric offset (<tt>+HH:MM</tt> / <tt>-HH:MM</tt>) is permitted as well as <tt>Z</tt>; a receipt is not required to
be written in UTC, and <tt>-00:00</tt> carries the meaning RFC 3339 gives it;</li>
<li>a fractional-second part of 1 to 9 digits is permitted, and trailing zeroes are significant bytes
rather than a canonical form to be trimmed;</li>
<li>a Producer MUST NOT emit a <tt>date-time</tt> whose seconds field reads <tt>60</tt>, and a verifier MUST reject
one. This profile forbids the leap-second representation rather than requiring anyone to validate
it. The reason is that no other rule here can be enforced offline without it: <xref target="RFC3339"></xref>,
Section 5.7 admits <tt>60</tt> only at an actual leap second, adjusted by the numeric offset, and says
future leap seconds cannot be predicted -- so a verifier holding no leap-second table cannot
decide whether <tt>2030-06-30T23:59:60Z</tt> is a valid <tt>date-time</tt> at all, and the requirement to reject
what is not valid RFC 3339 would have no determinate answer. Forbidding the representation costs a
Producer nothing it needs: an agent action stamped during a leap second is recorded at <tt>:59</tt> or at
the following second, and the receipt still denotes an instant. This is a deliberate narrowing of
RFC 3339 by this profile, not a claim about RFC 3339, and <xref target="open-questions"></xref> records it as a
decision a working group may reverse.</li>
</ul>
<t>A verifier MUST NOT normalize a timestamp before hashing, for the same reason it MUST NOT normalize
anything else (<xref target="canon-params"></xref>). Relying parties comparing timestamps across receipts MUST parse
them into instants first: byte equality and instant equality are different questions here.</t>
<t>Valid RFC 3339 syntax does not establish that the timestamp is truthful; it remains signer-chosen.
A verifier that accepts a well-formed timestamp has learned that the issuer wrote a well-formed
timestamp, and nothing about when anything happened.</t>
<t><tt>governance.ruleId</tt> is a signer-asserted label naming which rule fired. It is not a content
commitment to a policy, and a relying party MUST NOT treat it as one. Where a receipt carries no
<tt>governance.compliance</tt>, the receipt's &quot;policy identity&quot; is exactly that label and nothing
stronger. (-00's terminology defined a <tt>policyHash</tt> that -00's payload never carried; this
subsection resolves that inconsistency in favour of what the wire format actually does.)</t>
<t>This profile adds no member to <tt>noa.receipt/0.1</tt>. A claim that this record does not carry travels
as a separate signed artifact, linked by hash -- see <xref target="separate-artifacts"></xref> for why that choice is
load-bearing rather than stylistic.</t>
</section>
</section>

<section anchor="cose"><name>COSE Signed Statement profile</name>
<t><strong>A receipt may be presented in two forms, and this profile defines both.</strong> A receipt's <em>payload</em>
is the JSON object of <xref target="payload"></xref>, and its native signature (<xref target="payload-binding"></xref>) is computed over
that object; a receipt presented as payload plus native signature, with no COSE envelope, is a
<strong>bare receipt</strong>. A receipt carried inside a COSE_Sign1 is an <strong>enveloped receipt</strong>, and only an
enveloped receipt can become a Signed Statement (<xref target="signed-statement"></xref>). Both forms are legitimate
presentations: the reference implementation's chain path verifies bare receipts and never touches
COSE (<xref target="implementation-status"></xref>), while its COSE path produces the enveloped form. Every requirement
below that names the envelope applies to the enveloped form only; a Receipt Verifier given a bare
receipt checks the payload, the recomputed <tt>chain.hash</tt> and the native signature, and reports that
no envelope was present. Whether a SCITT-native profile should <em>require</em> the envelope is left open
(<xref target="open-questions"></xref>).</t>

<section anchor="protected-header"><name>Protected header</name>
<t><em>Every requirement below names its actor: what a <strong>Producer</strong> MUST emit, and what a <strong>Receipt
Verifier</strong> -- given one receipt and a keyring (<xref target="classes"></xref>) -- MUST check. No set and no evidence
artifacts are needed.</em></t>
<t>An enveloped receipt is a COSE_Sign1 carried as the <strong>tagged</strong> form, CBOR tag 18: a Producer MUST
emit the tag, and a verifier MUST reject an untagged COSE structure rather than guess at its type.
Its protected header MUST contain <tt>alg</tt>;
the receipt MUST additionally carry a key identifier, which SHOULD be in the protected header and MAY
be in the unprotected header (see below -- this avoids the RFC 2119 contradiction of requiring <tt>kid</tt> in
the protected header while also permitting it in the unprotected one). In detail:</t>

<ul spacing="compact">
<li><strong><tt>alg</tt> (label 1) = <tt>-19</tt> (Ed25519)</strong> as registered in the IANA COSE Algorithms registry by
<xref target="RFC9864"></xref>, Section 2.2 (the fully-specified Ed25519 identifier; <xref target="RFC8032"></xref> Section 5.1
parameter set). Issuers MUST use <tt>-19</tt>. <strong>Verifiers MUST reject every other <tt>alg</tt> value</strong>
(algorithm-confusion defense). That includes the polymorphic <tt>EdDSA</tt> identifier
<tt>-8</tt>, which <xref target="RFC9864"></xref>, Section 4.2.2 marks Deprecated (it was registered in <xref target="RFC9053"></xref>). It is
not a special case, and for this profile there is no
legacy-compatibility exception. The signing key MUST be an OKP key with <tt>crv = 6</tt> (Ed25519), the
curve registered for Ed25519 in the COSE Elliptic Curves registry (<xref target="RFC9053"></xref>, Section 7.1); a
verifier that can inspect the resolved key's type or curve MUST reject any key that is not
Ed25519 rather than attempt the verification, since a check against a key of another type answers
a different question than the <tt>alg</tt> value claims.
<xref target="RFC9864"></xref> makes <tt>alg = -19</tt> self-disambiguating, so a verifier MUST NOT rely on the key's <tt>crv</tt>
alone to determine the algorithm.</li>
<li><strong>A key identifier sufficient to resolve the verification key.</strong> <tt>kid</tt> (label 4) is the
baseline: a Producer MUST emit one, and it MUST be sufficient on its own for a verifier holding
the right keyring to resolve the verification key. The <tt>kid</tt> SHOULD be carried in the protected header (where it is covered by
the signature and cannot be stripped or swapped); it MAY instead be carried in the unprotected
header -- a verifier MUST resolve it from either bucket, preferring the protected copy when both
are present, and MUST report which bucket it used. Resolving a key and naming an identity are two
different acts, and this profile separates them: an unprotected <tt>kid</tt> MAY be used to <strong>resolve</strong>
the verification key, and MUST NOT be reported as the emitter's <strong>identity</strong>, because it is not
covered by the signature and can be swapped without breaking it (<xref target="identity-binding"></xref>). <xref target="RFC9052"></xref> treats <tt>kid</tt> as a hint that need
not be unique; this profile narrows that for a verifier's own key store. Where one <tt>kid</tt> resolves
to more than one key, a verifier MUST try each candidate and accept only if exactly one verifies;
where more than one key was tried it MUST report the identifier as ambiguous rather than silently
choose -- attribution built on a collision
is attribution to the wrong party. <tt>kid</tt> is an opaque byte string in <xref target="RFC9052"></xref> and this profile
does not narrow that; what it does require is that no verifier resolve it through a <strong>lossy
decode</strong>. A verifier whose key store is keyed by text MUST decode the <tt>kid</tt> bytes, re-encode the
result, and require the original bytes back before using the identifier, and MUST refuse to
resolve one that fails that round trip -- a lossy decode maps every invalid byte sequence onto
the same replacement character, so distinct signed identifiers collapse onto one entry and
attribution goes to whoever holds it.</li>
<li><strong>Certificate references are OPTIONAL and additional, never a substitute.</strong> A deployment using a
PKI MAY additionally carry <tt>x5t</tt> (label 34) or <tt>x5chain</tt> (label 33) as defined by <xref target="RFC9360"></xref>.
A receipt MUST NOT rely on them as its only key identifier, because a verifier conforming to this
profile is required to resolve <tt>kid</tt> and is not required to process certificates. The reference
implementation ignores both headers and refuses a receipt carrying neither a protected nor an
unprotected <tt>kid</tt> (<xref target="implementation-status"></xref>).</li>
<li><strong>Unknown headers are ignored, critical ones are fatal.</strong> The mere presence of an additional
registered header parameter -- <tt>kid</tt>, <tt>content type</tt>, <tt>CWT_Claims</tt>, or <tt>crit</tt> itself -- is never on
its own a reason to reject a COSE_Sign1. What decides the outcome is <tt>crit</tt> (label 2).
<strong>This profile requires</strong> a verifier to ignore any header parameter it does not understand that
<tt>crit</tt> does not list, rather than reject the object -- that rule is this document's, not
<xref target="RFC9052"></xref>'s, which defines <tt>crit</tt> without imposing it. What <xref target="RFC9052"></xref>, Section 3.1 does state
is the fatal direction: a <tt>crit</tt> entry naming a header parameter that is not in the protected
bucket is a fatal processing error. A verifier MUST likewise reject an object whose <tt>crit</tt> lists a
header parameter it cannot process.
<xref target="signed-statement"></xref> adds one further rejection rule, for <tt>CWT_Claims</tt> appearing in both buckets.</li>
</ul>
<t>The protected header MUST satisfy the Core Deterministic Encoding Requirements of <xref target="RFC8949"></xref>,
Section 4.2.1: shortest-form argument encodings and map keys sorted in bytewise lexicographic order
of their deterministic encodings, each key occurring once. Verifiers MUST reject any other
encoding.</t>
</section>

<section anchor="payload-binding"><name>Payload binding, and the two signatures</name>
<t><em>Construction rules address the <strong>Producer</strong>; verification rules address the <strong>Receipt Verifier</strong>,
which is given the receipt and a keyring (<xref target="classes"></xref>).</em></t>
<t>The payload is the JCS serialization from <xref target="payload"></xref>, carried <strong>attached</strong> as the COSE_Sign1
payload. This profile does not define a detached-payload mode: in COSE, verifying a detached
payload requires the verifier to be supplied the payload bytes themselves for the <tt>Sig_structure</tt>,
so a detached receipt is not self-contained, and <tt>chain.hash</tt> -- a digest over the receipt with
<tt>chain.hash</tt> and <tt>sig.value</tt> removed -- is a chain-linkage value, not a substitute for those bytes.
A verifier conforming to this profile MUST reject a COSE_Sign1 whose payload element is absent or
null. (An earlier revision said a detached payload's digest was bound through <tt>chain.hash</tt>; that
was wrong in both directions, and is corrected rather than carried forward. A detached mode, if
one is ever wanted, is future work -- see <xref target="implementation-status"></xref>.)</t>
<t>A receipt in this profile carries <strong>two independent signatures</strong>, and conflating them is the
failure this subsection exists to prevent.</t>

<ol spacing="compact">
<li>The <strong>native receipt signature</strong>, <tt>sig.value</tt>, is computed over a domain-separated preimage,
fixed byte-exactly by the construction below.</li>
<li>The <strong>outer COSE_Sign1 signature</strong> covers the RFC 9052 <tt>Sig_structure</tt> over the protected
header and the payload -- and the payload is the whole receipt <em>including</em> its native <tt>sig</tt>
member. The <tt>external_aad</tt> field of that structure is the <strong>empty byte string</strong> in this profile;
a verifier MUST use a zero-length <tt>external_aad</tt> and MUST NOT accept an envelope that only
verifies under some application-supplied value, since nothing in the envelope would record what
that value was.</li>
</ol>
<t>The native construction, in full:</t>

<artwork><![CDATA[HASH-INPUT = JCS( the receipt object with exactly two members
                  removed: chain.hash and sig.value; every other
                  member, including sig.alg and sig.kid, is present )

MESSAGE    = UTF8("NOA-Receipt-v0.1-sig:")  ; 21 octets: tag then ":"
          || SHA-256(UTF8(HASH-INPUT))      ; RAW 32-octet digest,
                                            ; NOT its hex form
                                            ; 21 + 32 = 53 octets

sig.value  = base64(Ed25519(MESSAGE))       ; standard alphabet,
                                            ; with padding
sig.alg    = "ed25519"                      ; exact lowercase literal
chain.hash = "sha256:" || lowercase-hex(SHA-256(UTF8(HASH-INPUT)))
]]></artwork>
<t><tt>||</tt> denotes octet-string concatenation, and <tt>base64</tt> is the standard alphabet with padding
(<xref target="RFC4648"></xref>, Section 4). Two encoding rules close variants that would otherwise let one signature
be written two ways: <tt>sig.value</tt> MUST be canonical base64 -- exactly one padded form, no whitespace,
no URL-safe alphabet, and the unused bits of the final quantum set to zero, as <xref target="RFC4648"></xref>,
Section 3.5 requires of a canonical encoding -- and MUST decode to exactly 64 octets. Re-encoding
the decoded value and requiring the original string back enforces every one of those rules at once,
and is the check a verifier SHOULD implement rather than testing each separately. The signature
itself MUST be a
canonical Ed25519 encoding, with the scalar <tt>S</tt> reduced modulo the group order. A verifier MUST
reject a non-canonical encoding of either kind rather than accept it as an equivalent form. This
matters more here than in a bare COSE profile because <tt>sig.value</tt> is excluded from the hash input
(<xref target="payload-binding"></xref>): a permissive decoder would let two byte-different receipts share one chain
hash. The digest that is signed and the digest that appears in
<tt>chain.hash</tt> are the same digest of the same input, in two encodings. The domain tag means a receipt
signature cannot be replayed as a signature for any other object class in the same system, and the
checkpoint tag (<xref target="checkpoint"></xref>) is distinct from it.</t>
<t><strong>The native algorithm label is checked before any cryptography.</strong> <tt>sig.alg</tt> MUST be the exact
lowercase string <tt>ed25519</tt>. A Receipt Verifier MUST reject any other value -- including a
differently cased spelling, and including a value naming an algorithm it happens to support --
before attempting a signature check. A verifier that selects its algorithm from the resolved key or
from its own configuration answers a different question than the receipt asks.</t>
<t><strong>The verification equation is pinned.</strong> A verifier MUST check the equation
<tt>[S]B = R + [k]A'</tt> -- the cofactorless check that <xref target="RFC8032"></xref>, Section 5.1.7 names as &quot;sufficient,
but not required&quot; beside the cofactored <tt>[8][S]B = [8]R + [8][k]A'</tt>. Both are permitted by RFC 8032
and they do not accept the same set of signatures: every signature the cofactorless equation accepts
is accepted by the cofactored one, and the reverse does not hold. Leaving the choice to the
implementer would leave <em>validity itself</em> implementation-defined, and in a hash chain a
disagreement about one signature is a disagreement about accepted history -- which is the hazard
this profile exists to remove. This revision therefore pins the narrower equation, so that two
conforming verifiers accept the same set. <xref target="open-questions"></xref> records that the working group may
revisit the choice; what it may not do is leave it unmade.</t>
<t>Determinism is a product of this equation together with the encoding rules around it -- the
canonical <tt>S</tt> above, the canonical base64 that carries the signature, and the public-key rules
below -- and not of the equation alone. <xref target="implementation-status"></xref> reports which shipped verifiers
satisfy the pinned equation and which do not.</t>
<t><strong>The verification key itself.</strong> Three rules bind whichever actor resolves a key for a native
signature check, and they exist for the same reason -- the same signed chain must not be accepted by
one verifier and rejected by another:</t>

<ul spacing="compact">
<li>The resolved key MUST be an Ed25519 key (<xref target="RFC8032"></xref>, Section 5.1). A verifier that can inspect
the resolved key's type or curve MUST reject any other key rather than attempt the verification:
dispatching the verification algorithm on the key's type instead of on <tt>sig.alg</tt> is the same
algorithm-confusion defect <xref target="protected-header"></xref> closes on the envelope path.</li>
<li>A public key whose encoded y-coordinate is not a canonical field element MUST be rejected: after
the sign bit (bit 255) is cleared, the remaining 255-bit little-endian value MUST be less than
<tt>2^255 - 19</tt>. A public key that is one of the eight canonical small-order Ed25519 point encodings
MUST also be rejected. Both rules are about the <strong>key</strong>, not about the verification equation:
they remove the two classes on which widely deployed libraries were measured to disagree, so that
two verifiers handed one signed chain do not accept two different histories. The verification
equation itself is pinned above, not left to the implementer.</li>
<li>Where a verifier accepts a key in an encoded form, that encoding MUST be canonical: the verifier
MUST re-encode the key it parsed and require byte equality with the input, and MUST reject a
base64 spelling that does not re-encode to itself. This profile defines no keyring format
(<xref target="not-defined-here"></xref>), so the rule binds whatever encoding a deployment chooses. Without it one
logical key has many accepted spellings, and any later comparison on key bytes -- a fingerprint,
a pin, a deduplication -- is bypassable by re-spelling the key.</li>
</ul>
<t>The relationship between the two signatures is normative:</t>

<ul spacing="compact">
<li>A verifier MUST NOT treat a valid outer COSE signature as evidence that the native signature
verifies, and MUST NOT treat a valid native signature as evidence that the envelope is
authentic. Each is checked on its own inputs. <strong>A Receipt Verifier (<xref target="classes"></xref>) given an enveloped
receipt MUST run both</strong>, and MUST report which of the two produced any result it returns; given a
bare receipt it runs the native check alone and MUST report that no envelope was verified. What is
never conformant is running one check and reporting the other's property.</li>
<li>The two signatures MAY be produced by different keys. Where they are, the COSE <tt>kid</tt> identifies
the party that emitted <em>this envelope</em> and <tt>sig.kid</tt> identifies the party that signed <em>this
receipt into its chain</em>. A relying party MUST NOT collapse the two identities, and neither may a
verifier: they are two claims about two different parties, and <xref target="identity-binding"></xref> says what each
is checked against rather than letting one stand for both. A verifier MUST report every identifier
it reports <strong>as an identity</strong>, saying which claim each belongs to -- always the native <tt>sig.kid</tt>,
and the outer <tt>kid</tt> when it came from the protected bucket. An outer <tt>kid</tt> taken from the
unprotected bucket resolved a key and named nobody (<xref target="protected-header"></xref>), and MUST NOT be reported
as the emitter's identity.</li>
<li><strong><tt>chain.hash</tt> MUST be recomputed, and a mismatch MUST be rejected.</strong> A Receipt Verifier MUST
compute <tt>&quot;sha256:&quot;</tt> followed by the lowercase hex of SHA-256 over <tt>HASH-INPUT</tt> -- the same input
the native signature covers -- and compare it to the receipt's <tt>chain.hash</tt>. If they differ the
receipt MUST be rejected as tampered. This check is not optional and is not implied by either
signature: <tt>HASH-INPUT</tt> removes <tt>chain.hash</tt>, so <tt>sig.value</tt> does not cover it, and the outer
COSE signature that does cover it may have been applied by a different party (<xref target="payload-binding"></xref>).
Without this recomputation a receipt whose <tt>chain.hash</tt> is an arbitrary value passes every other
Receipt-Verifier check, and every artifact that references a receipt by hash
(<xref target="separate-artifacts"></xref>) inherits the forgery.</li>
<li><strong>The two checks consume different inputs, and the difference is normative.</strong> The outer COSE
signature is verified over the payload <strong>bytes exactly as received</strong>: a verifier MUST NOT parse,
re-serialize, re-order or normalize them first. The native signature and <tt>chain.hash</tt> are
verified over a <strong>derived</strong> input: the payload is parsed, the two named members are omitted, the
result is re-serialized under <xref target="canon-params"></xref>, and that string is hashed. A verifier MUST run
both and MUST NOT substitute one input for the other.</li>
<li><strong>The canonicality check is over the whole payload, not over the derived input.</strong> A verifier MUST
parse the received payload bytes, re-serialize the <em>complete</em> parsed object under <xref target="canon-params"></xref>,
and require the result to equal the received bytes: <tt>JCS(parse(bytes)) == bytes</tt>. If they differ,
the receipt MUST be rejected rather than repaired, because the field values the verifier would go
on to report are then not the values the outer signature covers. The derived input of the native
path omits two members by construction and therefore can never equal the received bytes; nothing
in this profile asks it to.</li>
</ul>
</section>

<section anchor="signed-statement"><name>Carrying a receipt as an RFC 9943 Signed Statement</name>
<t><em>The mapping binds the <strong>Producer</strong>. The evaluation rule at the end binds whichever actor evaluates
a receipt as a Signed Statement, and names it there.</em></t>
<t>A COSE_Sign1 built to <xref target="protected-header"></xref> alone is a valid signed object, but it is <strong>not</strong> by
itself a Signed Statement in the sense of <xref target="RFC9943"></xref>. That architecture states two protected-header
requirements in the body of its Section 6: the protected header of a Signed Statement MUST include
the CWT Claims header parameter, whose value MUST include the Issuer Claim (label 1) and the
Subject Claim (label 2); and <tt>kid</tt> MUST be present in the protected header whenever neither <tt>x5t</tt>
nor <tt>x5chain</tt> is present there. A receipt that omits them will be refused at registration by a
conforming Transparency Service. This subsection specifies the mapping, so that the difference
between &quot;a signed receipt&quot; and &quot;a registrable Signed Statement&quot; is a checkable property rather than
a hopeful sentence.</t>
<t>To be carried as a Signed Statement under this profile, a receipt MUST additionally have:</t>
<table>
<thead>
<tr>
<th>Requirement</th>
<th>Value for this profile</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>CWT_Claims</tt> header parameter (label 15) in the <strong>protected</strong> header, encoded as specified in <xref target="RFC9597"></xref>, Section 2</td>
<td>REQUIRED (<xref target="RFC9943"></xref>, Section 6)</td>
</tr>

<tr>
<td><tt>iss</tt> (CWT claim 1)</td>
<td>the Issuer of the Signed Statement: the authority on whose behalf the receipt is emitted. It is a signer-asserted string until something binds it to the verification key -- <xref target="registration"></xref> requires a Transparency Service to make that binding and states that this profile defines no mechanism for it. This profile does not require <tt>iss</tt> to equal the identity that submits the statement for registration; the two are separable, and a Transparency Service applies its own Registration Policy to the submission</td>
</tr>

<tr>
<td><tt>iss</tt> when <tt>x5t</tt> or <tt>x5chain</tt> is in the protected header</td>
<td>additionally MUST be a string conforming to the URI syntax, of length 1 to 8192 (<xref target="RFC9943"></xref>, Section 6)</td>
</tr>

<tr>
<td><tt>sub</tt> (CWT claim 2)</td>
<td>the <tt>scope.chain</tt> identifier, so one chain is one subject. <tt>scope.chain</tt> carries no namespace of its own (<xref target="field-semantics"></xref>), so two issuers can choose the same string: a Transparency Service MUST scope subjects by issuer rather than treat <tt>sub</tt> as globally unique, and a chain carrying several agents remains one subject</td>
</tr>

<tr>
<td><tt>kid</tt> in the <strong>protected</strong> header</td>
<td>REQUIRED. <xref target="RFC9943"></xref>, Section 6 makes this conditional on the absence of <tt>x5t</tt>/<tt>x5chain</tt>; this profile requires a resolvable <tt>kid</tt> unconditionally (<xref target="protected-header"></xref>) and requires it protected for registration (<xref target="registration"></xref>), so the condition never relaxes it here</td>
</tr>
</tbody>
</table><t>The <tt>CWT_Claims</tt> header parameter MUST occur exactly once across the two header buckets, as
<xref target="RFC9597"></xref>, Section 2 requires: a receipt carrying label 15 in both the protected and unprotected
headers is malformed, and a verifier MUST reject it rather than prefer one copy.</t>
<t><strong>These claims are necessary, and this profile does not claim they are sufficient.</strong> <xref target="RFC9943"></xref>
describes a Statement as carrying a media type that tells a Transparency Service what it is holding,
and this revision registers none (<xref target="iana-considerations"></xref>) while leaving the protected <tt>content type</tt>
a SHOULD. So a receipt that carries <tt>iss</tt> and <tt>sub</tt> has met the requirement this profile can state
and may still be refused by a service that dispatches on a media type it does not have. Closing that
is the media-type registration named in <xref target="open-questions"></xref>, not a further claim here.</t>
<t>A verifier or Transparency Service evaluating a receipt <strong>as a Signed Statement</strong> MUST reject one
that lacks protected <tt>CWT_Claims</tt> carrying both <tt>iss</tt> and <tt>sub</tt>, per <xref target="RFC9943"></xref>, Section 6. A verifier evaluating a receipt
merely as a signed record MUST NOT infer registrability from a successful signature check: the two
are different questions and this profile keeps them apart deliberately.</t>
<t>A receipt carried as a Signed Statement SHOULD also carry a protected <tt>content type</tt> (label 3)
describing the payload. This revision registers no media type (<xref target="iana-considerations"></xref>), which has a
consequence worth stating rather than leaving to discovery: until one exists, nothing makes two
conforming producers choose the same value, and where they choose differently a Transparency Service
that dispatches policy on content type will treat them as different formats. A <strong>Deployment</strong> SHOULD therefore agree the value with the
Transparency Service it registers against, and MUST NOT rely on it for cross-deployment
interoperability. Registering a media type is the first IANA action a future revision should
request.</t>
<t>Producers that do not emit these claims produce receipts that verify offline exactly as specified
everywhere else in this document, and that a conforming TS is entitled to refuse. The reference
implementation is currently in that position; <xref target="implementation-status"></xref> states it as a named conformance gap
rather than leaving a reader to discover it at registration time.</t>
</section>
</section>

<section anchor="hash-chaining-and-completeness"><name>Hash-chaining and completeness</name>
<t><em>This section's number, title and anchor are unchanged from -00, and the construction it defines is
unchanged. <strong>Two sentences of -00's paragraph were factually wrong and are corrected here</strong> rather
than reproduced and contradicted twenty lines later. <xref target="changes"></xref> states the before and after, for the
benefit of a document that adopts this section by reference (<xref target="relationship-to-other-work"></xref>).</em></t>
<t>Each receipt carries a monotonic <tt>chain.seq</tt> (genesis = 0) and <tt>prevHash</tt>. A Chain Verifier checking
a presented chain MUST verify: each <tt>prevHash</tt> equals the prior receipt's <tt>chain.hash</tt>; the genesis
receipt has <tt>prevHash == null</tt>; and, when a signed checkpoint is present <strong>and an identity manifest
is supplied</strong>, that the checkpoint was signed by one of the keys that manifest authorizes for the
genesis receipt's <tt>agent.id</tt>, so that a foreign checkpoint produced by a re-heading attacker is
rejected -- without a manifest that binding does not hold (<xref target="checkpoint"></xref>). This profile detects
in-band tampering <em>within</em> a presented chain, and detects tail truncation <strong>only against an
authenticated checkpoint</strong> (<xref target="chain-axes"></xref>), never from the presented set alone; it does NOT, by
itself, detect <strong>equivocation</strong> (an issuer signing two divergent chains) -- that requires
registration in a SCITT Transparency Service or an equivalent external witness.</t>

<section anchor="chain-axes"><name>Chain-level failure axes</name>
<t><em>Section 5 and this subsection address the <strong>Chain Verifier</strong> (<xref target="classes"></xref>): a Receipt Verifier is
given no set, cannot evaluate these conditions, and does not fail them.</em></t>
<t><em>This subsection is NEW in -01. Section 5 keeps its number, title and anchor because another
profile adopts it by reference (<xref target="relationship-to-other-work"></xref>); two of its sentences are corrected
in this revision and <xref target="changes"></xref> states which; everything below adds
Chain-Verifier-class requirements that -00 did not state, and <xref target="changes"></xref> lists each of them.</em></t>
<t>The checks above are properties of a receipt SET, not of any receipt. <strong>Six conditions</strong> are named
below, and every one of them is a property of the presented receipts, decided by comparing them to
one another: a verifier presented with any single receipt from the set will find that receipt
individually well-formed, correctly hashed, and correctly signed. They are named so that a
reconciling verifier can report WHICH set-level property failed, rather than collapsing every one of
them onto a single &quot;invalid&quot;.</t>
<t>A checkpoint is deliberately <strong>not</strong> among them. It is a separate object, supplied separately, and
what it answers is a separate question -- so it has its own axis, defined after this table. Nothing
on that axis is a chain condition, and nothing on it can fail a chain. That separation is
<xref target="absence"></xref>'s rule applied to this section: a finding about one object is never a finding about
another.</t>
<table>
<thead>
<tr>
<th>Axis</th>
<th>Failure</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>chain-link-broken</tt></td>
<td>a <tt>prevHash</tt> does not equal the prior receipt's <tt>chain.hash</tt>; a non-genesis receipt carries <tt>prevHash == null</tt>; or the genesis receipt carries a <tt>prevHash</tt> that is not <tt>null</tt> (Section 5 requires it to be)</td>
</tr>

<tr>
<td><tt>chain-sequence-duplicate</tt></td>
<td>two receipts in one <tt>scope.chain</tt> carry the same <tt>chain.seq</tt></td>
</tr>

<tr>
<td><tt>chain-sequence-gap</tt></td>
<td>the presented <tt>chain.seq</tt> values are not contiguous, or do not begin at 0. A conforming Chain Verifier requires the complete chain from genesis; see below</td>
</tr>

<tr>
<td><tt>chain-scope-mismatch</tt></td>
<td>the presented set does not all carry one <tt>scope.chain</tt>. A Chain Verifier is handed a set <em>as presented</em> and checks that it is one chain; it does not assume it. A supplied checkpoint that names a different chain is <strong>not</strong> this condition -- it is a finding about the checkpoint (<xref target="checkpoint"></xref>)</td>
</tr>

<tr>
<td><tt>chain-tenant-split</tt></td>
<td>two present, unequal <tt>scope.tenant</tt> values appear within one <tt>scope.chain</tt> (<xref target="optional-members"></xref>)</td>
</tr>

<tr>
<td><tt>chain-key-discontinuity</tt></td>
<td>two receipts in the presented chain carry the same <tt>agent.id</tt> and different <tt>sig.kid</tt></td>
</tr>
</tbody>
</table><t>A Chain Verifier MUST detect all six conditions -- each is decided from the presented set alone, so
none of them depends on an input the verifier may not have been given -- and MUST report them
distinctly enough that a consumer can tell which held.</t>
<t><strong>The checkpoint axis, which is not a chain condition.</strong> A checkpoint answers one question: <em>has the
tail of this chain been cut?</em> A Chain Verifier MUST report that question in exactly one of three
states, and MUST NOT let any of them alter the verdict the six conditions produced:</t>

<ul spacing="compact">
<li><strong>answered</strong> -- a checkpoint was supplied, was structurally valid, named this chain, and
authenticated. The verifier compared it to the presented head. The comparison yields either
<em>consistent</em> -- the head matches, or the presented chain is a consistent extension of it as
described below -- or the failure <tt>checkpoint-head-conflict</tt>: the presented head and an
authenticated checkpoint disagree. Which of them is wrong -- a deleted tail, a fork, or a signer
error -- is not established by the comparison, and a verifier MUST NOT name a cause it did not
observe.</li>
<li><strong>unanswered</strong> -- no checkpoint was supplied. The verifier MUST report the tail question as
unanswered and MUST NOT report it as an absence of truncation (<xref target="checkpoint"></xref>).</li>
<li><strong>unusable</strong> -- a checkpoint was supplied and could not be used: it was malformed, it named
another chain, or it did not authenticate (<xref target="checkpoint"></xref>). The verifier MUST report <em>which</em>, MUST
report the tail question as unanswered, and MUST NOT convert any of it into a finding about the
receipts. <tt>checkpoint-unverified</tt> is this document's identifier for the authentication case.</li>
</ul>
<t>Two consequences follow, and both are the reason the axis is separate. A presented chain that
satisfies all six conditions is a valid chain whatever arrives beside it: a defective checkpoint
subtracts an answer, it does not create a failure. And whether the verifier could authenticate the
checkpoint is what decides between <em>answered</em> and <em>unusable</em> -- never whether a keyring happened to
be supplied. <xref target="implementation-status"></xref> measures what the other reading costs: every shipped verifier
branches on whether a keyring was passed, and both branches produce a finding against the chain.</t>
<t>The distinction between the conditions is operational, not cosmetic: <tt>chain-sequence-duplicate</tt> and <tt>chain-scope-mismatch</tt>
can indicate a splice of two genuine histories -- every receipt involved verifies in isolation. A
duplicate sequence has innocent causes too, a retransmitted copy being the common one: where the two
receipts at that sequence are byte-identical a verifier SHOULD say so, and where they differ it has
found an issuer that signed two histories at one position, which is the serious case -- while
<tt>chain-link-broken</tt> indicates in-band alteration. Reporting them as one verdict tells a relying
party that something is wrong but not what an operator must do next.</t>
<t>The six labels above are this document's identifiers for the six <em>conditions</em>, and
<tt>checkpoint-head-conflict</tt> and <tt>checkpoint-unverified</tt> are its identifiers for the two named
checkpoint-axis outcomes. A Chain Verifier MAY report any of them under its own strings; what it
MUST do is make them distinguishable in its output, and keep the chain conditions distinguishable
from the checkpoint axis. No particular spelling is required on the wire. Implementers
aligning with a reconciliation layer should note that these conditions correspond to divergence
axes carried by <xref target="I-D.hillier-scitt-arp"></xref>; that alignment was measured against this profile's own
adversarial corpus (see <xref target="implementation-status"></xref>).</t>
<t><strong>Completeness is required, not optional.</strong> A Chain Verifier MUST reject a presentation that does
not begin at <tt>seq == 0</tt>, and a <strong>Deployment</strong> presenting a chain for verification MUST present it
from genesis. This is
what every implementation of this profile already does -- five verifiers in five languages each
require <tt>0..n-1</tt>, though only one of the five is an independent oracle for the others
(<xref target="implementation-status"></xref>) -- and a presentation of, say, <tt>seq 1</tt> and <tt>seq 2</tt> is refused as a
missing genesis rather than accepted as a partial view. Verification of a <em>range</em> that does not
start at genesis is therefore not a capability of this profile: it is neither specified here nor
implemented anywhere, and <xref target="implementation-status"></xref> lists it as such. -00 said &quot;a verifier checking
a range of receipts&quot; and left the boundary of a &quot;range&quot; unstated; this revision states it. No
implementation behaviour changes: the stricter reading is the one every implementation has always
enforced, and only the documentation has caught up.</t>
<t><strong>What a checkpoint comparison can and cannot show.</strong> A mismatch between the presented head and a
checkpoint's <tt>headHash</tt> can reveal truncation <em>relative to that checkpoint</em> -- that is, relative to
one signed assertion made at one moment by one key. It is not a general detector of tail
truncation: a prover who withholds both the tail and the checkpoint that would expose it presents a
set with no mismatch in it, and no property of the presented set reveals the omission
(<xref target="security-considerations"></xref>). Registration in a Transparency Service replaces the prover's own
checkpoint with a third party's signed proof of registration, which is stronger and still not a
general answer; <xref target="registration"></xref> states exactly what it is worth to an offline holder.</t>
<t><strong>An older checkpoint over a longer chain is not truncation.</strong> A checkpoint asserts a head at the
moment it was minted; a chain that has grown since then legitimately presents a <em>later</em> head. A
verifier MUST therefore distinguish two mismatches. Where the checkpoint's <tt>headHash</tt> appears in the
presented chain at exactly its <tt>highestSeq</tt>, and the presented head's <tt>chain.seq</tt> is greater, the
presented chain <strong>extends</strong> the checkpoint consistently: this is not <tt>checkpoint-head-conflict</tt>, and a
verifier MUST NOT report it as a conflict. Every other mismatch -- a head at a lower sequence, or a
checkpoint whose asserted head does not appear in the presented chain at its own sequence at all --
is <tt>checkpoint-head-conflict</tt>: the presented set and an authenticated checkpoint disagree about the head.
A verifier MUST report the disagreement and MUST NOT report a cause for it. Deletion of a tail is
one explanation; an issuer that signed two divergent histories is another; a checkpoint minted over
a different lineage, or a signer error, is a third. The comparison separates none of them, and
naming one is the same manufacture of a specific accusation <xref target="absence"></xref> forbids -- which is why this
revision renamed the condition rather than keep -00's reading of it as truncation. The reference implementation does not draw this distinction and reports
one verdict for both cases; that is recorded as a conformance gap in <xref target="implementation-status"></xref>.</t>
<t><strong>Why the unauthenticated case turns on authentication, and not on inputs.</strong> The rule above -- that
a checkpoint the verifier did not authenticate yields <tt>checkpoint-unverified</tt> and never a truncation
finding -- is the one this whole document turns on. A checkpoint is supplied to the verifier, often
by the same party whose chain is in question. If an unauthenticated one could establish truncation, anyone able to hand a verifier a
file could mint a signed-looking forensic conclusion about someone else's chain -- an accusation
manufactured from attacker-controlled bytes. Only an authenticated checkpoint carries the authority
to say a tail is missing, and <tt>checkpoint-unverified</tt> is the indeterminate result that names the
reason instead. <xref target="absence"></xref> states the general form of the rule, over every input rather than this
one; <xref target="implementation-status"></xref> measures two places where a shipped verifier breaks it.</t>
<t>The prohibitions in the other direction are unchanged: an unauthenticated checkpoint MUST NOT be
reported as a satisfied tail check, and MUST NOT upgrade any result. A verifier MUST state, in its
output, whether the checkpoint that produced any tail conclusion was authenticated, and
<xref target="security-considerations"></xref> states what an authenticated one is and is not worth.</t>
<t><strong>One agent, one key, within one presented chain.</strong> A Chain Verifier MUST reject a presented chain
in which two receipts share an <tt>agent.id</tt> and differ in <tt>sig.kid</tt>, reporting
<tt>chain-key-discontinuity</tt>. The condition needs no keyring: it compares the receipts to one another.</t>
<t>This is the condition that catches the ordinary form of chain extension, and what it catches is
narrower than &quot;extension is closed&quot;. Consider a party that holds any key the verifier trusts and
appends receipts to another party's chain under the victim's own <tt>agent.id</tt>: the links are intact,
there is no sequence gap, the <tt>scope.chain</tt> is constant and the tenant is constant, so none of the
other conditions fires. What gives that attacker away is that the appended receipts carry a
different <tt>sig.kid</tt> while claiming the same agent.</t>
<t>Two escapes remain, and they belong with the claim rather than after it, because a reader who takes
the claim without them will believe the chain is closed when it is not:</t>

<ul spacing="compact">
<li><strong>It fires within one <tt>agent.id</tt>, and chains may carry several.</strong> An attacker holding another
trusted key can append receipts under a <em>new</em> <tt>agent.id</tt> and no condition in the table fires: the
set is still one chain, one tenant, contiguous and correctly linked. Closing that needs a rule
about <em>which agents may appear in a chain</em> -- an append-authority binding this profile does not
define (<xref target="not-defined-here"></xref>). The identity manifest binds an <tt>agent.id</tt> to keys; it does not bind
a chain to a set of agents, and a Chain Verifier given one still has nothing to test the new
agent against.</li>
<li><strong>The comparison is on identifiers, and that is what it detects.</strong> The condition compares
<tt>sig.kid</tt> strings, because a Chain Verifier is defined over a presented set and needs no keyring
to evaluate it. One consequence is unavoidable and is stated rather than hidden: two <em>different</em>
keys presented under one <tt>kid</tt> are indistinguishable to it, so an attacker who can get a second
key registered under the victim's identifier is not caught by this condition -- <xref target="registration"></xref>
and the keyring, not this comparison, are where that is addressed. A verifier MAY additionally
compare resolved key material where it can, and if it does it MUST report that as a separate
result, because it is a different check with a different failure set; what it MUST NOT do is
substitute one for the other under this condition's name.</li>
</ul>
<t>Within one presented chain, then, one agent speaks under one key identifier or the set fails -- and
the two escapes above are the boundary of that sentence, not exceptions to be discovered later. The
cost is the corresponding limit stated in <xref target="checkpoint"></xref>: key rotation is not expressible inside a
chain, and a deployment that rotates a signing key starts a new one.</t>
<t><strong>Sequence orders a chain; timestamps do not.</strong> <tt>chain.seq</tt> and <tt>prevHash</tt> are the only ordering
this profile defines. <tt>ts</tt> is signer-chosen (<xref target="optional-members"></xref>) and this profile places no
requirement that it be non-decreasing along a chain: a set whose timestamps run backwards can be
fully conformant on every axis above, and a relying party that sorts or reasons by <tt>ts</tt> is using a
field no check constrains. Where a deployment needs temporal ordering it MUST obtain it from
something other than these records.</t>
<t>Where more than one condition holds, a verifier MUST report all of them rather than the first it
found: an operator's next step depends on the set, and a single-value result field cannot carry it.
This profile defines no wire encoding for that report -- how a verifier's output is structured is
outside it (<xref target="not-defined-here"></xref>) -- only that the conditions be individually recoverable from it.</t>
<t><strong>A condition that holds is not a note; it is a failure.</strong> A Chain Verifier MUST NOT return a
positive result for a set on which any of the six conditions holds. That rule has no exception in
this revision, and one case is worth spelling out because the reasoning above cuts the other way:
two receipts at the same <tt>chain.seq</tt> that are byte-identical are a retransmitted copy rather than a
divergent history, and a verifier SHOULD say so in its report -- but the condition still holds, so
the result is still a failure. De-duplicating such a set and returning a positive result is <strong>not</strong>
conformant here. Whether a <em>future</em> revision should permit it is an open question (<xref target="open-questions"></xref>);
this one answers it in the strict direction so that two verifiers cannot disagree about the same
bytes today.</t>
<t>A verifier MUST NOT report a chain-level axis as a per-receipt failure, and MUST NOT infer from
the absence of these axes that the presented set is complete: completeness against records the
prover never presented is not a property of the presented set (see <xref target="security-considerations"></xref>).</t>
</section>

<section anchor="checkpoint"><name>Checkpoint structure</name>
<t><em>The structure and signing rules address the <strong>Producer</strong> of a checkpoint; the authentication and
scoping rules address the <strong>Chain Verifier</strong>, and apply only when a checkpoint is supplied.</em></t>
<t>A <em>checkpoint</em> is a separately signed assertion of a chain's current head. It is not a receipt and
is not part of any chain; it is the object the tail check of <xref target="chain-axes"></xref> compares against.</t>

<artwork><![CDATA[{
  "spec":       "noa.checkpoint/0.1",
  "chain":      "<chain id>",         ; the scope.chain it speaks for
  "highestSeq": <int>,                ; chain.seq of the head receipt
  "headHash":   "sha256:<hex>",       ; chain.hash of that receipt
  "ts":         "<RFC 3339 date-time>",
  "sig":        { "alg": "ed25519", "kid": "<key id>",
                  "value": "<base64>" }
}
]]></artwork>
<t>Its signature mirrors the receipt construction of <xref target="payload-binding"></xref>, and is pinned here to the
same byte-exactness:</t>

<artwork><![CDATA[CHECKPOINT-HASH-INPUT =
    JCS( the checkpoint object with exactly one member removed:
         sig.value; sig.alg and sig.kid remain present, and there
         is no chain.hash member to omit )

CHECKPOINT-MESSAGE =
       UTF8("NOA-Checkpoint-v0.1-sig:")   ; 24 octets: tag then ":"
    || SHA-256(UTF8(CHECKPOINT-HASH-INPUT))
                                          ; RAW 32-octet digest,
                                          ; NOT its hex form

sig.value = base64(Ed25519(CHECKPOINT-MESSAGE))
sig.alg   = "ed25519"
]]></artwork>
<t>The domain tag is distinct from the receipt tag, so neither signature can be replayed as the other,
and the colon is part of the signed prefix in both constructions.</t>
<t><strong>The object is closed, and a Chain Verifier MUST validate its structure before authenticating
it.</strong> A checkpoint MUST carry exactly the members shown above and no others, at either level, and
<tt>sig</tt> MUST carry exactly <tt>alg</tt>, <tt>kid</tt> and <tt>value</tt>. A Chain Verifier MUST reject a checkpoint
carrying any other member. Closure is load-bearing here in a way it is not for an ordinary schema:
<tt>CHECKPOINT-HASH-INPUT</tt> copies every member of the object except <tt>sig.value</tt>, so a smuggled member
sits <strong>inside</strong> the signature. Its signer signs it honestly, the signature verifies, and the
closed-object check is the only control that rejects it. The value rules, stated as requirements
rather than left to be read off a figure:</t>

<ul spacing="compact">
<li><tt>spec</tt> MUST be the exact string <tt>noa.checkpoint/0.1</tt>;</li>
<li><tt>chain</tt> MUST be a non-empty string, and a Chain Verifier MUST compare it to the <tt>scope.chain</tt> of
the presented set. A checkpoint naming another chain is not evidence about this one, and the
finding is <strong>about the checkpoint</strong>: the verifier MUST report that the checkpoint does not
describe the presented chain, MUST NOT use it for any tail conclusion, and MUST NOT let it fail an
otherwise valid presentation. It is not a chain-level condition (<xref target="chain-axes"></xref>), for the reason
<xref target="absence"></xref> gives: nothing about the presented receipts failed;</li>
<li><tt>highestSeq</tt> MUST be an integer, at least 0, and within the range <xref target="canon-params"></xref> fixes for
numbers;</li>
<li><tt>headHash</tt> MUST be <tt>&quot;sha256:&quot;</tt> followed by 64 lowercase hexadecimal digits;</li>
<li><tt>ts</tt> MUST be a valid RFC 3339 <tt>date-time</tt>, under the same rule and for the same reason as a
receipt's (<xref target="optional-members"></xref>);</li>
<li><tt>sig.alg</tt> MUST be the exact string <tt>ed25519</tt>; <tt>sig.kid</tt> and <tt>sig.value</tt> MUST be non-empty
strings; and <tt>sig.value</tt> MUST satisfy the encoding rules of <xref target="payload-binding"></xref>.</li>
</ul>
<t>A checkpoint failing any of these value rules is malformed, and so is one whose <tt>chain</tt> does not
match. Every such failure is a finding <strong>about the checkpoint</strong>: it MUST NOT establish
<tt>checkpoint-head-conflict</tt>, MUST NOT be reported as a finding about the chain, and MUST NOT by itself
turn an otherwise valid presented chain into a failure. The object the verifier was handed is
defective, which is a different statement from the records being defective; a verifier that
conflates the two lets anyone who can hand over a file manufacture an accusation about someone
else's history. What the verifier MUST do is report that it was given a checkpoint it could not use,
and answer the tail question as unanswered (<xref target="chain-axes"></xref>) -- the same outcome as being given no
checkpoint at all, because that is what it effectively has.</t>
<t><strong>Which key may sign it.</strong> A checkpoint is authenticated against the same keyring as the receipts.
Authentication either succeeds or it does not, and a verifier that cannot perform it -- because it
was given no keyring, or none holding that signer's key -- holds an <em>unauthenticated</em> checkpoint
rather than an absent one: it MUST report every checkpoint-derived conclusion as unauthenticated
(<xref target="chain-axes"></xref>), and MUST NOT fall back to comparing heads.
Key-level authentication alone, however, only establishes that <em>some</em> trusted key asserted this
head. To bind the assertion to the chain, a Chain Verifier <em>that was supplied an identity manifest</em>
(<xref target="identity-binding"></xref>) MUST require the checkpoint's <tt>sig.kid</tt> to be one of the keys that manifest
authorizes for the
<tt>agent.id</tt> of the <strong>genesis receipt</strong> -- the receipt at <tt>seq == 0</tt>, the chain opener. A checkpoint
signed by any other trusted key MUST be rejected as <tt>UNTRUSTED</tt> -- the result defined in
<xref target="identity-binding"></xref> for a valid signature by a key this identity is not authorized to use -- rather
than accepted as a tail check;
this is what stops a co-trusted key from certifying its own truncated head.</t>
<t>Two scoping rules follow, and both are limits rather than features. The comparison is between two
<strong>native</strong> key identifiers -- the checkpoint's own <tt>sig.kid</tt>, and the identifiers the manifest
authorizes for the genesis receipt's <tt>agent.id</tt> -- because a checkpoint is a native artifact and is
not carried in a COSE envelope; a deployment that wraps receipts for registration therefore has two identifier
spaces, and MUST NOT assume the outer envelope's <tt>kid</tt> is the one the manifest authorizes here. And
the guarantee is opener-scoped: on a chain carrying more than one <tt>agent.id</tt>, the opener's
checkpoint certifies the opener's view of the head and does not certify a co-agent's tail. A Chain
Verifier MUST NOT report an unqualified tail check on such a chain, and MUST state which agent's
view the check covers.</t>
<t><strong>What a checkpoint does not commit to, and what follows.</strong> The object above binds a chain
identifier, a sequence, a head hash and a timestamp. It does not bind the tenant, the issuer, the
agent set, the genesis hash, or any manifest version. Three consequences are normative for a Chain
Verifier:</t>

<ul spacing="compact">
<li><strong>Freshness is not self-evident.</strong> <tt>ts</tt> is signer-chosen, so an authentic checkpoint stays
authentic forever. This profile defines no expiry, and a verifier MUST NOT treat a checkpoint's
age as bounded by anything in the object. A deployment that needs freshness MUST impose a maximum
age from its own clock and say so in its output; an attacker who can replay an obsolete but
authentic checkpoint otherwise faces no barrier.</li>
<li><strong>The guarantee is opener-scoped, and a co-agent's tail is not covered.</strong> On a chain carrying
more than one <tt>agent.id</tt>, a checkpoint authorized for the opener says nothing about receipts
signed by a co-agent -- the requirement for that case is stated once, above.</li>
<li><strong>Key identifiers are pinned within a presented chain.</strong> The reference implementation binds each
<tt>agent.id</tt> to the first <tt>sig.kid</tt> it presents and rejects any later change as a key swap
(<xref target="implementation-status"></xref>). In-chain key rotation is therefore not expressible in this revision:
a deployment that rotates a signing key starts a new chain, and this profile defines no rotation
or revocation mechanism.</li>
</ul>
<t>Checkpoint issuance policy -- how often a checkpoint is minted, how it is distributed, and how a
relying party obtains one it did not receive from the prover -- is deployment-specific and is not
specified here. A verifier given no checkpoint performs no tail check at all. It MUST report the tail question as unanswered, and MUST NOT report it as an absence of truncation.
The absence vocabulary that names such a result is defined once, in <xref target="absence"></xref>; a Chain Verifier
that uses it is reading a definition, not inheriting an Evidence-Verifier obligation. Those are different
answers, which is what <xref target="absence"></xref> exists to keep apart.</t>
</section>
</section>

<section anchor="identity-binding"><name>Identity binding</name>
<t><em>Attribution addresses the <strong>Receipt Verifier</strong>, conditional on an identity manifest being supplied.
The relationship rules of <xref target="who-signed"></xref> address the <strong>Evidence Verifier</strong> and depend on artifacts
listed in <xref target="not-defined-here"></xref>.</em></t>
<t><tt>agent.id</tt> is a signer-asserted label. To authenticate WHICH agent acted (not merely that a
trusted key signed), a verifier MAY be supplied an identity manifest binding <tt>agent.id</tt> to its
authorized key identifiers.</t>
<t><strong>Each signature attributes a different party, and the manifest answers only one of the two
questions.</strong> An enveloped receipt carries two identifiers, and treating either as a substitute for
the other is the failure this subsection exists to prevent:</t>

<ul spacing="compact">
<li>The <strong>native</strong> <tt>sig.kid</tt> attributes the <strong>agent</strong>: it is the key that signed this receipt into its
chain, and it is the identifier <tt>agent.id</tt> makes a claim about. When a manifest is supplied, a
verifier MUST check the (<tt>agent.id</tt>, native <tt>sig.kid</tt>) pairing against it, and MUST reject an
unauthorized pairing as <tt>UNTRUSTED</tt> -- a result distinct from both &quot;valid&quot; and &quot;tampered&quot;: the
bytes are intact and the signature verifies, and the key that produced it is not one this
<tt>agent.id</tt> is authorized to use. A verifier MUST report that as its own outcome rather than
folding it into a signature failure, for the same reason <xref target="chain-axes"></xref> separates its conditions.</li>
<li>The <strong>outer</strong> COSE <tt>kid</tt> attributes the <strong>party that emitted this envelope</strong> -- an issuer
submitting its own receipt, or a relay presenting someone else's. A verifier that reports it as an
identity at all MUST take it from the protected bucket, since an unprotected identifier is not
covered by any signature. This profile defines no authorization list for that party: the manifest
binds agents to keys, not envelopes to emitters.</li>
<li>Therefore two rules, and they are the ones that close the laundering. A verifier <bcp14>MUST NOT</bcp14> use
an authorized outer <tt>kid</tt> to satisfy the agent check: an envelope signed by a key that happens to
be authorized for <tt>agent.id</tt> says nothing about who signed the receipt inside it. And a verifier
<bcp14>MUST NOT</bcp14> reject a receipt solely because its outer signer is not one of the agent's authorized
keys: a relay is a legitimate presentation, and the agent claim is carried by the native signature
it did not touch. A verifier MUST report the two results separately.</li>
</ul>
<t>A checkpoint has one signature and one claim: the manifest check is against the checkpoint's own
<tt>sig.kid</tt>, under the opener-binding rule of <xref target="checkpoint"></xref>.</t>
<t>Without a manifest, attribution is key-level only -- &quot;some key the verifier trusts signed this&quot; and
nothing about which agent -- and a verifier MUST surface that limitation to the relying
party.</t>

<section anchor="who-signed"><name>Who signed is not who approved</name>
<t><tt>governance.approval.by</tt> names an approver. The receipt's signature establishes that the <em>issuer</em>
emitted this record and has not since altered it; it does not establish that the named party held
any key, saw this action, or approved it before it ran. A relying party MUST NOT treat
<tt>governance.approval</tt> as pre-execution authorization by the named party. <xref target="authorization"></xref> states
what would be required instead.</t>
<t><strong>Two levels, and the difference between them is the whole answer.</strong> The strength of an approval
claim depends on what the relying party was given, and a specification that states only one level
misleads in one direction or the other.</t>

<ul spacing="compact">
<li><strong>A bare receipt, checked by a receipt verifier.</strong> <tt>governance.approval.by</tt> is an opaque string.
The verification rules of this profile neither resolve it against a key nor compare it to any
signer. It is covered by the issuer's signature -- so the issuer cannot alter the name after the
fact -- but the name and any key are unrelated <em>at this layer</em>: a receipt naming a person who has
never held a key verifies exactly like one naming the true approver. A relying party holding only
a receipt therefore holds a recorded name, not a cryptographic party.</li>
<li><strong>A complete evidence presentation.</strong> Where a deployment also produces the approval artifacts of
<xref target="authorization"></xref>, the approver IS a cryptographic party: a key enrolled as an approver, holding
the authority tier the action's risk class requires, neither revoked nor used before activation,
signs the decision artifact <em>and</em> the verdict receipt, and a verifier can require all three to be
the same key. What becomes checkable at this level is narrower than &quot;a human decided&quot;: it is that
<em>a key enrolled as an approver signed a decision bound to this action</em>, and its absence is fatal
rather than assumed. Whether a person held that key, saw the action, or understood it stays
outside every check in this profile, as the paragraph below states.</li>
<li><strong>An issuing gate MAY close the gap at emission time, and this profile recommends it.</strong> Nothing
prevents the party that mints a receipt from requiring <tt>governance.approval.by</tt> to name the key
that signed the approval, and refusing to issue otherwise. That is a producer-side control, so it
binds nobody downstream and a verifier MUST NOT assume it ran. What it changes is what the
<em>issuer knows</em>, not what a later reader can check: the receipt still carries an opaque string, and
no member records that the gate ran (<xref target="three-claims"></xref>). A holder of that receipt alone is in the
first case above, whatever the issuer's process was; the name becomes checkable only when the
approval artifacts of <xref target="authorization"></xref> travel with it. The reference implementation's gate and
grant signer both enforce the control (<xref target="implementation-status"></xref>).</li>
</ul>
<t><strong>A Receipt Verifier or Chain Verifier that reports anything about an approval MUST state that its
result rests on the opaque name alone.</strong> That requirement is testable: its input is a receipt and
its subject is what a receipt carries. The second level -- a name backed by the approval artifacts
of <xref target="authorization"></xref> -- is described here but carries no requirement in this revision, because those
artifacts are not defined by this document. A bare receipt is precisely where the
confusion arises -- and MUST NOT let a bare receipt's <tt>approval</tt> field be read as the second. <strong>Neither level establishes that a named human
physically held the signing device</strong>: that binding is an out-of-band enrolment ceremony, outside
every check either level performs, and it MUST be stated as a non-claim wherever an approval is
presented. Nor does either level establish that anyone read, understood, or consented to what was
displayed.</t>
<t><xref target="implementation-status"></xref> reports a shipped instance of the second level and of the producer-side control, and
states exactly which component leaves the name opaque and which refuses it.</t>
<t><strong>The rest of this subsection is NON-NORMATIVE in this revision.</strong> It runs over the signed outcome
artifacts and the trust configuration <xref target="not-defined-here"></xref> lists as undefined here, and over a
&quot;request signer&quot; this document can only describe by example. Nothing below can be tested against
this specification alone, so none of it is stated as an RFC 2119 requirement: it is what a companion
that defines those artifacts has to require, written now so that the companion inherits the analysis
instead of rediscovering it. An implementation cannot fail this subsection, and <xref target="open-questions"></xref>
records the work of turning it into requirements somewhere it can be tested.</t>
<t>Where an outcome about the action is <em>itself</em> signed, independence is decided by comparing the
<strong>outcome signer</strong> against <strong>two</strong> other parties, because a deployment that separates them has two
distinct keys and collapsing them hides a real dependency:</t>

<ol spacing="compact">
<li>the <strong>request signer</strong> -- the key that asked for the action (in the reference implementation, the
gate key that signs the request receipt, the execution grant and the hold records); and</li>
<li>the <strong>authorization signer</strong> -- the key that approved it (the enrolled approver device key that
signs the decision artifact and the verdict receipt).</li>
</ol>
<t>A verifier has to derive and report <strong>each</strong> relationship separately rather than a single merged
verdict: an outcome independent of the approver but signed by the requesting gate is not independent
evidence, and neither is its mirror image. Comparing against whichever key merely <em>emitted</em> the
receipt answers a third question and is not a substitute for either. Both relationships have to be
derived by the verifier from its own trust configuration. A self-declared relationship -- a producer
writing &quot;independent&quot; about itself -- is not evidence of independence, and an undetermined
relationship cannot be upgraded to an independent one.</t>
<t>A verifier that reports such a relationship draws the value from the vocabulary below -- closed,
though a verifier need not be able to produce every member of it (see the rule after the table) --
names the two parties it compared, marks the result as verifier-derived, and carries the identity of
the trust configuration it used -- for example a
trust-policy digest over the thresholds applied and a registry-snapshot digest over the key
material consulted -- so that the determination can be recomputed by anyone holding the same
configuration. This profile does not define those digests' constructions (<xref target="not-defined-here"></xref>), so
reproducibility here is scoped to parties who share a construction rather than promised in
general. What it requires is
that a verifier reporting a relationship publish enough for someone else to recompute it, and state
the construction where consumers of the result can find it; values computed under different
constructions are not comparable:</t>
<table>
<thead>
<tr>
<th>Relationship</th>
<th>Meaning</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>SAME_SIGNING_KEY</tt></td>
<td>one key signed both</td>
</tr>

<tr>
<td><tt>SAME_ADMINISTRATIVE_PARTY</tt></td>
<td>distinct keys under one administrative registry</td>
</tr>

<tr>
<td><tt>SEPARATE_ROLE_SAME_ORGANIZATION</tt></td>
<td>distinct authorized purposes, one organization</td>
</tr>

<tr>
<td><tt>INDEPENDENT_ORGANIZATION</tt></td>
<td>distinct organizations, established from the configuration</td>
</tr>

<tr>
<td><tt>UNKNOWN</tt></td>
<td>the configuration does not establish a relationship</td>
</tr>
</tbody>
</table><t>Two rules make this testable rather than decorative.</t>

<ul spacing="compact">
<li><strong>A verifier should not offer a value its configuration cannot derive.</strong> An enum member that no
input can produce is an invitation to claim it: a consumer reads the vocabulary, not the
derivation. A verifier that cannot establish organizational independence omits
<tt>INDEPENDENT_ORGANIZATION</tt> from what it can return, rather than returning it on weak evidence.</li>
<li><strong><tt>SAME_SIGNING_KEY</tt> is decided on key material, not on a key identifier.</strong> One key
presented under two identifiers, in two roles, is the same signer; a determination that compares
identifier strings alone reports independence to an attacker who registers a second name for one
key.</li>
</ul>
<t><tt>UNKNOWN</tt> is the default and is not a failure of the artifact; it is a statement about the
verifier's configuration. Where a consumer asks for independent evidence (<xref target="purpose"></xref>), no result
other than <tt>INDEPENDENT_ORGANIZATION</tt> satisfies that intent. Separate keys prove only
distinct keys; separate processes improve isolation; separate roles prove purpose authorization.
None of the three, alone, establishes independent administrative control. <xref target="implementation-status"></xref> reports a
shipped instance of this determination, including the value it deliberately does not offer.</t>
</section>
</section>

<section anchor="registration"><name>Registration in a Transparency Service</name>
<t><em>This section addresses the <strong>Transparency Service</strong>, except the first sentence, which addresses
the submitting <strong>Deployment</strong>.</em></t>
<t>A receipt carrying the protected claims of <xref target="signed-statement"></xref> is a Signed Statement and MAY be
submitted to a Transparency Service. A receipt without them is not, and MUST NOT be described as
registrable.</t>
<t>The Registration Policy for this profile is stated as a list because each item closes a specific
substitution. A Transparency Service registering a statement under this profile:</t>

<ol spacing="compact">
<li>MUST verify the outer COSE_Sign1 signature, and MUST verify it <strong>with a key it has bound to the
Issuer named in <tt>iss</tt></strong> -- the resolved key or certificate belongs to that Issuer, or an explicit
delegation from that Issuer to the signing key is presented and checked. <xref target="RFC9943"></xref>,
Section 6.3 states the same requirement in its own terms: the signature is verified with the
verification key <em>of the Issuer</em>. A service that cannot make that binding MUST NOT record the
statement as that Issuer's;</li>
<li>MUST verify the protected header conforms to <xref target="protected-header"></xref> -- an <tt>alg</tt> of <tt>-19</tt> and
deterministic CBOR -- and MUST require a <strong>protected</strong> <tt>kid</tt> for registration specifically. A
receipt whose <tt>kid</tt> is only in the unprotected header conforms to <xref target="protected-header"></xref> and is not
registrable under this policy: registration binds a subject to an issuer, and an identifier the
signature does not cover cannot do that. Producers intending to register MUST protect the <tt>kid</tt>;</li>
<li>MUST verify that protected <tt>CWT_Claims</tt> are present and carry both <tt>iss</tt> and <tt>sub</tt>;</li>
<li>MUST verify that the payload is a well-formed receipt under <xref target="payload"></xref>; and, <strong>where it can
resolve the native signer's key</strong>, MUST verify the payload's native signature. Registering an
envelope whose payload is a forged receipt is precisely the attack the outer signature does not
stop, since <xref target="payload-binding"></xref> permits the two signers to differ. This profile defines no way for
a Transparency Service to obtain that key -- the keyring is an out-of-band input (<xref target="terminology"></xref>)
-- so where the service cannot resolve it, <strong>it MUST refuse the registration</strong>. The alternative a
reader might expect, registering while attesting that the payload was not checked, is not
available: this profile defines no header, claim, log-entry field or media type in which that
status could travel, so a later reader could not distinguish it from a checked one. Refusal is
the only outcome this document can specify without inventing a carrier it has not defined. A
future revision that defines one MAY relax this;</li>
<li>MUST verify that <tt>sub</tt> equals the payload's <tt>scope.chain</tt>, so that the subject a log indexes is
the chain the statement is actually about; and</li>
<li><strong>The issuer/submitter relationship is the sixth item, and it is guidance rather than a
requirement</strong>, because this document cannot make it testable: a Transparency Service applies its
own policy to the relationship between the Issuer named in <tt>iss</tt> and the party submitting the
statement, and records which policy it applied. This profile does not require those two to be the same party, does not define the
policy, and -- decisively -- defines neither the minimum contents of such a record nor anywhere
it is carried, so a service that refused every submission under an unnamed policy would satisfy
any wording used here. What the profile can do is say plainly that the question exists and that a
service which never asks it is registering statements whose issuer relationship nobody checked.</li>
</ol>
<t><strong>What <tt>iss</tt> is worth, and where it stops.</strong> This profile defines no issuer key discovery: no
registry, no well-known location, no delegation format. Item 1 therefore states a requirement whose
<em>mechanism</em> lives outside this document, and a deployment must supply one. Until it does -- and for
any holder who has only the receipt, with no registration behind it -- <tt>iss</tt> is an <strong>issuer-asserted
string</strong>: it names the authority the signer claims to speak for, and nothing in the envelope proves
the relationship. A <strong>Transparency Service</strong>, and any <strong>Receipt Verifier</strong> that reads <tt>iss</tt> at all,
MUST NOT report it as an authenticated identity on the strength of a valid outer signature alone,
and MUST say which it had: an identity bound to the verification key, or a string the signer
asserted.</t>
<t>A Transparency Service knows this policy applies by agreement with the registrant, not by
inspection: no media type is registered for the payload (<xref target="iana-considerations"></xref>), so a service
cannot dispatch on content type alone. Until one exists, a deployment states the profile out of
band.</t>
<t>Items 4 and 5 are the ones a reader should not skim. Item 4 is what stops a trusted registrant
obtaining a log entry -- and a Transparency Receipt -- for a receipt nobody in the chain ever
signed. Its cost is stated rather than hidden: a Transparency Service that cannot resolve native
signer keys registers nothing under this profile, which is a real operational constraint and the
honest consequence of having no way to publish &quot;not checked&quot;. Without item 5
an issuer can register a chain-A receipt under <tt>sub = chain-B</tt>, leaving the log holding a subject
history that no receipt supports. On success the TS returns a Transparency Receipt: a third party's
signed proof that this statement was registered in its log. In <xref target="RFC9942"></xref>'s terms that is a proof
about the log's verifiable data structure, not the COSE countersignature header parameter of
<xref target="RFC9052"></xref>; the two are different constructs, and this profile means the first everywhere it says
Transparency Receipt.</t>
<t><strong>What registration is worth, stated exactly.</strong> An inclusion proof establishes membership in <em>the
view that log served</em>. Non-equivocation -- that the log did not serve one history to one party and
a different history to another -- is not established by any single receipt. It requires consistency
proofs between log states and a party that monitors them; this profile specifies neither the
monitoring role nor any requirement on anyone to perform it, and a relying party that wants
non-equivocation MUST obtain it from the Transparency Service's own ecosystem.</t>
<t><strong>The offline case gets the same scepticism this document applies to checkpoints.</strong> A Transparency
Receipt handed to an offline relying party <em>by the prover</em> is prover-supplied evidence, exactly like
the checkpoint of <xref target="checkpoint"></xref>: a better signer, and still no way for a party that never queries
the log to detect a log that served two views.</t>
<t>Two further honesty notes. Nothing in <xref target="hash-chaining-and-completeness"></xref> supplies any of this, so a
deployment that never registers has strictly less. And no reproducible demonstration of registration
against an external Transparency Service has been published for this profile
(<xref target="implementation-status"></xref>): the policy above is a specification, not a report of an executed
interop.</t>
</section>

<section anchor="external"><name>What a receipt does not claim about the external world</name>
<t><em>The non-claims in this section constrain what any actor may assert, and each is written against the
actor it binds. Two blocks are marked NON-NORMATIVE where they appear -- the claim-separation duty
of <xref target="three-claims"></xref> and the intent machinery of <xref target="purpose"></xref> -- because their inputs are artifacts
<xref target="not-defined-here"></xref> lists as undefined; the absence vocabulary of <xref target="absence"></xref> is normative, because a
verifier's own results are something this document can constrain.</em></t>
<t>A receipt in this profile is emitted at the <em>governed boundary</em>: the point where an action was
admitted, refused, deferred, or dispatched. Everything beyond that boundary -- what a downstream
controller did with the request, and what happened physically -- is outside the record.</t>

<section anchor="three-claims"><name>Three claims, three independent failures</name>
<t>The following are distinct claims. They may share one action digest as a join key
(<xref target="digest-boundary"></xref>), but digest equality proves none of them. Each has to be evaluated and reported
separately, so that one can fail while the others stand -- a requirement for whichever document
defines claims 2 and 3, since this one does not (<xref target="not-defined-here"></xref>):</t>

<ol spacing="compact">
<li><strong>Pre-execution authorization</strong> -- a named, accountable principal approved <em>this exact action</em>
before it ran.</li>
<li><strong>Controller-reported outcome</strong> -- an identified downstream system reported a terminal status
for the request, under its own semantics.</li>
<li><strong>Physical completion</strong> -- an identified witness observed the intended effect, against a
criterion fixed before execution.</li>
</ol>
<t>A receipt under this profile carries NONE of claims 2 and 3, and carries claim 1 only as the
signer-asserted label described in <xref target="who-signed"></xref>. A deployment MAY apply the producer-side control <xref target="who-signed"></xref> recommends -- refusing to issue
unless <tt>governance.approval.by</tt> names the key that signed the approval -- but no receipt carries
evidence that the control ran, so a verifier MUST NOT report anything about it. Whether the control
was applied is a fact about the issuer's process, established outside this profile or not at all. These are NORMATIVE non-claims, <strong>scoped to what a receipt establishes</strong>. A <strong>Producer</strong> MUST NOT
present a receipt as evidence of any of the three. A verifier evaluating a receipt -- alone or as
part of a chain -- MUST NOT return a result whose named fields assert any of them <strong>on that basis</strong>,
and where a consumer asked for one it MUST report that the receipt does not establish it. For claims
2 and 3 <xref target="purpose"></xref> names the intent the consumer asked for and the evidence it would require. For
claim 1 it does not: this profile defines no intent token for pre-execution authorization, so the
report carries the non-claim above rather than a name from <xref target="purpose"></xref>.
The prohibition is about the basis of an assertion rather than about the claims themselves: a
verifier that also holds an authenticated outcome or observation artifact may report what that
artifact establishes, which is the case <xref target="purpose"></xref> describes.</t>
<t>In particular, <tt>governance.verdict == EXECUTED</tt> records that the action was dispatched, or
attempted, at the governed boundary. It does not attest that a controller accepted the request,
that the request succeeded, or that any physical effect followed. A controller reporting
&quot;succeeded&quot; attests success under its own semantics; that is a report, and a report is not an
effect.</t>
</section>

<section anchor="purpose"><name>Validity and sufficiency are separate results</name>
<t>A receipt may be cryptographically impeccable and still be the wrong evidence for the question
being asked. Two questions therefore have to be kept apart:</t>

<ul spacing="compact">
<li><strong>artifact validity</strong> -- signature, canonical form, chain position, identity binding;</li>
<li><strong>sufficiency for the consumer's intent</strong> -- whether what was presented answers the question
asked.</li>
</ul>
<t><strong>One of the four intents is normative here and three are not</strong>, and the line is drawn by what this
document defines. A verifier that supports intents at all MUST answer <tt>BASE_ACTION_RECEIPT</tt> from
receipt validity alone, and MUST NOT report it as satisfied for an object that fails <xref target="payload"></xref> or
<xref target="cose"></xref>: its predicate is defined here in full, so two implementations can be tested against the
same answer. The other three are satisfied only by artifacts <xref target="not-defined-here"></xref> lists as undefined,
and the machinery around them -- how an intent is supplied, how sufficiency is compared, how the
answer is carried -- is undefined with them. <strong>Those three rows and the paragraphs that follow the
table are NON-NORMATIVE in this revision</strong>, for that reason -- with one exception, labelled where it
appears at the end of this subsection, which is normative because its subject is a receipt. It names four intents so that the common questions have shared spellings, and defines
each by the evidence that answers it:</t>
<table>
<thead>
<tr>
<th>Intent</th>
<th>Satisfied only by</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>BASE_ACTION_RECEIPT</tt></td>
<td>a receipt valid under <xref target="payload"></xref> and <xref target="cose"></xref></td>
</tr>

<tr>
<td><tt>CONTROLLER_OUTCOME</tt></td>
<td>an authenticated controller-outcome artifact bound to this action. <xref target="separate-artifacts"></xref> describes what such an artifact must say; <strong>this revision defines no wire format for it</strong> (<xref target="not-defined-here"></xref>)</td>
</tr>

<tr>
<td><tt>PHYSICAL_COMPLETION</tt></td>
<td>an authenticated physical-observation artifact bound to this action, from a witness the verifier trusts for that purpose. Same status: described in <xref target="separate-artifacts"></xref>, <strong>not defined here</strong></td>
</tr>

<tr>
<td><tt>INDEPENDENT_OUTCOME</tt></td>
<td>one of the two artifact-based intents above -- <tt>CONTROLLER_OUTCOME</tt> or <tt>PHYSICAL_COMPLETION</tt> -- whose signer the verifier derives as <tt>INDEPENDENT_ORGANIZATION</tt> (<xref target="who-signed"></xref>). A receipt cannot satisfy it: the receipt's issuer is the party whose account is in question, so it is never independent of itself</td>
</tr>
</tbody>
</table><t>Only the first is satisfiable from a receipt alone. The other three need artifacts
<xref target="not-defined-here"></xref> lists as undefined in this revision, and <tt>INDEPENDENT_OUTCOME</tt> is additionally
unsatisfiable by any verifier that cannot derive organizational independence -- which, per
<xref target="implementation-status"></xref>, is every implementation this document reports on. A verifier that cannot
satisfy an intent reports that in those terms rather than as a failure of the artifact.</t>
<t>A verifier that supports intents should support at least these four and report a <em>named</em>
insufficiency rather than a generic failure: a relying party that asked for physical proof and
received a valid receipt has learned something specific, and collapsing that onto &quot;invalid&quot; destroys
it. Where two authenticated claims conflict -- a controller reporting success and a witness
reporting that the effect did not occur -- the conflict is its own result, carrying both claims,
with neither erased in favour of the other; this document defines no token for it
(<xref target="not-defined-here"></xref>).</t>
<t><strong>One rule here is normative, because it constrains what may be said about artifacts this document
does define.</strong> A verifier MUST NOT report an unqualified &quot;physical completion proven&quot;. The strongest
defensible result is proven <em>to the stated policy</em>, naming the trust configuration that made it so,
and a receipt never supplies it (<xref target="three-claims"></xref>).</t>
</section>

<section anchor="absence"><name>Absence is not a negative claim</name>
<t>A verifier MUST keep the following distinct, and MUST NOT collapse them:</t>
<table>
<thead>
<tr>
<th>Result</th>
<th>Meaning</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>NOT_CLAIMED</tt></td>
<td>the producer made no claim on this point</td>
</tr>

<tr>
<td><tt>NOT_PROVIDED</tt></td>
<td>a claim may exist, but the verifier was not given acceptable evidence of it</td>
</tr>

<tr>
<td><tt>NOT_APPLICABLE</tt></td>
<td>the point does not arise for this action</td>
</tr>

<tr>
<td><tt>INDETERMINATE</tt></td>
<td>evidence was presented but does not settle the question</td>
</tr>
</tbody>
</table><t>&quot;The verifier did not receive acceptable evidence&quot; is not &quot;no evidence exists&quot;, and rendering the
first as the second turns a gap in a presentation into a negative finding about the world. This is
the same discipline <xref target="chain-axes"></xref> applies to set-level failures: name the condition, do not merge
it. A verifier MUST NOT define a result code that asserts a negative fact about the world it did
not observe; <xref target="implementation-status"></xref> records a shipped verifier that carries no such code by design.</t>
<t><strong>A finding names the object that failed, and no other.</strong> The rule the paragraph above is one
instance of, stated once so that an implementer can apply it to the case this document did not
foresee: <strong>a failure to authenticate or to understand one object MUST NOT produce a finding about a
different object.</strong> The subject of the finding is the object that failed.</t>
<t>The rule is about <em>whose</em> failure is being reported, not about whether failures may be reported at
all. A receipt whose own signature does not verify is <tt>TAMPERED</tt> -- a finding about that receipt,
and the correct one. A receipt this profile cannot parse is malformed -- again about that receipt.
What is forbidden is transferring the failure: an unauthenticated checkpoint yields
<tt>checkpoint-unverified</tt>, a finding about the checkpoint, and never a finding about the chain
(<xref target="chain-axes"></xref>); a checkpoint whose structure or scope is wrong is a finding about the checkpoint
(<xref target="checkpoint"></xref>); a trust document a verifier does not recognise is a finding about that document,
never about the receipts it was supposed to authenticate. In each case the honest object -- the
chain, the receipts -- is not accused of anything, because nothing about it failed.</t>
<t>This matters because the accusation is the whole thing these artifacts exist to make credible. A
verifier that turns its own inability to read one file into a finding against someone else's records
has produced the one output a verification tool must never produce, and
<xref target="implementation-status"></xref> records two measured places where a shipped implementation does exactly
that.</t>
</section>

<section anchor="separate-artifacts"><name>Why the base record is not extended</name>
<t>A machine-checkable non-claim carried as a field of the base receipt -- for example a
<tt>physical_completion</tt> member defaulting to <tt>&quot;none&quot;</tt> -- was proposed on the SCITT list and is
<strong>not</strong> adopted here, for two reasons that are worth stating rather than asserting.</t>
<t>First, mechanics: adding a member to <tt>noa.receipt/0.1</tt> changes the JCS bytes of every receipt that
carries it, and therefore the receipt hash, the native signature, the <tt>prevHash</tt> linkage, every
checkpoint over that chain, and every archived conformance vector. That cost is payable only at a
new wire version with a new signing domain, never by mutating a version other implementations have
already frozen.</t>
<t>Second, semantics: a default value on a frozen record cannot distinguish &quot;the producer considered
physical completion and claims nothing&quot; from &quot;the producer has never heard of the question&quot;. A
default is not a statement.</t>
<t>The claims of <xref target="three-claims"></xref> therefore belong in <strong>separate signed artifacts</strong>, each linked to
the authorization by hash -- at minimum the hash of the receipt that carried the authorizing verdict
and the hash of the execution grant, and OPTIONALLY a shared action digest (<xref target="digest-boundary"></xref>) -- and each carrying its own
signer, its own vocabulary, and its own failure. A controller-outcome artifact says what a
controller reported; a physical-observation artifact says what a witness observed, under a named
method, in a named window, against a criterion digest fixed before execution. Motor or controller
telemetry alone is not an observation of the object.</t>
<t>This revision does not define those artifacts' wire formats. It fixes the vocabulary and the
failure model they must satisfy, so that the work can land in one place instead of fragmenting
across profiles, and so that a receipt consumer today can be told exactly which question a receipt
does not answer.</t>
</section>
</section>

<section anchor="authorization"><name>Pre-execution authorization and post-execution evidence</name>
<t><em>This section is addressed to the <strong>Evidence Verifier</strong> (<xref target="classes"></xref>), over artifacts this profile
does not define (<xref target="not-defined-here"></xref>), and is <strong>NON-NORMATIVE in its entirety</strong>: no bullet below
carries an RFC 2119 requirement, none of them can be failed, and there is no exception. The
requirements they point at live where they are testable -- <xref target="three-claims"></xref> states the non-claims a
receipt cannot escape, and <xref target="external"></xref> states what a bare receipt cannot establish. A Receipt or
Chain Verifier neither satisfies nor fails anything in this section.</em></t>
<t>Authorization and evidence differ in time, in signer, and in failure mode. An authorization is
made before the action, by a party accountable for allowing it, and fails if that party did not
approve <em>this exact action</em>. Evidence is made after the action, by a party that observed
something, and fails if the observation is not bound to the same action or its signer is not
trusted for the purpose.</t>
<t>The substitution these differences permit is the one this section is about. All three bullets run
over artifacts, comparisons or results this document does not define -- the hold, decision and
execution-grant records of <xref target="not-defined-here"></xref>, and the query that would ask about a named human --
and no binding defined here establishes which grant authorized a particular attempt, or that it
preceded the attempt: <tt>ts</tt> cannot supply the ordering, because it is signer-chosen
(<xref target="optional-members"></xref>). They are written as what a companion defining those artifacts has to require.</t>

<ul spacing="compact">
<li>A verifier presented with a <strong>post-execution record</strong> where a <strong>pre-execution authorization</strong> was
required has to refuse and name the failing check. The refusal is not avoided because the record's
signature verifies: attribution records who was recorded as acting; it never grants authority.</li>
<li>A <strong>machine policy decision</strong> is not a human authorization. A receipt carrying
<tt>governance.verdict == ALLOWED</tt> under an automatic rule records that a policy allowed the action.
Where a consumer asks whether a named human authorized it, that receipt has to yield a result
distinct from an accept -- and the reason this is not a requirement here is worth naming: &quot;a named
human authorized it&quot; is not one of the intents this document defines, the identity is supplied by
nothing defined here, no comparison against the receipt is specified, and no token or result
schema carries the answer (<xref target="not-defined-here"></xref>). The requirement that does exist is the non-claim,
and it lives where its input is a receipt: <xref target="three-claims"></xref> forbids presenting a receipt as
evidence that a named human authorized the action. Per <xref target="absence"></xref>, an unproven result is also not
a finding that no human approved.</li>
<li>An authorization is bound to <strong>one execution attempt</strong> or it is not an authorization for the
attempt that happened. Where a deployment issues single-use, time-bounded execution grants, a
verifier has to check that the grant presented is the grant that authorized <em>this</em> attempt, and
refuse a valid grant for a different attempt rather than accept it as generic permission. What
makes that checkable -- a grant identifier, a single-use nonce, an audience and an expiry, and a
binding to the attempt -- is exactly what this profile does not define.</li>
</ul>
<t>A relying party that needs the authorization claim needs artifacts this profile does not define:
a signed hold or deferral, a signed decision by a party holding an approval role, and a single-use
execution grant, each under its own signing domain. <xref target="implementation-status"></xref> reports what the reference
implementation does today, so that the gap between this profile and that implementation is a
measurement rather than an impression.</t>
</section>

<section anchor="digest-boundary"><name>The shared action digest boundary</name>
<t><em>The first rule below binds the <strong>Producer</strong> that emits a digest -- it must travel with a
construction identifier -- and every actor that compares one, in practice an <strong>Evidence Verifier</strong>.
The second rule binds whoever reads the comparison.</em></t>
<t>Records about one action are only comparable if they carry a value that means the same thing to
both producers. Two rules make that possible, and this profile states both while defining neither
construction.</t>
<t><strong>Determinability, not assumption.</strong> <strong>This rule -- the one paragraph below, and nothing else in
this section -- is NON-NORMATIVE here</strong>: the digest it governs is not a member of this record and is
defined by a companion (<xref target="not-defined-here"></xref>), and it is written because a companion defining one has
to carry it. The two requirements at the end of this section are normative and are labelled there. Any digest offered as a cross-producer join key
travels with a construction identifier, and that identifier commits to the construction's
canonicalization parameters -- serialization, member sort, number rendering, normalization,
escaping, digest algorithm, and domain separation. A verifier presented with a digest under an
unrecognised or non-matching construction identifier does not compare the values at all. The
comparison then yields nothing -- not a match, and not a mismatch -- and is reported
as <strong>indeterminate</strong>: &quot;these records were not shown to be about the same action&quot;, never &quot;these
records are about different actions&quot;. That is the same distinction <xref target="absence"></xref> draws; a question the
verifier could not reach is not a negative answer to it. Two constructions that differ only in whether they apply Unicode
normalization will agree on almost every input and disagree on the ones that matter; a shared
vector, not a shared name, is what establishes that two parties compute the same value.</t>
<t><strong>Correlation is not attestation.</strong> Digest equality establishes that two records commit to the same
<em>action projection</em> -- the same field values under the same construction. It establishes nothing
about whether that action was authorized, whether a controller reported anything, or whether any
effect occurred; each of those fails independently (<xref target="three-claims"></xref>). A digest is a join key, and a
join key that is mistaken for an authorization claim is the substitution <xref target="authorization"></xref> forbids.</t>
<t><strong>Two rules do stay normative here, because their subject is this record.</strong> <tt>action.paramsHash</tt>
does not satisfy the first rule above and MUST NOT be used as a join key (<xref target="canon-params"></xref>); and a
verifier MUST NOT treat equality of any digest as evidence that an action was authorized, which is
the substitution <xref target="authorization"></xref> is about. Nor does the base record carry a member for a shared digest: <tt>noa.receipt/0.1</tt> is
closed (<xref target="payload"></xref>), so a correlation value travels <em>beside</em> a receipt in whatever artifact
references it, never inside one. A deployment that wants correlation does not modify the receipt.</t>
<t>A construction meeting both rules is published: <xref target="I-D.toraman-noa-action-digest"></xref> fixes the
projection members, the domain tag, the preimage, the digest, and a published conformance corpus
(counted in <xref target="implementation-status"></xref>). One consumer of it is <xref target="I-D.toraman-noa-settlement-evidence"></xref>. This
profile does not restate that construction, does not require it, and would apply the same two
rules to any other.</t>
<t><strong>On indeterminate positive controls.</strong> In an independent reconciliation run reported on the SCITT
list on 27 July 2026 (<eref target="https://mailarchive.ietf.org/arch/msg/scitt/zWZBQXXSGV-vgbTeOLTjlb9DGLg/"/>),
a <em>positive</em> control was held INDETERMINATE because the digest it carried came from a construction
the reconciler had no vector for. That is the rule above working, not a harness defect: a positive
control that upgrades itself on an unverified binding makes every negative case unreliable too.</t>
<t>Three things follow, and they are different things.</t>

<ul spacing="compact">
<li><strong>What is now pinned.</strong> The correlation construction this profile's producers use is published
byte-exactly, with its vectors, so a reconciler holding those values has something to compute
against rather than assume (<xref target="implementation-status"></xref>).</li>
<li><strong>What that does not do.</strong> The control that came back INDETERMINATE carries another party's
digest under that party's construction. Publishing ours does not upgrade it, and this profile
does not claim otherwise: that row upgrades when its own preimage rule and vectors are published.
The rule is symmetric.</li>
<li><strong>What is still open on our side, for a digest we did not define.</strong> A party computing the
reconciliation layer's own subject digest over a receipt's <tt>action</tt> object cannot do so
reproducibly today, because this profile has never fixed that object's encoding: <tt>rollbackRef</tt> is
optional, and present-as-null and omitted produce different digests. Every receipt in the shipped
corpus carries it, which is a convention, not a rule an implementer can cite. Any party wanting
to compute a digest over <tt>action</tt> therefore needs two things this revision does not provide: a
normative rule fixing the encoding, and an expected value published per conformance vector so a
positive control has something to equal. Both are named here as work rather than left for a
harness to discover -- which is the same rule this section applies to everyone else.</li>
</ul>
</section>

<section anchor="policy-replay-is-out-of-scope-for-this-revision"><name>Policy replay is out of scope</name>
<t><em>This section addresses the <strong>Producer</strong> and every verifier: none of them may present a receipt as
carrying a re-derivable verdict.</em></t>
<t>A deterministic, offline <strong>policy-replay</strong> capability -- a verifier re-running the in-force
policy over the recorded inputs to re-derive the recorded verdict, with no access to the agent,
model, or any service -- is a distinct capability that this revision deliberately does NOT
specify. The optional commitment of <xref target="optional-members"></xref> carries hashes of a policy, its read-set,
and the recorded inputs; it does not carry the replay <em>construction</em> (the deterministic evaluation
grammar, operator set, evaluation order, and conformance vectors), which is left to a separate
companion profile and is not normative here. A <strong>Producer</strong> and every verifier MUST NOT represent a receipt under
this profile as carrying a re-derivable verdict, and MUST NOT infer from a present commitment that
the referenced policy was adequate or in force.</t>
</section>

<section anchor="implementation-status"><name>Implementation status</name>
<t><em>This section is to be removed by the RFC Editor before publication, per <xref target="RFC7942"></xref>. Removing it
will renumber what follows, so a citation of this revision by section number should be read against
this revision. References to this section from the body are pointers to measurement, never to
normative content: no requirement depends on it.</em></t>
<t>A zero-runtime-dependency reference implementation (signer, offline verifier, deterministic
CBOR, COSE_Sign1 profile, JCS canonicalizer, conformance vectors, and separately implemented
verifiers in four further languages) is available under the Apache-2.0 license at
<eref target="https://github.com/NordenSoft/noa"/>. Unless explicitly stated otherwise, the measurements below are
from <tt>main</tt> commit <tt>4764cab</tt>. Later repository changes are identified by their own commit hashes and
are not included in the pinned snapshot's counts. No number in this section is an estimate: each
was produced by running a command or counting files in that tree. Three classes of measurement are
reproducible from what is written here -- the corpus counts (three are file counts in the named
directories; the action-digest and settlement-evidence corpora are each a single <tt>vectors.json</tt>
whose entry array is counted, which is why counting files there gives the wrong answer), the
conformance run (<tt>node impl-py/conformance.mjs</tt>), and the repository facts cited to a path. The
rest -- the 25-verdict cross-field measurement below, the divergence runs of D1, and the decoded
header bytes -- were produced by ad-hoc scripts that are not committed, so they are reported as
measurements with their inputs described rather than as anything a reader can re-run today.
Committing them is open work.</t>
<t><strong>Algorithm identifier.</strong> The implementation emits the fully-specified Ed25519 identifier <tt>-19</tt>
(<xref target="RFC9864"></xref>) and rejects every other <tt>alg</tt>, including the polymorphic <tt>EdDSA</tt> identifier <tt>-8</tt>,
which <xref target="RFC9864"></xref>, Section 4.2.2 marks Deprecated (it was registered in <xref target="RFC9053"></xref>). There is one emission site and one verification gate; no code path
emits <tt>-8</tt> and no legacy-compatibility flag exists.</t>
<t>The protected header it builds is the deterministic CBOR map <tt>{1: -19, 4: kid}</tt> -- two members,
because the <tt>kid</tt> is in the <em>signed</em> bucket, so attribution cannot be swapped without breaking the
signature. Its bytes therefore begin <tt>a2 01 32 04 ...</tt>, the trailing part being the <tt>kid</tt> byte
string; a measured envelope's header reads <tt>a2 01 32 04 49 ...</tt> for a nine-octet <tt>kid</tt>. A forged
envelope carrying <tt>-8</tt> under a genuine Ed25519 signature -- the only construction that tests the
<tt>alg</tt> pin rather than the signature -- is rejected at the product API with &quot;protected header alg is
not Ed25519 (-19, RFC 9864)&quot;; so are <tt>-7</tt>, <tt>alg</tt> as a text string, a non-canonical two-byte
encoding of <tt>-19</tt>, a duplicated <tt>alg</tt> label, and an absent <tt>alg</tt>.</t>
<t><strong>One implementation, stated as such.</strong> The <tt>-19</tt>/<tt>-8</tt> behaviour above is a property of a single
COSE implementation. The separately implemented Python verifier does not implement COSE at all, and
the Go, Rust and C# verifiers contain none, so they provide no confirmation in either direction.
A third-party check does exist and did run -- a foreign stack (<tt>cbor2</tt> + PyCA <tt>cryptography</tt>) reads
<tt>alg = -19</tt> from a shipped vector, verifies the Ed25519 signature over the RFC 9052
<tt>Sig_structure</tt>, and rejects a tampered payload -- but that corpus holds one vector and it is a
<em>positive</em> one. At the pinned snapshot that vector's own metadata still carried the stale <tt>a10132</tt>
header string beside an envelope whose protected header holds both <tt>alg</tt> and <tt>kid</tt>, so the file
contradicted itself; <tt>df777a6</tt> corrected it. There is no cross-implementation NEGATIVE vector for <tt>-8</tt>; that refusal is proven
only inside this implementation's own tests, and closing it is one of the open vector commitments
recorded below.</t>
<t><strong>The two signatures.</strong> <tt>receiptToCose</tt> wraps the JCS-canonical receipt as the COSE_Sign1 payload;
<tt>receiptFromCose</tt> verifies the COSE signature, re-canonicalizes the payload and requires byte
equality with the signed bytes, and validates the receipt shape -- it does <strong>not</strong> verify the
native <tt>sig.value</tt> or any chain property, which is the separate <tt>verifyChain</tt> path. The two checks
are genuinely independent, which is what <xref target="payload-binding"></xref> says they are; what no entry point does
is run <em>both</em>, recorded as a gap below.</t>
<t><strong>Chain-level axes.</strong> All six chain conditions of <xref target="chain-axes"></xref> are detected and reported
distinctly, under the implementation's own reason strings rather than under this document's labels:
a broken link, a duplicate sequence number, a sequence gap, more than one chain partition in one
input, the cross-tenant split, and a mid-chain key change for one agent -- the last reported as a
&quot;key swap&quot;, in both the TypeScript and the separately implemented Python verifier. What it gets
wrong is the <strong>checkpoint axis</strong>, on both of its named outcomes: <tt>checkpoint-unverified</tt> is not
distinguished at all, and <tt>checkpoint-head-conflict</tt> is reported over-broadly by treating a
legitimate extension as one. Both are in the gap list below. <tt>scope.tenant</tt> drift across one chain is
fail-closed by default, and absence of the member does not reset the comparison.</t>
<t><strong>On the word &quot;separately&quot;.</strong> What is demonstrated is five implementations in five languages that
agree on a shared corpus, not five independent derivations of this specification: the Go, Rust and
C# sources describe themselves as ports of the Python reference, and the Python one was written
against the TypeScript one. Agreement between a source and its port is weaker evidence than
agreement between two independent readings of a specification, and this document does not claim the
stronger thing.</t>
<t><strong>Cross-implementation conformance, run rather than cited.</strong> <tt>node impl-py/conformance.mjs</tt>,
executed at the pinned <tt>4764cab</tt> snapshot on 2026-08-14, ran <strong>99 checks</strong> and exited <strong>0</strong>, with its own summary line reporting that
the separately implemented Python verifier agrees with the TypeScript reference on every vector, including the
impersonation, truncation and duplicate-key security verdicts. The Go, Rust and C# verifiers are
each compared against the Python one over a shared file-based corpus, and the TypeScript reference
is compared against it too, so the topology is a star with the Python reference at its centre rather
than five implementations tested against one another; what that can and cannot settle is stated at
the end of the register below. No claim of demonstrated external interoperability across all five
is made here.</t>
<t><strong>Conformance gaps against this revision's own rules, at the pinned snapshot.</strong> What follows is the
output of two independent reverse audits -- code read first, then the requirement it should have
mapped to -- and it is deliberately long. An earlier draft of this section listed six gaps and
called that the whole count; the claim was false, and the correction is to publish the register
rather than to shorten it. &quot;All five&quot; below means the TypeScript reference and the separately
implemented Python, Go, Rust and C# verifiers. A fix for the first three is in flight in the reference
repository; this register describes the code as measured at the pinned snapshot, not the code as it
will be.</t>
<t><strong>One measurement covers the first three rows, and it was run rather than reasoned.</strong> Five receipts
were built, each carrying <tt>action.riskClass: CRITICAL</tt> on a <tt>wire.transfer</tt> action, and each was
presented as identical bytes to all five verifiers: (A) <tt>agent.principal: SANDBOX_SIM</tt> with
<tt>governance.sandboxed: false</tt> and <tt>governance.verdict: EXECUTED</tt>; (B) <tt>governance.verdict:
SIMULATED</tt> with <tt>governance.sandboxed: false</tt>; (C) <tt>action.reversible: false</tt> with
<tt>action.rollbackRef: &quot;snap_1&quot;</tt>; (D) <tt>governance.verdict: ROLLED_BACK</tt> with <tt>action.reversible:
false</tt>; (E) <tt>ts: &quot;2026-13-45T99:99:99.000Z&quot;</tt>. <strong>All 25 combinations returned VALID.</strong></t>
<t>A and B are the worst of the five, for the reason this profile exists at all: a reader has to be
able to tell a real action from a rehearsal. A and B are signed, chain-valid, fully verifying
receipts for an action the issuer itself classified <tt>CRITICAL</tt>, and they can be argued in both
directions afterwards -- the issuer can say the record shows a simulation, the counterparty can say
it shows an execution, and the receipt supports both readings. The receipt carries no amount and
this profile defines no way to open <tt>paramsHash</tt> (<xref target="payload"></xref>), so nothing here establishes what the
action was worth; the risk class the issuer assigned is the whole of what the record says about its
seriousness.</t>

<ul spacing="compact">
<li><strong>G1 -- contradictory simulation markers are accepted.</strong> <xref target="field-semantics"></xref> requires
<tt>governance.sandboxed: true</tt> whenever <tt>agent.principal</tt> is <tt>SANDBOX_SIM</tt> or <tt>governance.verdict</tt>
is <tt>SIMULATED</tt>. The requirement is satisfied by no shipped verifier: each validates the primitive
type of the three members and stops, so cases A and B verify in all five.</li>
<li><strong>G2 -- contradictory rollback markers are accepted.</strong> <xref target="field-semantics"></xref> requires
<tt>action.rollbackRef</tt> to be absent or <tt>null</tt> unless <tt>action.reversible</tt> is <tt>true</tt>, and a
<tt>ROLLED_BACK</tt> verdict to be accompanied by <tt>action.reversible: true</tt>. Neither relationship is
checked by any shipped verifier; cases C and D verify in all five.</li>
<li><strong>G3 -- a timestamp's shape is checked, its meaning is not.</strong> <xref target="optional-members"></xref> requires a
verifier to reject a value that is not a valid RFC 3339 <tt>date-time</tt>. Every implementation
validates the shape of the string and none of them asks whether it denotes an instant, measured
rather than inferred: case E -- month 13, day 45, hour 99 -- verifies in all five. The reference's
own source records why it stays there, and the reason is narrower than an earlier draft of this
register claimed: tightening the predicate changes no bytes, no digest and no signing domain, so
it is <strong>not</strong> a wire-format change. What it changes is the set of already-signed receipts an
existing verifier accepts, which the pinned source calls &quot;an API change, never a format change&quot; --
five implementations have to move together or they disagree on receipts nobody can re-sign. That
is a release-coordination problem, not a version-of-the-record problem.</li>
<li><strong>G4 -- no entry point is a conforming Receipt Verifier.</strong> <xref target="payload-binding"></xref> requires a Receipt
Verifier given an enveloped receipt to run both signature checks and to report which produced its
result. <tt>receiptFromCose</tt> verifies the outer COSE signature, re-canonicalizes the payload and
validates its shape but never touches <tt>sig.value</tt>; the native signature is checked in the chain
path. A caller can obtain both properties by calling both, and nothing prevents it; what the
repository does not contain at the pinned snapshot is a caller that does. The Receipt Verifier
this profile defines is not an entry point the library exposes, and not one its own callers
assemble.</li>
<li><strong>G5 -- whole-payload canonicality is proven on one path only.</strong> <xref target="payload-binding"></xref> requires
<tt>JCS(parse(bytes)) == bytes</tt> over the bytes as received. <tt>receiptFromCose</tt> performs exactly that
comparison; the bare chain path in all five is handed parsed values and cannot prove the original
bytes were canonical.</li>
<li><strong>G6 -- an unauthenticated checkpoint produces an accusation.</strong> <xref target="chain-axes"></xref> requires
<tt>checkpoint-unverified</tt> and forbids a truncation or tampering finding. With a keyring, all five
answer <tt>TAMPERED</tt>; the four that carry a reason string give &quot;checkpoint not authenticated against
keyring&quot; or their own wording of it, and the Go verifier returns the status token alone. Without a
keyring the head comparison runs anyway: an honest, complete chain presented together with an
attacker-signed checkpoint returns <tt>TAMPERED</tt> in all five -- measured, on identical bytes -- under
the reference's message &quot;tail truncated/extended&quot;. Neither branch produces the indeterminate result this revision requires, and both are the
violation of <xref target="absence"></xref> in its clearest form: a file anyone can hand the verifier becomes a
finding against someone else's records. It is also the shape <xref target="chain-axes"></xref> now forbids in as many
words: the obligation keys on whether the checkpoint authenticated, and every shipped verifier
branches on whether a keyring was passed.</li>
<li><strong>G7 -- a legitimate extension is reported as a head failure.</strong> <xref target="chain-axes"></xref> requires a verifier
to distinguish a chain that has grown past an older checkpoint from one that conflicts with it.
All five compare the checkpoint tuple against the current head only and report a single verdict
for both cases. In the reference the message says so in as many words -- &quot;tail
truncated/extended&quot;, which also names a cause <xref target="chain-axes"></xref> no longer permits the comparison to
name; the Go verifier returns the status alone, so there the two cases are indistinguishable even
in principle.</li>
<li><strong>G8 -- no Signed Statement mode exists, on either side, and that is a capability absence rather
than a rule broken.</strong> The producer emits <tt>{1: -19, 4: kid}</tt> and no <tt>CWT_Claims</tt>, so what it mints
is not registrable (<xref target="signed-statement"></xref>); the verifier ignores label 15 as a non-critical header,
and a committed test shows it accepting a protected header carrying a malformed <tt>15: bstr</tt> as a
perfectly good generic COSE_Sign1. No code path anywhere evaluates a receipt <strong>as a Signed
Statement</strong>. This profile requires no Producer to implement Signed Statement production -- bare
receipts are a defined presentation (<xref target="cose"></xref>) -- so the honest statement is that the reference
cannot be registered, not that it violates a requirement. It is listed here because a reader who
assumes registrability would be wrong, and because item 1 of the list further below says the same
thing about the verifier half.</li>
<li><strong>G9 -- only the first condition that holds is reported.</strong> <xref target="chain-axes"></xref> requires a verifier to
report every condition that holds. All five return on the first failure they find, so a set that
is both spliced and truncated reports one of the two, and the operator's next step depends on the
set rather than on whichever check ran first.</li>
<li><strong>G10 -- one implementation cannot distinguish the axes at all.</strong> <xref target="chain-axes"></xref> requires the six
chain conditions and the checkpoint axis to be distinguishable in a verifier's output, under
whatever strings it chooses. Four of the five return a status together with a reason string and
distinguish the six chain conditions through it. The Go verifier returns a status token alone -- its chain entry point's return type
is a single string -- so every distinction it makes internally is a source comment, and nothing
beyond <tt>MALFORMED</tt>, <tt>TAMPERED</tt> and <tt>UNTRUSTED</tt> is recoverable from its output. In all five, the
remaining two conditions are conflated by G6 and G7, so six is also the ceiling.</li>
<li><strong>G11 -- an ambiguous <tt>kid</tt> cannot be handled at all.</strong> <xref target="protected-header"></xref> requires a verifier to
try every key a <tt>kid</tt> resolves to, to accept only if exactly one verifies, and to report the
identifier as ambiguous when more than one was tried. Every shipped keyring is a map from a <tt>kid</tt>
string to <strong>one</strong> key, so the requirement is not expressible without changing that type: no
implementation can satisfy it today and none reports the case.</li>
<li><strong>G12 -- a public option turns off a MUST.</strong> <xref target="optional-members"></xref> requires a Chain Verifier to
reject a transition between two present, unequal <tt>scope.tenant</tt> values. The shipped verifier
exposes <tt>requireTenantConsistency</tt>, which defaults to refusing such a chain but can be set to
<tt>false</tt> -- and with it set, the repository's own vector for that case returns VALID on a chain
whose tenant drifts. It is documented and opt-in, which makes it a migration path rather than an
accident, and it is still a configuration in which the implementation does not conform.</li>
<li><strong>G13 -- the two identifiers are returned, and nothing says which claim each carries.</strong>
<xref target="payload-binding"></xref> requires a verifier to report the identifiers it treats as identities and to
say which claim each belongs to. Both values are in fact reachable from the shipped COSE result:
it returns the outer <tt>kid</tt> alongside the parsed receipt, whose <tt>sig.kid</tt> is the native one, so a
caller does not have to re-read its input. What is missing is the labelling and the second check
-- the result does not distinguish &quot;this key emitted the envelope&quot; from &quot;this key signed the
receipt into its chain&quot;, and the native signature is not verified on that path at all. G21 is the
sharper form of the same defect.</li>
<li><strong>G14 -- the attribution limitation is surfaced by one implementation of five.</strong> <xref target="identity-binding"></xref>
requires a verifier without an identity manifest to surface that attribution is key-level only.
The TypeScript reference emits exactly that warning; the other four emit nothing.</li>
<li><strong>G15 -- an unanswered tail question is reported by one implementation of five.</strong> <xref target="checkpoint"></xref>
requires a verifier given no checkpoint to report the tail question as unanswered rather than to
omit it. Only the TypeScript result carries a <tt>tailChecked</tt> field; the other four return ordinary
chain validity with no distinct absence result.</li>
<li><strong>G16 -- the opener-scoped qualification is missing in two implementations.</strong> <xref target="checkpoint"></xref>
requires a multi-agent checkpoint result to state whose view the check covers. TypeScript, Python
and C# emit that warning; Go and Rust carry no warnings channel at all, measured as zero
occurrences of any warning field in either source tree -- which also makes G14 and G15
unsatisfiable there without an API change.</li>
<li><strong>G17 -- the approval reporting level does not exist, and the obligation that would need it is
not triggered.</strong> <xref target="who-signed"></xref> binds a verifier <strong>that reports something about an approval</strong>: it
has to say whether what it holds is an opaque name on a bare receipt or complete cryptographic
evidence. The shipped chain result carries a status, a chain identifier, a count, two booleans, a
reason and warnings -- it does not carry <tt>governance.approval</tt> at all, so it reports nothing about
approval and the conditional never fires. Nothing here is broken. What is true, and why the row
stays: a consumer that wants the distinction cannot obtain it from these results, and an
implementation that later begins reporting approval takes on the obligation at that moment.</li>
<li><strong>G18 -- the Transparency Service role is not implemented, which is coverage absence, not
nonconformance.</strong> <xref target="registration"></xref> requires a Transparency Service to check the outer signature,
the protected <tt>kid</tt>, the CWT claims, a well-formed payload, the native signature where the key is
resolvable, and <tt>sub == scope.chain</tt>. No implementation performs them, because none claims that
role -- and <xref target="classes"></xref> is explicit that an implementation conforms to the role whose obligations
it performs, so declining a role is not a failure of it. The register carries the row so that a
reader counting shipped coverage against this document's roles sees the hole, not because
something is broken.</li>
<li><strong>G19 -- an unknown non-critical header can be fatal.</strong> <xref target="protected-header"></xref> requires a verifier to
ignore a header it does not understand that <tt>crit</tt> does not list. The reference's CBOR decoder
refuses the whole envelope when such a header's value uses a CBOR major type it does not
implement, which is a refusal reached before header policy runs.</li>
<li><strong>G20 -- the pinned equation is satisfied where it could be measured, and one implementation's is
a property of its linked library rather than of its own source.</strong> <xref target="payload-binding"></xref> pins the
cofactorless check <tt>[S]B = R + [k]A'</tt>. Read from source: the Python verifier computes exactly that
equation; the Rust verifier calls <tt>verify_strict</tt>, which its own header names as cofactorless; and
the Go verifier calls the standard library, whose implementation carries the comment
<tt>[S]B = R + [k]A</tt> and byte-compares the recomputed point against the signature's first half. The
TypeScript reference verifies through <tt>node:crypto</tt>, so the equation it executes is the one its
<strong>linked OpenSSL</strong> implements, not one this repository chooses: measured on Node v23.7.0 linked to
OpenSSL 3.6.3, that library's Ed25519 verifier recomputes the strict equation and names it as
such, so the reference satisfies the pin <strong>in that pairing</strong>. It is stated per version because
that is what was measured; a different Node/OpenSSL pairing is a different measurement, and an
in-repository comment describing the path as &quot;cofactored&quot; is a comment about key validation and is
wrong about the equation. The C# verifier delegates to a BouncyCastle assembly whose source is not
present in the environment these measurements were taken in, so its equation is <tt>[UNVERIFIED]</tt> --
recorded that way rather than assumed in either direction. <strong>An earlier draft of this register
asserted categorically that the TypeScript reference failed the pin, on the strength of that
source comment. That was false</strong>, and the correction is recorded here rather than quietly dropped:
a register that over-accuses is as unreliable as one that under-reports.</li>
<li><strong>G21 -- the COSE path checks the manifest against the wrong signature.</strong> <xref target="identity-binding"></xref> now
requires the agent claim to be checked against the <strong>native</strong> <tt>sig.kid</tt>, and forbids an authorized
outer <tt>kid</tt> from standing in for it. The shipped COSE path does the opposite: it pairs
<tt>receipt.agent.id</tt> with the <strong>outer</strong> envelope <tt>kid</tt> and never checks the native <tt>sig.kid</tt> against
the manifest at all (<tt>src/cose/receipt-cose.ts</tt>, the identity-binding block). Two consequences
follow, and both were <strong>run against the built package rather than reasoned about</strong>. Legitimate
relay -- native signature by the key the manifest authorizes for <tt>alice</tt>, envelope signed by an
unrelated relay: <tt>ok:false</tt>, &quot;agent \&quot;alice\&quot; is not authorized for signing key \&quot;k-relay\&quot;&quot;,
refusing a presentation <xref target="identity-binding"></xref> says MUST NOT be refused on that ground. Laundering --
native signature by a key authorized for nobody, claiming <tt>alice</tt>, wrapped by the key the manifest
does authorize for <tt>alice</tt>: <tt>ok:true</tt>, with the returned payload's native <tt>sig.kid</tt> naming the
unauthorized key. Both verdicts are the reverse of the rule. The chain path is not affected -- it
checks the native <tt>sig.kid</tt> -- so this is a defect of the envelope entry point only. The probe is
an ad-hoc script and is not committed, which is the reproducibility class stated at the top of this
section.</li>
</ul>
<t><strong>Behaviour the reference enforces that this revision does not require.</strong> These are not gaps against
a rule; they are places where a receipt this profile calls conformant is refused by the reference
implementation. That is an interop hazard in the opposite direction and it is stated here rather
than discovered in a bake-off. The classes that earlier reviews found in this position have left
this list, because this revision states them as requirements: checkpoint structure and scope; the
native <tt>sig.alg</tt> literal; the key type, encoding and public-point rules; the <tt>id</tt> bound; the
non-empty-string members; the forbidden member names; <tt>compliance.verdict</tt>; and the UTF-8 round trip
before a <tt>kid</tt> is resolved. What remains:</t>

<ul spacing="compact">
<li><strong>S1 -- a signing-key lifecycle.</strong> <xref target="checkpoint"></xref> says this revision defines no rotation or
revocation mechanism. The reference has one: its keyring may be a <tt>noa.signing-key-lifecycle/0.1</tt>
document in which each key carries a <tt>retiredAt</tt>, and a receipt, checkpoint or envelope signed by
a retired key is refused -- deliberately, since a signer-chosen timestamp
cannot prove an artifact predates a compromise. Whether that is a <em>divergence from the profile</em>
depends on something this document does not fix: a keyring holds the keys a verifier trusts, and
nothing here requires a retired key to remain in one. A deployment that simply removes it reaches
the same refusal without the lifecycle format, and S2 below says why both are conformant --
keyring encoding and lifecycle are undefined here. What the format actually buys is a stated
reason travelling with the refusal instead of a silent absence. Whether revocation belongs in the
profile is a question for the next revision. A related detail of the same format: it carries the public key as an
encoded key with no separate curve field, so the <tt>crv = 6</tt> requirement of <xref target="protected-header"></xref> is
checked by inspecting the encoded key, which the reference does.</li>
<li><strong>S2 -- unused keyring entries are shape-checked.</strong> The TypeScript reference checks the shape of
every static keyring entry before the chain walk -- each value must be a non-empty string -- and
refuses the whole keyring if any entry fails, including entries for keys nobody signed with. It
does not decode those unused entries as SPKI or as Ed25519 keys, so the divergence is real for an
empty or non-string entry and not for a well-shaped but cryptographically malformed one. The other
four look only at the key they actually use. Keyring encoding is undefined here
(<xref target="not-defined-here"></xref>), so both behaviours conform and the two answers are both correct.</li>
<li><strong>S3 -- a non-UTF-8 COSE <tt>kid</tt> refuses the object.</strong> <xref target="protected-header"></xref> now requires a verifier
not to <strong>resolve</strong> an identifier through a lossy decode. The reference goes further and refuses
the whole envelope, so a deployment using opaque non-UTF-8 identifiers cannot use it at all.</li>
<li><strong>S4 -- deterministic CBOR is enforced envelope-wide.</strong> <xref target="protected-header"></xref> requires the Core
Deterministic Encoding Requirements for the protected header. The reference's decoder applies them
to the whole COSE object including the unprotected map, so an envelope this profile permits can be
refused for an encoding choice outside the protected bytes.</li>
<li><strong>S5 -- input ceilings that differ, which is not the same as ceilings that are missing.</strong> The
TypeScript reference fixes a 16 MiB input limit, a JSON nesting depth of 64, a canonicalizer depth
of 64 and a CBOR depth of 32. The others are not unbounded: the Go verifier defines its own
parser ceiling of 2000 nesting levels (<tt>impl-go/parse.go</tt>), the C# verifier inherits
<tt>JsonDocument</tt>'s default maximum depth of 64, and the Rust verifier relies on <tt>serde_json</tt>'s
nesting limit, which its own source names as the control. Python has no explicit gate and stops at
the interpreter's recursion limit. So five verifiers enforce <strong>four different ceilings</strong>, and a
document between the tightest and the loosest is refused by some and accepted by others.
<xref target="security-considerations"></xref> assigns limits to the <strong>Deployment</strong> and fixes no number here, so every
one of them conforms -- which is precisely why the disagreement is a fact about interop rather
than a defect in any of them. An earlier draft of this register said the other four had &quot;no
equivalent fixed limits&quot;; that was false, and this row is the correction.</li>
<li><strong>S6 -- non-NFC handling.</strong> The reference reports non-NFC strings in a warnings channel by default
and can be configured to refuse them outright; the other four have no mode and no warning.
<xref target="optional-members"></xref> permits both, which means the same receipt legitimately receives two different
verdicts from two conforming verifiers, and a caller cannot tell which mode produced the one it
holds.</li>
</ul>
<t><strong>Divergences among the five that the conformance harness cannot see.</strong> The harness compares
verdicts over a shared corpus. A behaviour only one implementation has, or that no vector exercises,
is invisible to it -- and these were found by reading the five sources against one another rather
than by running them.</t>

<ul spacing="compact">
<li><strong>D1 -- one trust document, two verdicts, and one of them is an accusation.</strong> An honest two-receipt
chain signed by one key was presented to all five verifiers twice: once with a plain
<tt>{kid: publicKey}</tt> keyring, once with this package's own <tt>noa.signing-key-lifecycle/0.1</tt> trust
document carrying <strong>the same key</strong> with <tt>retiredAt: null</tt>. Plain keyring: <tt>VALID</tt> in all five.
Lifecycle document: <tt>VALID</tt> in TypeScript, <tt>TAMPERED</tt> in Python, Go, Rust and C#, the Python
reason reading &quot;unknown kid k-life at seq 0&quot;. The four do not know the format, read it as a flat
map, fail to find the <tt>kid</tt>, and conclude the receipt was signed outside the trust root -- so a
legitimate trust document holding the correct key makes four of five verifiers accuse an honest
chain of tampering. It is G6's defect reached through a different input, and the same violation of
<xref target="absence"></xref>. No file under <tt>conformance/</tt> mentions the format, so no runner ever feeds it to them.</li>
<li><strong>D2 -- one implementation does not state the key-canonicality check.</strong> TypeScript, Python, Rust
and C# each carry the <tt>y &gt;= q</tt> rejection in their own source. The Go verifier rejects the eight
small-order encodings and has no y-coordinate gate at all, inheriting whatever its standard
library does. <xref target="payload-binding"></xref> makes that rejection a MUST as of this revision, so the Go
verifier does not state a check it is now required to make.</li>
<li><strong>D3 -- two implementations have no channel for a required statement.</strong> Go and Rust carry no
warnings field, so G14, G15 and G16 cannot be satisfied in them without an API change. The corpus
compares verdicts, and a missing warning is not a verdict.</li>
<li><strong>D4 -- COSE exists once.</strong> The envelope path is implemented only in the TypeScript reference, so
nothing on it -- the <tt>alg</tt> pin, <tt>crit</tt> handling, deterministic encoding, <tt>kid</tt> resolution -- has
cross-implementation evidence in either direction.</li>
</ul>
<t><strong>What the harness can and cannot settle, stated once.</strong> <tt>conformance/MATRIX.md</tt> is generated from
four runs: the TypeScript reference against the Python one, and then the Go, Rust and C# verifiers
each against the Python one. The topology is a star with the Python reference at its centre, not
five implementations tested against one another -- so a behaviour the Python verifier does not
implement cannot be seen by that harness at all, and five-way agreement and five-way nonconformance
coexist without contradiction. G1 to G3 are exactly that case: all five agree with each other, and
none of them agrees with this document.</t>
<t><strong>Two claims in the reference implementation's own comments are retracted by this revision and not
yet corrected there.</strong> <tt>src/cose/receipt-cose.ts</tt> calls its output a &quot;COSE_Sign1 Signed Statement&quot;
and promises &quot;the external non-equivocation anchor NOA's self-signed chain lacks&quot;. Neither survives
this revision: <xref target="signed-statement"></xref> shows what a Signed Statement additionally requires and the
register above records that the implementation emits none of it, while <xref target="registration"></xref> narrows what
registration supplies. A reader who opens the code will find the older wording; it is a comment,
not a control, and correcting it is filed.</t>
<t><strong>Conformance corpora, counted at the pinned snapshot.</strong> <tt>conformance/vectors/attack/</tt> 16 files and
<tt>conformance/vectors/malformed/</tt> 9 (25 adversarial in total); <tt>conformance/action-digest/</tt> 33
vectors, 34 in the repository since <tt>df777a6</tt>; <tt>packages/rail-x402/conformance/settlement-evidence/</tt>
72 (1 accept, 57 reject, 14 control); <tt>packages/evidence/conformance/</tt> 77 bundles. Alongside them,
<tt>conformance/golden/</tt> answers a different question: whether a real past release's own signed output
still verifies today.</t>
<t><strong>What a third party measured.</strong> On 27 July 2026 an independent reconciliation implementation ran
against this corpus at a pinned commit and reported on the SCITT list
(<eref target="https://mailarchive.ietf.org/arch/msg/scitt/zWZBQXXSGV-vgbTeOLTjlb9DGLg/"/>): <strong>23 of 23</strong>
adversarial files then published produced at least one integrity-class divergence, with
<tt>cross-chain-splice</tt> the clean case -- both receipts verify in isolation and the forgery exists only
in the set. The same run measured a shared subject digest agreeing with another party's
canonicalization corpus on <strong>22 of 22</strong> cases, while a separate ledger-indexing construction that
applies Unicode normalization agreed on 20 of 22 under a UTF-16 member sort and 19 of 22 under a
code-point sort -- the collisions being invisible without a shared vector, which is the argument
<xref target="digest-boundary"></xref> makes normative. The corpus has since grown from 23 adversarial files to 25.</t>
<t><strong>What is still missing.</strong> The purpose-specific negative vectors that <xref target="external"></xref>, <xref target="purpose"></xref> and
<xref target="authorization"></xref> call for are not in the public corpus, with one exception: the
attribution-substitution vector landed at <tt>df777a6</tt>. The rest -- a controller success with no
physical evidence, a controller success against a witness reporting non-completion, same-party
evidence presented as independent, a missing signer relationship, a stale or replayed observation,
and the positive baselines mirroring each -- remain committed and unwritten, as does a
cross-implementation negative for the <tt>alg</tt> pin.</t>
<t><strong>Not implemented, stated plainly.</strong> Ten things, so that no reader has to infer them. Item 1 is
gap G8 of the register above. Item 2 is permitted but unimplemented; items 3 and
4 are capabilities this profile deliberately excludes (<xref target="payload-binding"></xref>, <xref target="chain-axes"></xref>); the rest
are absences with nothing owed against a rule. The distinction is the one <xref target="absence"></xref> insists on, applied
to this section.</t>

<ol spacing="compact">
<li><strong>No Signed Statement mode exists anywhere in the reference implementation, on either side.</strong>
The producer emits <tt>{1: -19, 4: kid}</tt> and no <tt>CWT_Claims</tt> (label 15), so what it mints does not
meet <xref target="signed-statement"></xref> and a conforming Transparency Service is entitled to refuse it at
registration. The verifier is the other half, and the half a reader is most likely to assume: it
<strong>ignores</strong> label 15 as a non-critical header, and a committed test shows it accepting a
protected header carrying a malformed <tt>15: bstr</tt> as a perfectly good generic COSE_Sign1. No code
path anywhere evaluates a receipt <em>as a Signed Statement</em>.</li>
<li><strong>Certificate-based key resolution is permitted but not implemented.</strong> <xref target="protected-header"></xref> lets a
deployment carry <tt>x5t</tt> or <tt>x5chain</tt> in addition to <tt>kid</tt>; the verifier ignores both headers and
resolves <tt>kid</tt> alone. Nothing is excluded -- it is simply not built.</li>
<li><strong>There is no detached-payload mode.</strong> The parser requires the payload element to be a byte
string and refuses a null one; the producer always attaches. <xref target="payload-binding"></xref> describes that
and nothing more.</li>
<li><strong>No range-presented verification.</strong> Every implementation requires the complete chain from
<tt>seq == 0</tt>; a presentation starting at <tt>seq 1</tt> is refused as a missing genesis, not verified as
a partial view. <xref target="chain-axes"></xref> states that as the rule. A verifier that could authenticate a
mid-chain range against a trusted anchor is a real capability and this profile does not have one.</li>
<li><strong>No controller-outcome artifact and no physical-observation artifact.</strong> The vocabulary of
<xref target="three-claims"></xref> for claims 2 and 3 is specification ahead of implementation. The plane that
exists is a payment settlement plane over one rail family; it is an instance of the <em>shape</em>
(<xref target="purpose"></xref>, <xref target="absence"></xref>), not an answer to the actuator case the review thread was about.</li>
<li><strong>No settlement-evidence enrolment registry at the pinned <tt>4764cab</tt> snapshot</strong> -- the registry
an Evidence Verifier would consult to decide which action classes require settlement evidence.
Device enrolment through pairing is a different mechanism and did exist at that snapshot, with
its own conformance vectors; this item is not about it. Without the action-class registry,
settlement evidence
cannot be <em>required</em>, so at this snapshot its absence costs nothing and its presence buys no
upgrade. Subsequent implementation work is outside the measurements reported here.</li>
<li><strong>The relationship signal is not surfaced in bundle verdicts</strong>, and no level of this
implementation can derive organizational independence at all.</li>
<li><strong>The bundle verifier never reaches a network service</strong>, by design: a verifier that silently
depends on a reachable service is not an offline verifier. Anything that would require a live
query is therefore outside what one of its verdicts can establish.</li>
<li><strong>No published reproducible demonstration of registration with an external SCITT Transparency
Service.</strong></li>
<li><strong>The purpose-specific negative vectors of <xref target="implementation-status"></xref> are not written</strong>, including a
cross-implementation negative for the <tt>alg</tt> pin. The attribution-substitution vector that was
open in earlier drafts of this list is no longer among them: it landed at <tt>df777a6</tt>.</li>
</ol>
<t>The boundary item 1 leaves is worth stating in the sharpest available words: <strong>&quot;the signature
verifies&quot; and &quot;an RFC 9943 Signed Statement validates&quot; are different properties, and this
implementation currently establishes only the first.</strong> Closing it is work on both sides -- the
producer must emit the protected claims, and the verifier must gain a mode that checks them and
refuses a receipt that lacks or malforms them, per <xref target="signed-statement"></xref> and <xref target="RFC9597"></xref>, Section 2,
which defines label 15 as a CWT Claims map that occurs exactly once across the two header buckets.</t>
<t>Every one of these is a boundary this document states rather than a gap it hides. Where a reader
finds a normative requirement here whose enforcement is on this list, that requirement is a
specification for implementers -- including this one -- and not a report of shipped behaviour.</t>
</section>

<section anchor="security-considerations"><name>Security Considerations</name>
<t><em>Analysis binds every actor. Where a sentence carries a requirement it names its actor; the
requirements in this section fall on the <strong>Producer</strong> (what MUST NOT appear on a receipt), on
verifiers (what they MUST NOT report), and on the <strong>Relying Party</strong> (how results may be read).</em></t>
<t>This profile attests exactly two things: (1) the record is tamper-evident and
signature-verifiable under the issuer's key; and (2) the recorded fields -- action, principal
class, verdict, and whichever optional members are present -- are bound into that signature.
&quot;Policy identity&quot; is one of the optional ones: both carriers may be absent (<xref target="optional-members"></xref>),
and a receipt that omits them records no policy identity at all while remaining conformant. The principal field is a class marker and
not an identity: a receipt recording <tt>HUMAN</tt> names no human, and a relying party MUST NOT read one
out of it. It does NOT attest that the recorded
inputs are true, complete, or timely (no capture-completeness); that the policy was adequate; that
no action occurred outside the instrumented boundary; that the agent was correct, safe, or wise;
that a named approver authorized this exact action before it ran; that a controller succeeded; or
that any physical effect occurred. These non-goals are NORMATIVE: implementations and relying
parties MUST NOT imply the stronger claims from a receipt.</t>
<t>Equivocation -- one issuer signing two divergent histories and showing each to a different party --
is not detectable by any check in this profile. <xref target="chain-axes"></xref> catches a fork only when both branches
are <em>presented together</em>, as a duplicate sequence; nothing reaches a branch the prover withholds. Registration in a Transparency Service is a necessary step toward detecting them and is
not a sufficient one: detection additionally requires consistency proofs between log states and a
party that monitors them (<xref target="registration"></xref>), neither of which this profile specifies or requires. A
deployment that registers and never monitors has an auditable record and no equivocation defence. Identity attribution above key level requires the out-of-band
identity manifest. Algorithm-confusion and key-confusion attacks are mitigated by the strict
<tt>alg = -19</tt> and pinned-curve requirements of <xref target="cose"></xref>. Raw prompts, tool arguments, and secrets
MUST NOT appear on a receipt; only hashes (optionally HMAC for low-entropy values) are carried.</t>
<t><strong>The scope of a checkpoint check is the scope of the key that signed it.</strong> A checkpoint can reveal
truncation relative to itself, and only to the strength of the binding between checkpoint authority
and chain. With an identity manifest, that authority is bound to the keys the manifest authorizes
for the genesis receipt's <tt>agent.id</tt> -- a set, not one key (<xref target="checkpoint"></xref>) -- so a re-heading
attacker's checkpoint over its own appended head is rejected. WITHOUT a
manifest, checkpoint authentication is key-level only: any key the verifier trusts can mint a
checkpoint over any head, so a co-trusted key holder can delete the most recent receipts, sign a
checkpoint over the truncated head, and obtain a verified tail check over an incomplete chain.
A Chain Verifier MUST surface this limitation to the relying party whenever a checkpoint
authenticates without a manifest, and MUST NOT report such a result as an unqualified
tail-truncation check. Even with a manifest the guarantee is opener-scoped: on a chain carrying
more than one <tt>agent.id</tt>, the opener's checkpoint does not certify a co-agent's tail.</t>
<t>A verifier never normalizes a non-NFC string, and this profile sets no default for whether it
rejects one (<xref target="optional-members"></xref>); the analysis below is written for the accepting configuration,
and a verifier in the refusing configuration simply never sees the ambiguity. In the accepting
configuration, because a non-NFC string is neither normalized nor rejected
(<xref target="canon-params"></xref>, <xref target="optional-members"></xref>), two receipts may carry <tt>agent.id</tt> or <tt>action.id</tt> values
that render identically but differ in bytes, and both verify. A verifier configured in the optional
refusal mode rejects such a receipt as malformed instead -- which removes the ambiguity for that
verifier and for nobody else, since the same bytes still verify elsewhere. Relying parties that match, index, or alert on these fields MUST compare them as bytes. Normalizing
at their own layer before comparing is <strong>not</strong> an equivalent option: it is precisely what collapses
two distinct, validly signed identifiers onto one index entry, match or alert subject, which is the
collision this paragraph exists to warn about. A relying party MUST NOT assume that visual equality
implies byte equality.</t>
<t><strong>A trusted key can try to extend someone else's chain, and one condition stops part of it.</strong> Every
receipt in a chain is individually valid; nothing in a receipt says who is entitled to add the
<em>next</em> one. A party holding any key the verifier trusts can therefore append well-formed receipts
to another party's chain -- correct <tt>prevHash</tt>, contiguous sequence, same <tt>scope.chain</tt>, same
tenant -- and, <strong>where the appended receipts claim the victim's own <tt>agent.id</tt></strong>, every condition
but one stays silent. The one that fires is <tt>chain-key-discontinuity</tt> (<xref target="chain-axes"></xref>): those
receipts carry a different <tt>sig.kid</tt> under that same <tt>agent.id</tt>. A Chain Verifier that omits the
check accepts a forged tail as history. Two escapes remain, stated here rather than left for an
attacker to find: an attacker who appends under a <strong>new <tt>agent.id</tt></strong> trips no condition at all,
because this profile defines no append authority for a chain (<xref target="chain-axes"></xref>, <xref target="not-defined-here"></xref>);
and an attacker whose second key is registered under the victim's <strong><tt>kid</tt></strong> defeats a comparison
made on identifiers rather than on resolved key material. Where the attacker instead reuses the
victim's own <tt>agent.id</tt> <em>and</em> <tt>sig.kid</tt>, it is signing with the victim's key, which is a
key-compromise problem this profile cannot address.</t>
<t><strong>A receipt can be re-wrapped by anyone who holds a trusted key.</strong> The payload is self-contained
and self-signed, and <xref target="payload-binding"></xref> permits the outer COSE signer to differ from the native
signer. It follows that any party whose key a verifier trusts can take another party's receipt,
strip its envelope, and re-emit the same payload under its own outer signature: both signatures
verify, because both are genuine. Nothing in the bytes distinguishes the original emitter from a
relay. The consequences are bounded but real: the relay cannot alter a field without breaking the
native signature, so the <em>content</em> is safe; what it can do is present someone else's record as
something it emitted, and -- if the verifier attributes from the outer <tt>kid</tt> -- have that
attribution land on itself. What stops that is the attribution model of <xref target="identity-binding"></xref>, and it
is worth being exact about which half does the work. The manifest binds <tt>agent.id</tt> to authorized
keys and is checked against the <strong>native</strong> <tt>sig.kid</tt>, so a relay cannot make itself the agent: the
agent claim is carried by a signature the relay did not produce and cannot alter. The relay is not
refused for relaying -- <xref target="identity-binding"></xref> forbids rejecting a receipt solely because its outer
signer is not one of the agent's keys, because relaying is a legitimate presentation. What a
verifier MUST NOT do is report the outer signer as the party that made the record. Without a
manifest, attribution is key-level only and even the agent claim is unbound. Registration does not help here: a Transparency Service that follows
<xref target="registration"></xref> will happily register a relayed statement, because every signature in it is valid.</t>
<t><strong>Same-party evidence is the forensic trap this profile names rather than solves.</strong> A signed
outcome produced under the same administrative control as the authorization is not independent
evidence, however sound its cryptography; independence is a property of the trust configuration a
verifier applies, never of a field a producer writes about itself (<xref target="who-signed"></xref>). An
implementation that renders <tt>UNKNOWN</tt> as &quot;independent&quot; has defeated the control.</t>
<t><strong>Substitution across claim classes is a signature-valid attack.</strong> Both the post-execution record
presented as authorization and the machine decision presented as human approval (<xref target="authorization"></xref>)
carry genuine signatures. An <strong>Evidence Verifier</strong> that returns one boolean cannot express &quot;signature
good, claim wrong&quot;. That is a defect in what such a result can tell its reader, not a conformance
failure: <xref target="purpose"></xref>'s claim-class machinery is non-normative in this revision, so nothing here makes
a one-boolean result nonconformant. A reader deciding whether to rely on such a verifier should
treat the missing distinction as the gap it is.</t>
<t><strong>Parsing attacker-supplied input is a cost this profile does not bound.</strong> A verifier ingests JSON
and CBOR from whoever presents them, and the checks required here -- duplicate-member detection,
surrogate validation, canonical re-serialization -- are all performed on that input before anything
about it is trusted. This profile bounds exactly one string -- <tt>id</tt>, at 128 code points
(<xref target="field-semantics"></xref>) -- and sets no maximum payload size, nesting depth,
member count, or chain length, and a Chain Verifier is additionally required to hold a complete
chain from genesis (<xref target="chain-axes"></xref>), which grows without bound. A <strong>Deployment</strong> MUST impose its
own limits and MUST refuse input that exceeds them rather than attempting a partial verification;
a limit that is exceeded is not a tampering finding and MUST NOT be reported as one. A deployment
that omits limits has a denial-of-service surface, not a correctness problem.</t>
<t><strong>An unpinned digest construction is a silent join.</strong> Comparing digests produced under different
constructions can both miss a real correlation and, where one construction normalizes and another
does not, map two different actions onto one value. <xref target="digest-boundary"></xref> requires the comparison to
fail closed and be reported as indeterminate.</t>
<t><strong>Absence of <tt>scope.tenant</tt> is not a tenancy reset</strong> (<xref target="optional-members"></xref>); a verifier that treats
it as one accepts a cross-tenant splice that carries no tampering evidence of its own.</t>
</section>

<section anchor="privacy-considerations"><name>Privacy Considerations</name>
<t><em>This section addresses the <strong>Producer</strong> and the <strong>Deployment</strong> (<xref target="classes"></xref>); verifiers inherit no
obligation from it.</em></t>
<t>A receipt is designed to be shown to parties who were not present when the action ran, so what it
carries is a privacy decision as much as a security one.</t>
<t><strong>Raw parameters are forbidden, not prevented.</strong> This profile prohibits a <strong>Producer</strong> from placing
raw prompts, tool arguments, secrets or other sensitive parameters anywhere on a receipt
(<xref target="payload"></xref>), and <tt>action.paramsHash</tt> carries a digest in place of the values. What the format
cannot do is enforce it: several members are unconstrained strings (<xref target="field-semantics"></xref>), so a
producer that writes a secret into one produces a receipt every verifier accepts. A deployment
handling sensitive parameters therefore needs a producer-side control, and a relying party that
needs the parameters must obtain them out of band. Whether it can then <em>open</em> the commitment is a matter for the
producer's own contract, not for this profile: <xref target="payload"></xref> states that the preimage construction is
undefined here, and for the keyed form the key is a tenant secret. So the privacy property is
one-directional -- the receipt does not disclose the parameters themselves -- and the verification
property it might seem to imply does not follow from this document alone. What the receipt does
disclose is set out below: a guessable commitment is an oracle, and the identifiers and timestamps
are linkable by design.</t>
<t><strong>A hash of a low-entropy value is not a privacy control.</strong> Where the committed parameters are
guessable -- an amount, a boolean, a small identifier -- SHA-256 is a confirm-by-guess oracle
rather than a shield, since anyone can hash a candidate and compare. For those cases the profile
provides the keyed form <tt>hmac-sha256:&lt;hex&gt;</tt> under a tenant-scoped key, which removes the oracle
from anyone who does not hold the key. Producers SHOULD use it whenever the parameter space is
enumerable, and MUST NOT treat a plain digest of a low-entropy value as a disclosure control.</t>
<t><strong>What remains linkable, by design.</strong> A receipt still carries <tt>scope.chain</tt>, <tt>agent.id</tt>,
<tt>action.id</tt>, <tt>action.canonical</tt>, timestamps and, where present, <tt>governance.approval.by</tt>. These are
opaque strings to this format, but they are stable across receipts, which is what makes a chain a
chain: any party holding two receipts can tell they concern the same chain or the same agent, and
timestamps support timing correlation. A <strong>Deployment</strong> that treats those identifiers as sensitive SHOULD
choose opaque, non-descriptive values -- an internal identifier rather than a person's address --
because the format cannot enforce that and a verifier cannot undo a descriptive choice.</t>
<t><strong>Registration publishes, to the extent the log retains.</strong> Submitting a Signed Statement places it
and the CWT Claims in its protected header into an append-only log whose purpose is that third
parties can inspect it. How much of the payload they can see is the log's choice, not this
profile's. <xref target="RFC9943"></xref>, Section 6.2 contemplates statements whose payload is carried as a hash
rather than in full -- and <strong>that construction is not conformant with this profile as specified</strong>:
<xref target="payload-binding"></xref> requires the receipt itself to be the attached payload, and <xref target="registration"></xref>
requires a Transparency Service to validate that payload as a receipt and to check <tt>sub</tt> against its
<tt>scope.chain</tt>. A hash-only statement fails both. A deployment that needs to disclose less therefore
cannot get it from this revision; defining a mapping -- what the hash covers, and what a service can
still check -- is work for a future one. CWT Claims in COSE headers are not encrypted
(<xref target="RFC9597"></xref>, Section 3), so <tt>iss</tt> and <tt>sub</tt> are visible to every party that can read the log. A
deployment SHOULD decide what may be published <em>before</em> it registers: transparency rests on an
append-only sequence, which <xref target="RFC9943"></xref>, Section 5.1.3 requires the log's verifiable data structure
to guarantee -- it cannot be modified, deleted or reordered -- so an entry cannot be un-registered,
and this document defines no redaction mechanism. What a service retains and serves beyond that history is its own policy, which
this profile does not constrain.</t>
<t><strong>Signer relationship reporting is a disclosure too.</strong> The trust-configuration digests that
<xref target="who-signed"></xref> requires alongside a derived relationship commit to a policy and a registry snapshot;
they do not disclose their contents, and a <strong>Producer</strong> SHOULD keep it that way rather than
inlining registry material into a result a relying party may forward.</t>
</section>

<section anchor="open-questions"><name>Open questions for the working group</name>
<t><em>This section is to be removed by the RFC Editor before publication. It records what this revision
deliberately did not settle, so that a reader does not mistake an open question for an oversight.</em></t>

<ul spacing="compact">
<li><strong>The verification equation is pinned in this revision, and the working group may revisit which
one.</strong> <xref target="payload-binding"></xref> pins the cofactorless check together with the canonical signature
scalar, the canonical public-key encoding and the rejection of the eight small-order encodings;
<xref target="RFC8032"></xref>, Section 8.8 explains in its own words why the choice cannot be left open -- any
signature meeting the non-multiplied equation satisfies the multiplied one, so implementations
that choose differently disagree about the exact set of valid signatures, and in a hash chain that
is a divergent accepted history. This revision therefore chose, rather than leaving validity
implementation-defined. What remains genuinely open is <em>which</em> of the two the working group wants.
The evidence, taken from this document's own measurements (<xref target="implementation-status"></xref>, G20): four of
the five shipped verifiers were measured cofactorless -- Python, Rust and Go from their sources,
and the TypeScript reference through the OpenSSL version it was measured against -- and the fifth
could not be measured. So the equation chosen here is the one four of five already implement, and
the argument for the other is not &quot;it matches what ships&quot;. Either can be made deterministic;
neither can be left unmade.</li>
<li><strong>The request signer is defined by example, which is why the text that needs it is not
normative.</strong> <xref target="who-signed"></xref> derives a signer relationship against the <em>request signer</em>, and this
document describes that party only by pointing at what the reference implementation signs with.
That is one of the reasons the relationship analysis there carries no requirement in this
revision. The question for the working group is what a definition would have to fix -- the party,
the trust configuration it is derived from, and the digests that identify that configuration --
before the analysis can become a requirement anywhere.</li>
<li><strong>Bare or enveloped?</strong> <xref target="cose"></xref> admits both forms because both ship. Whether a
SCITT-native profile should require the envelope -- making every receipt registrable at the cost
of forbidding the bare form every current implementation verifies -- is a genuine fork.</li>
<li><strong>Duplicate sequence numbers, in a future revision.</strong> This revision answers the question in the
strict direction: a byte-identical duplicate is one of the six chain conditions, so the set fails
and
a verifier that de-duplicates and returns a positive result is not conformant (<xref target="chain-axes"></xref>).
That is deliberately the safe answer rather than the obviously right one -- a retransmitted copy
is an innocent event, and refusing it costs an operator something real. Whether a <strong>future</strong>
revision should permit acceptance after de-duplication, and under what reporting requirement, is
the open question; it is not open in this one.</li>
<li><strong>Where the Evidence-Verifier layer belongs, now that this revision has stopped requiring it.</strong>
<xref target="who-signed"></xref>, <xref target="three-claims"></xref>, <xref target="purpose"></xref>, <xref target="authorization"></xref> and <xref target="digest-boundary"></xref> each mark the
passages that run over artifacts <xref target="not-defined-here"></xref> lists as undefined, and those passages carry
no RFC 2119 requirement here: an implementation cannot fail them. That was the honest answer to an
untestable obligation, and it leaves a real question rather than settling it. Either the artifact
formats come into this profile and the analysis becomes normative here, or the analysis moves into
the companion documents that define them and becomes normative there. What should not persist is
the present state, where the reasoning is written in one document and enforceable in none.</li>
<li><strong>Should the two untokenised results be named?</strong> <xref target="not-defined-here"></xref> lists them: the conflict
result of <xref target="purpose"></xref> and the unproven-authorization result of <xref target="authorization"></xref>. Neither carries a
requirement in this revision -- both live in passages marked non-normative -- and neither has a
token, so an implementation that reports them chooses its own name and two implementations
reporting the same finding will not agree on it. Defining the result and its token together is a
small vocabulary decision with a real interoperability consequence, and it is the working group's
to make rather than this author's.</li>
<li><strong>&quot;Issuer&quot; is overloaded across three different parties.</strong> <xref target="classes"></xref> aliases the native
Producer as the Issuer; <xref target="signed-statement"></xref> uses <tt>iss</tt> for the authority the outer Signed
Statement is emitted on behalf of; <xref target="who-signed"></xref> calls the native signer the issuer; and a relay
that wraps someone else's receipt is a fourth party using the same envelope position
(<xref target="security-considerations"></xref>). Nothing here is contradictory, and an implementer can still build
from it -- but a reader has to carry the disambiguation in their head, and the ambiguity invites
exactly the native/outer collapse <xref target="identity-binding"></xref> forbids. Whether these should be distinct
named roles, and which of them <tt>iss</tt> denotes when they differ, is a naming decision with security
consequences and it belongs to the working group.</li>
<li><strong>This profile forbids the leap-second representation, and that is a decision to review.</strong>
<xref target="optional-members"></xref> requires a Producer not to emit a <tt>date-time</tt> whose seconds field reads <tt>60</tt>,
and a verifier to reject one. The alternative this revision rejected was to admit <tt>60</tt> and require
nobody to validate it, which is not implementable offline: <xref target="RFC3339"></xref>, Section 5.7 admits <tt>60</tt>
only at an actual leap-second position adjusted by the value's offset, and says future leap
seconds cannot be predicted, so a verifier holding no table cannot decide whether such a value is
a valid <tt>date-time</tt> -- and the requirement to reject what is not valid would have no determinate
answer. Pinning a table was rejected too: a table is state that ages, and offline verifiers
acquire it at different times, so two conforming verifiers would still disagree about a historical
instant. Forbidding the representation is the only branch that leaves every verifier the same
answer, and it costs a Producer nothing it needs. The working group may prefer one of the branches
this revision declined; the cost of each is stated here so the choice can be made on evidence.</li>
<li><strong>Key rotation, and what it currently costs.</strong> <xref target="chain-axes"></xref> requires one key per <tt>agent.id</tt>
within a presented chain, which is what closes the chain-extension attack; the price is stated in
<xref target="checkpoint"></xref>: rotation is not expressible inside a chain, so a deployment that rotates a signing
key starts a new one. The reference implementation has gone further and refuses a receipt signed
by a key its trust document marks retired, which this profile does not require
(<xref target="implementation-status"></xref>). Whether a rotation or revocation mechanism belongs in the profile --
and if so, whether it may relax the one-key rule or must sit beside it -- is a genuine trade
between an attack this revision closes and an operational need every deployment eventually has.</li>
<li><strong>Standards Track with no IANA action, or Experimental?</strong> The header of this revision reads
Standards Track, which is itself a change from -00's Informational (<xref target="changes"></xref>); it records the
author's current intention, not a decision taken for the working group.
<xref target="iana-considerations"></xref> requests nothing,
so there is no media type for the payload, and <xref target="signed-statement"></xref>'s <tt>content type</tt> cannot be
interoperable across deployments. Two answers are available and the document does not choose
between them: register a media type and stay on the Standards Track, or publish as Experimental
while the profile's open questions settle and register later. What would be wrong is to ship a
wire format on the Standards Track with neither.</li>
</ul>
</section>

<section anchor="iana-considerations"><name>IANA Considerations</name>
<t><em>This section carries no normative requirement on any actor.</em></t>
<t>This document has no IANA actions in this revision, and one of those absences is load-bearing:
without a registered media type for the action-receipt payload, the <tt>content type</tt> of
<xref target="signed-statement"></xref> cannot be interoperable across deployments. A future revision ought to
register one, and a Standards Track document that carries a wire format arguably should not ship without it
-- this revision states the gap rather than papering over it. It uses the existing IANA COSE Algorithms
registry value <tt>-19</tt> (Ed25519) and the existing COSE/CWT header parameters. The vocabularies named
in <xref target="who-signed"></xref>, <xref target="purpose"></xref>, and <xref target="absence"></xref> are shared spellings for <em>reporting</em>, not an
interoperable output format -- this revision defines no result schema (<xref target="not-defined-here"></xref>), so two
implementations using the same word still do not produce comparable output -- and they are not
proposed for registration in this revision. A future revision could request registration of
a media type for the action-receipt payload and/or of CWT or COSE claims for the payload fields.</t>
</section>

</middle>

<back>
<references><name>References</name>
<references anchor="normative-references"><name>Normative References</name>
<reference anchor="FIPS180-4" target="https://csrc.nist.gov/pubs/fips/180-4/upd1/final">
  <front>
    <title>Secure Hash Standard (SHS)</title>
    <author>
      <organization>National Institute of Standards and Technology</organization>
    </author>
    <date year="2015" month="August"></date>
  </front>
  <seriesInfo name="FIPS" value="PUB 180-4"></seriesInfo>
</reference>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.2104.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.2119.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.3339.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.4648.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8032.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8174.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8785.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8949.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9052.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9053.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9360.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9597.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9864.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9942.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9943.xml"/>
<reference anchor="UAX15" target="https://www.unicode.org/reports/tr15/">
  <front>
    <title>Unicode Standard Annex #15: Unicode Normalization Forms</title>
    <author>
      <organization>The Unicode Consortium</organization>
    </author>
    <date year="2025"></date>
  </front>
</reference>
</references>
<references anchor="informative-references"><name>Informative References</name>
<reference anchor="ACTA" target="https://datatracker.ietf.org/doc/draft-farley-acta-signed-receipts/">
  <front>
    <title>Signed Decision Receipts for Machine-to-Machine Access Control</title>
    <author fullname="Tom Farley" initials="T." surname="Farley">
      <organization></organization>
    </author>
    <date year="2026" month="June"></date>
  </front>
  <seriesInfo name="Internet-Draft" value="draft-farley-acta-signed-receipts-02"></seriesInfo>
</reference>
<reference anchor="AGENTROA" target="https://datatracker.ietf.org/doc/draft-nivalto-agentroa-route-authorization/">
  <front>
    <title>Agent Route Origin Authorization (AgentROA): A Cryptographic Policy Enforcement Framework for AI Agent Actions</title>
    <author initials="J." surname="Michalak">
      <organization>Nivalto, Inc.</organization>
    </author>
    <date year="2026" month="April"></date>
  </front>
  <seriesInfo name="Internet-Draft" value="draft-nivalto-agentroa-route-authorization-01"></seriesInfo>
  <refcontent>Work in Progress</refcontent>
</reference>
<reference anchor="AI-ART50" target="https://datatracker.ietf.org/doc/draft-dawkins-scitt-ai-article50/">
  <front>
    <title>A SCITT Profile for EU AI Act Article 50 Transparency Receipts</title>
    <author fullname="Veronica S. Dawkins" initials="V." surname="Dawkins">
      <organization></organization>
    </author>
    <date year="2026" month="May"></date>
  </front>
  <seriesInfo name="Internet-Draft" value="draft-dawkins-scitt-ai-article50-00"></seriesInfo>
</reference>
<reference anchor="AIVS" target="https://datatracker.ietf.org/doc/draft-stone-aivs/">
  <front>
    <title>AIVS: Agentic Integrity Verification Standard</title>
    <author fullname="Ben Stone" initials="B." surname="Stone">
      <organization></organization>
    </author>
    <date year="2026" month="March"></date>
  </front>
  <seriesInfo name="Internet-Draft" value="draft-stone-aivs-00"></seriesInfo>
</reference>
<reference anchor="ASQAV" target="https://datatracker.ietf.org/doc/draft-marques-asqav-compliance-receipts/">
  <front>
    <title>Compliance Profile of Signed Action Receipts for AI Agents</title>
    <author initials="J. A." surname="Gomes Marques">
      <organization>Asqav</organization>
    </author>
    <date year="2026" month="July"></date>
  </front>
  <seriesInfo name="Internet-Draft" value="draft-marques-asqav-compliance-receipts-07"></seriesInfo>
  <refcontent>Work in Progress</refcontent>
</reference>
<reference anchor="I-D.hillier-scitt-arp" target="https://datatracker.ietf.org/doc/draft-hillier-scitt-arp/">
  <front>
    <title>Attestation Reconciliation Protocol</title>
    <author fullname="Joel Hillier" initials="J. D." surname="Hillier">
      <organization>Certisyn, Inc.</organization>
    </author>
    <date year="2026" month="August"></date>
  </front>
  <seriesInfo name="Internet-Draft" value="draft-hillier-scitt-arp-03"></seriesInfo>
</reference>
<reference anchor="I-D.schrock-action-evidence-boundary" target="https://datatracker.ietf.org/doc/draft-schrock-action-evidence-boundary/">
  <front>
    <title>The Action Evidence Boundary for Consequential Agent Effects</title>
    <author fullname="Iman Schrock" initials="I." surname="Schrock">
      <organization></organization>
    </author>
    <date year="2026" month="August"></date>
  </front>
  <seriesInfo name="Internet-Draft" value="draft-schrock-action-evidence-boundary-03"></seriesInfo>
</reference>
<reference anchor="I-D.toraman-noa-action-digest" target="https://datatracker.ietf.org/doc/draft-toraman-noa-action-digest/">
  <front>
    <title>The NOA Action Digest: a Domain-Separated Correlation Value for Human-Approved Agent Actions</title>
    <author fullname="Tora Toraman" initials="T." surname="Toraman">
      <organization>NordenSoft</organization>
    </author>
    <date year="2026" month="August"></date>
  </front>
  <seriesInfo name="Internet-Draft" value="draft-toraman-noa-action-digest-01"></seriesInfo>
</reference>
<reference anchor="I-D.toraman-noa-settlement-evidence" target="https://datatracker.ietf.org/doc/draft-toraman-noa-settlement-evidence/">
  <front>
    <title>Settlement Evidence for Human-Approved Agent Payments</title>
    <author fullname="Tora Toraman" initials="T." surname="Toraman">
      <organization>NordenSoft</organization>
    </author>
    <date year="2026" month="August"></date>
  </front>
  <seriesInfo name="Internet-Draft" value="draft-toraman-noa-settlement-evidence-01"></seriesInfo>
</reference>
<reference anchor="OWASP-MCP" target="https://owasp.org/www-project-mcp-top-10/">
  <front>
    <title>OWASP MCP Top 10</title>
    <author>
      <organization>OWASP</organization>
    </author>
    <date year="2025"></date>
  </front>
  <refcontent>Entry MCP08:2025, Lack of Audit and Telemetry</refcontent>
</reference>
<reference anchor="PSER" target="https://datatracker.ietf.org/doc/draft-wilder-scitt-physical-site-engage-receipt/">
  <front>
    <title>A SCITT Profile for Physical-Site Engagement Receipts</title>
    <author initials="R." surname="Wilder">
      <organization>Wilder Robotics</organization>
    </author>
    <date year="2026" month="July"></date>
  </front>
  <seriesInfo name="Internet-Draft" value="draft-wilder-scitt-physical-site-engage-receipt-00"></seriesInfo>
</reference>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7942.xml"/>
<reference anchor="SCITT-VCP" target="https://datatracker.ietf.org/doc/draft-kamimura-scitt-vcp/">
  <front>
    <title>A SCITT Profile for Verifiable Audit Trails in Algorithmic Trading: The VeritasChain Protocol (VCP)</title>
    <author initials="T." surname="Kamimura">
      <organization></organization>
    </author>
    <date year="2026" month="July"></date>
  </front>
  <seriesInfo name="Internet-Draft" value="draft-kamimura-scitt-vcp-03"></seriesInfo>
</reference>
</references>
</references>

<section anchor="changes"><name>Changes since -00</name>
<t><em>This section is to be removed by the RFC Editor before publication.</em></t>
<t>This revision exists because of a public review thread. What follows is that story in order: what
was asked, what was built and measured in answer, and what this document now guarantees. Every
difference between the posted -00 and this text is accounted for here -- a reader diffing the two
should find no change this section does not explain.</t>

<section anchor="interface-stability-and-the-two-corrections-a-citing-document-must-act-on"><name>Interface stability, and the two corrections a citing document must act on</name>
<t>Section 5 keeps its number, its title and its XML anchor (<tt>hash-chaining-and-completeness</tt>), so
both numeric and fragment references from <xref target="PSER"></xref> continue to resolve, and the construction it
defines -- <tt>chain.seq</tt>, <tt>prevHash</tt>, the linkage rule -- is unchanged.</t>
<t><strong>Two sentences of its paragraph were wrong in -00 and are corrected in this revision.</strong> A document
that adopted Section 5 by reference inherited both, so the change is stated here in the terms an
implementer needs:</t>

<ul spacing="compact">
<li><em>Checkpoint authority.</em> -00 said the authority &quot;is bound to the genesis key (the key authorized at
<tt>seq == 0</tt>), so a foreign checkpoint produced by a re-heading attacker is rejected&quot;. That binding
does not exist on its own: it requires an identity manifest, and it binds to the <em>set</em> of keys the
manifest authorizes for the genesis receipt's <tt>agent.id</tt>. -01 says so, and says plainly that
without a manifest the property does not hold. <strong>An implementation that relied on -00's sentence
believes it rejects a re-heading attacker and does not.</strong></li>
<li><em>Tail truncation.</em> -00 said the profile &quot;detects in-band tampering and tail truncation <em>within</em> a
presented chain&quot;. Tail truncation is not detectable from a presented set at all -- a prover who
withholds the tail presents a set with nothing missing in it. -01 says truncation is detectable
only against an authenticated checkpoint. <strong>An implementation that relied on -00's sentence
believes a clean verification result means nothing was deleted from the end. It does not.</strong></li>
</ul>
<t>Nothing else in the paragraph changed, and no receipt changes validity because of these two
corrections: they describe what verification establishes, not how a receipt is built. <strong>Subsections
5.1 and 5.2 are NEW, and the principal requirements they add are:</strong></t>

<ul spacing="compact">
<li>detect all six chain conditions, each decided from the presented set alone (MUST), and report
them distinguishably (MUST);</li>
<li>report the checkpoint axis in exactly one of its three states -- answered, unanswered, unusable
(MUST) -- and never let any of them change the verdict the six conditions produced (MUST NOT);</li>
<li>require the complete chain from <tt>seq == 0</tt>, refusing a presentation that begins elsewhere (MUST);</li>
<li>distinguish a consistent extension past an older checkpoint from a head conflict (MUST), never
report the former as the latter (MUST NOT), and never name a cause -- truncation, fork or signer
error -- that the comparison did not establish (MUST NOT);</li>
<li>refuse to let an unauthenticated checkpoint establish a head conflict (MUST NOT), reporting
<tt>checkpoint-unverified</tt> instead;</li>
<li>state whether the checkpoint behind any tail conclusion was authenticated (MUST);</li>
<li>report every condition that holds rather than the first found (MUST);</li>
<li>never report a chain-level condition as a per-receipt failure (MUST NOT);</li>
<li>never infer completeness from the absence of these conditions (MUST NOT);</li>
<li>(this one binds a <strong>Deployment</strong>, not a Chain Verifier) take temporal ordering from something
other than <tt>ts</tt> (MUST), since no check constrains it;</li>
<li>on a multi-agent chain, never report an unqualified tail check (MUST NOT) and state whose view the
check covers (MUST);</li>
<li>never treat a checkpoint's age as bounded by anything in the object (MUST NOT), since <tt>ts</tt> is
signer-chosen and this profile defines no expiry;</li>
<li>validate a supplied checkpoint's structure before authenticating it (MUST) -- closed object,
<tt>spec</tt>, the four value shapes and the <tt>sig</tt> sub-object -- and never turn a malformed checkpoint
into a finding about the chain (MUST NOT);</li>
<li>compare a supplied checkpoint's <tt>chain</tt> to the presented <tt>scope.chain</tt> (MUST), report a mismatch
as a finding about the checkpoint, and never let it fail an otherwise valid presented chain
(MUST NOT);</li>
<li>never return a positive result for a set on which any of the six chain conditions holds
(MUST NOT), with no exception for a byte-identical duplicate;</li>
<li>when an identity manifest is supplied, require a checkpoint's <tt>sig.kid</tt> to be authorized for the
genesis receipt's <tt>agent.id</tt> and reject any other trusted key as <tt>UNTRUSTED</tt> (MUST);</li>
<li>when no checkpoint is supplied, report the tail question as unanswered (MUST) rather than as an
absence of truncation (MUST NOT);</li>
<li>reject a presented chain in which two receipts share an <tt>agent.id</tt> and differ in <tt>sig.kid</tt> (MUST),
reporting <tt>chain-key-discontinuity</tt>;</li>
<li>where it <em>chooses</em> to compare resolved key material as well (MAY), report that as a separate
result rather than under the identifier comparison's name (MUST, conditional on the choice);</li>
<li>report an unusable checkpoint as unusable and say which reason applies -- malformed, wrong chain,
or unauthenticated -- and report the tail question as unanswered in every one of those cases
(MUST);</li>
<li>(<strong>Deployment</strong>) present a chain for verification from genesis (MUST), since a Chain Verifier
refuses a presentation that begins elsewhere;</li>
<li>(<strong>Deployment</strong>) never assume the outer envelope's <tt>kid</tt> is the identifier an identity manifest
authorizes for a checkpoint (MUST NOT) -- a checkpoint is a native artifact and the two identifier
spaces are separate;</li>
<li>(<strong>Deployment</strong>) where freshness is needed, impose a maximum checkpoint age from its own clock and
say so in its output (MUST), because nothing in the object bounds it.</li>
</ul>
<t><strong>This list is not exhaustive, and this revision no longer claims that it is.</strong> Three earlier drafts
made that claim and each was wrong: the list is written by hand, 5.1 and 5.2 are long, and a
hand-written inventory of a long section rots the moment either changes. It records the principal
additions -- most binding a Chain Verifier, four binding a <strong>Deployment</strong> as marked, one conditional
on an option a verifier need not take. None constrains a Producer or changes how a chain is <em>built</em>,
so a profile that adopts the convention to build receipts is unaffected. A reader who needs the
complete set has to read 5.1 and 5.2, which is the honest instruction.</t>
<t><strong>One documentation narrowing, disclosed rather than buried.</strong> -00 said &quot;a verifier checking a
range of receipts MUST verify ...&quot; and never said what a range may be. 5.1 now states that a
conforming verifier requires the complete chain from <tt>seq == 0</tt> and refuses a presentation that
begins elsewhere. That is a narrowing <strong>of the text, not of any behaviour</strong>: all five verifiers -- the reference
implementation, the Python one compared directly to it, and the Go, Rust and C# ones compared to
Python -- already hardcode <tt>0..n-1</tt>, and a presentation of <tt>seq 1,2</tt> has always been refused as a
missing genesis. So no receipt that verified under a -00 implementation fails under a -01
implementation, and no implementation changes; what changed is that the requirement is now written
down where an implementer can read it, and the capability nobody has -- verifying a mid-chain range
against a trusted anchor -- is named as absent in <xref target="implementation-status"></xref> instead of being implied
by silence.</t>
<t>The same care applies to the gap condition itself: a correctly linked chain presenting <tt>0</tt> then <tt>2</tt>
was underspecified in -00 and is a <tt>chain-sequence-gap</tt> failure under 5.1. Any implementation of
-00 already rejected it, so this is again documentation catching up to behaviour rather than a
change of outcome -- but an implementer who read -00 literally and built a permissive verifier would
find their verifier non-conforming under -01, and that is worth saying plainly.</t>
<t><strong>What happened to every -00 section, checked against the posted text rather than remembered.</strong>
Sections 1 through 7 keep their -00 numbers. The rest moved, and one was removed:</t>
<table>
<thead>
<tr>
<th>In -00</th>
<th>In -01</th>
</tr>
</thead>

<tbody>
<tr>
<td>1-7</td>
<td>unchanged numbers</td>
</tr>

<tr>
<td>8 -- Relationship to ACTA signed receipts (equivalence), with its mapping table</td>
<td><strong>removed.</strong> It changed no requirement, and a field-by-field mapping to one other individual draft did not earn a section here</td>
</tr>

<tr>
<td>9 -- Policy-replay out of scope</td>
<td>11</td>
</tr>

<tr>
<td>10 -- Security Considerations</td>
<td>13</td>
</tr>

<tr>
<td>11 -- IANA Considerations</td>
<td>16</td>
</tr>

<tr>
<td>Appendix A -- Implementation status</td>
<td>12, promoted into the body where RFC 7942 places it</td>
</tr>

<tr>
<td>Appendices B and C -- the two reference lists</td>
<td>17.1 and 17.2</td>
</tr>

<tr>
<td>--</td>
<td>8, 9 and 10 are new (external-world non-claims, authorization vs evidence, the digest boundary)</td>
</tr>

<tr>
<td>--</td>
<td><strong>14, Privacy Considerations, is new</strong>, and -00 had no privacy section at all</td>
</tr>

<tr>
<td>--</td>
<td><strong>15, Open questions for the working group, is new</strong></td>
</tr>

<tr>
<td>--</td>
<td>Appendix A (this change log) and Appendix B (acknowledgments) are new</td>
</tr>
</tbody>
</table><t>The record format itself is untouched: no member of <tt>noa.receipt/0.1</tt> is added, removed, or
re-typed.</t>
</section>

<section anchor="what-the-author-committed-to-on-23-july-item-by-item"><name>What the author committed to on 23 July, item by item</name>
<t>The author's reply to the thread (<eref target="https://mailarchive.ietf.org/arch/msg/scitt/7QfK2KTD-WdRrPMnfZlwnDC3_NA/"/>)
listed ten things -01 would do. Eight are done in this text; two -- items 3 and 4 -- are partly
done, and the table says which part of each is outstanding.</t>
<table>
<thead>
<tr>
<th>Committed</th>
<th>Where it landed</th>
</tr>
</thead>

<tbody>
<tr>
<td>1. Separate authorization, controller-reported outcome and physical completion</td>
<td><xref target="three-claims"></xref></td>
</tr>

<tr>
<td>2. Define trust semantics and failure behaviour for each claim independently</td>
<td><xref target="purpose"></xref>, <xref target="absence"></xref></td>
</tr>

<tr>
<td>3. Make signer relationships machine-readable when signed outcome evidence is present</td>
<td><xref target="who-signed"></xref>; derived and machine-readable in the reconciler (<xref target="implementation-status"></xref>), <strong>not yet surfaced in bundle verdicts</strong>, and never derivable as organizational independence today</td>
</tr>

<tr>
<td>4. Add purpose-specific negative conformance vectors</td>
<td>The attribution-substitution vector landed at <tt>df777a6</tt> (corpus 33 -&gt; 34); the rest are named and committed in <xref target="implementation-status"></xref> and <strong>not yet written</strong> -- stated as open, not as done</td>
</tr>

<tr>
<td>5. Define the shared action-digest construction rather than assume equivalence with <tt>action.paramsHash</tt></td>
<td><xref target="digest-boundary"></xref>, construction published separately <xref target="I-D.toraman-noa-action-digest"></xref></td>
</tr>

<tr>
<td>6. Clarify the relation between the native receipt signature and the outer COSE envelope</td>
<td><xref target="payload-binding"></xref></td>
</tr>

<tr>
<td>7. Resolve the policy-identity inconsistency in the base payload</td>
<td><xref target="optional-members"></xref></td>
</tr>

<tr>
<td>8. Say clearly that a self-signed chain cannot prove an unseen tail does not exist</td>
<td><xref target="hash-chaining-and-completeness"></xref>, <xref target="chain-axes"></xref> and <xref target="security-considerations"></xref></td>
</tr>

<tr>
<td>9. Replace &quot;non-repudiable&quot; with the narrower wording</td>
<td>Abstract and <xref target="introduction"></xref> now say issuer-authenticated / signature-verifiable</td>
</tr>

<tr>
<td>10. Update the stale algorithm-migration note</td>
<td><xref target="implementation-status"></xref>: <tt>-19</tt> is emitted, <tt>-8</tt> rejected under a genuine signature, note corrected rather than carried forward -- and the stale <em>byte string</em> an earlier revision quoted is corrected in the same place</td>
</tr>
</tbody>
</table></section>

<section anchor="corrections-this-revision-makes-to-00-s-own-text"><name>Corrections this revision makes to -00's own text</name>

<ul spacing="compact">
<li><strong>The protected-header key-identifier rule CHANGED, and this is a change rather than a
correction.</strong> -00 required the protected header to contain &quot;a key identifier sufficient to resolve
the verification key: <tt>kid</tt> (label 4), <strong>or</strong> a certificate reference (<tt>x5t</tt> label 34 / <tt>x5chain</tt>
label 33) where a PKI is used&quot; -- a disjunction, not a contradiction. -01 makes a resolvable <tt>kid</tt>
mandatory and demotes the certificate references to additional-only, because a verifier conforming
to this profile is required to resolve <tt>kid</tt> and is not required to process certificates. A
producer that satisfied -00 by carrying <tt>x5t</tt> alone is <strong>not</strong> conformant to -01. <xref target="cose"></xref> now says
<tt>kid</tt> SHOULD be protected,
MAY be unprotected, MUST be resolvable from either with the protected copy preferred, and that a
verifier MUST report which bucket it used -- because attribution from an unprotected identifier is
not covered by the signature, so an unprotected <tt>kid</tt> may resolve a key but never names an
identity (<xref target="identity-binding"></xref>). It also states that additional registered headers MUST NOT cause
rejection unless listed in <tt>crit</tt>.</li>
<li><strong>Terminology defined a <tt>policyHash</tt> the payload never carried.</strong> <xref target="optional-members"></xref> resolves it
in favour of what the wire format does: <tt>governance.ruleId</tt> is a label, the optional
<tt>governance.compliance</tt> member is where a policy commitment lives, and neither makes a receipt
replayable.</li>
<li><strong><tt>scope.tenant</tt> was shown as always present.</strong> It is optional, and <xref target="optional-members"></xref> states
the rule that matters: absence MUST NOT reset a tenancy comparison, or an optional field becomes
a splice channel.</li>
<li><strong>-00 said the commitment fields were not carried at all</strong>, in the section that is now
<xref target="policy-replay-is-out-of-scope-for-this-revision"></xref>. They may be; what remains
unspecified is the replay <em>construction</em>. The prohibition on presenting a receipt as carrying a
re-derivable verdict is unchanged.</li>
<li><strong>The implementation-status byte string was wrong.</strong> An earlier revision quoted <tt>a10132</tt> as the
protected header this implementation emits. It is not: that encodes <tt>{1: -19}</tt> alone, and the
emitted header is the two-member map <tt>{1: -19, 4: kid}</tt> -- the <tt>kid</tt> having been moved into the
signed bucket, which the same document's own <xref target="cose"></xref> says. The identifier claim was right and the
byte-level claim was not; <xref target="implementation-status"></xref> now states the map, and the paired <tt>a10127</tt> claim about
the deprecated identifier is dropped rather than repeated. It was found by an adversarial pass
that decoded a shipped vector -- one command, which is exactly how a reviewer would have found it.</li>
<li><strong>The profile called its objects SCITT Signed Statements and called them registrable.</strong> They are
COSE_Sign1 objects; a Signed Statement additionally needs protected CWT Claims that a
Transparency Service can apply a Registration Policy to, and the reference implementation emits
none. <xref target="signed-statement"></xref> is new and specifies the mapping normatively; <xref target="cose"></xref> and Section 7 no
longer assert registrability in the present tense; and the gap heads the not-implemented register
in <xref target="implementation-status"></xref>. This was the round-1 review's categorical objection and it was right.</li>
<li><strong>The payload figure omitted <tt>sig</tt>.</strong> -00's figure ended at <tt>chain</tt>, which made its own
&quot;JCS(receipt WITHOUT <tt>chain.hash</tt> AND WITHOUT <tt>sig.value</tt>)&quot; construction impossible to implement
from the figure. <tt>sig</tt> (with <tt>alg</tt>, <tt>kid</tt>, <tt>value</tt>) is now shown as the mandatory member the
schema has always required.</li>
<li><strong>The optional-member contract listed two members; the schema has seven.</strong> <tt>agent.model</tt>,
<tt>action.rollbackRef</tt>, <tt>governance.ruleId</tt>, <tt>governance.approval</tt> and <tt>compliance.verdict</tt> join
<tt>scope.tenant</tt> and <tt>governance.compliance</tt>, and the figure marks each. The rule that
present-as-<tt>null</tt> is not absence -- different bytes, different hash -- is stated with them.</li>
<li><strong>Detached payloads were described backwards.</strong> -00 said a detached payload's digest is bound
through <tt>chain.hash</tt>. COSE requires the detached bytes themselves for the <tt>Sig_structure</tt>, and
<tt>chain.hash</tt> is a chain-linkage value computed over the receipt with <tt>chain.hash</tt> and <tt>sig.value</tt>
removed. The shipped parser refuses a non-byte-string payload. <xref target="payload-binding"></xref> now defines the
attached form only, and a detached mode is future work.</li>
<li><strong>The algorithm rule contradicted itself.</strong> -00 said verifiers MUST reject every other <tt>alg</tt> and
then that they SHOULD reject <tt>-8</tt> unless a legacy mode was negotiated. <tt>-8</tt> is not a special
case, no such mode exists in any implementation, and the exception is deleted.</li>
<li><strong>Certificate references were offered as a sufficient key identifier.</strong> They are now additional
and OPTIONAL, with <tt>kid</tt> required, matching a verifier that ignores <tt>x5t</tt>/<tt>x5chain</tt>.</li>
<li><strong>The signer-relationship comparator named the wrong pair.</strong> The relationship that decides
independence is outcome signer versus request/authorization signer -- what both reviewers asked
for, and what this document's own security prose already said. An earlier draft compared against
the receipt's issuer, which would report independence for an outcome signed by the requester.</li>
<li><strong>The escaping row was wrong and the normalization row was too absolute.</strong> JCS also escapes <tt>&quot;</tt>
and <tt>\</tt>; and a verifier MAY refuse a non-NFC receipt outright, which the shipped verifier offers
as a mode. What no verifier may do -- normalize before hashing -- is unchanged and is the rule that
matters.</li>
<li><strong>Timestamp validation was underspecified, and -01 makes the strict reading normative.</strong> -00
required &quot;RFC 3339&quot; form without saying what a verifier must do with a string that matches the
shape but is not a valid date-time. <xref target="optional-members"></xref> now requires rejection and pins the
accepted grammar; <xref target="implementation-status"></xref> records that the shipped validator stops at the shape
and why it will stay there. An earlier -01 draft carried the opposite sentence here, which
contradicted three other surfaces; this is the corrected one.</li>
<li><strong>-00's NFC rule was rewritten, and that rewrite is itself a change.</strong> -00 said flatly that all
strings MUST be Unicode NFC, which reads as a validity condition on receipts. -01 splits it into
a producer obligation and a verifier prohibition on normalizing, plus an optional refusal mode.
A receipt that -00 called malformed may verify under -01 in the default mode, and vice versa
under the refusal mode; implementers moving between revisions should read <xref target="canon-params"></xref> and
<xref target="optional-members"></xref> together.</li>
<li><strong>Nine -00 fragment anchors were broken and are restored.</strong> A shortened anchor scheme in an
earlier -01 draft would have broken deep links into <tt>introduction</tt>,
<tt>relationship-to-other-work</tt>, <tt>identity-binding</tt>,
<tt>policy-replay-is-out-of-scope-for-this-revision</tt>, <tt>security-considerations</tt>,
<tt>implementation-status</tt>, <tt>hash-chaining-and-completeness</tt>, and the two reference sections. Every
one of the -00 anchors is restored in the published XML, so both numeric and fragment references
from -00 continue to resolve.</li>
<li><strong>Six references were reclassified or added.</strong> RFC 9053 (the deprecated identifier named in a
MUST, and the curve registry), RFC 3339, RFC 2104, RFC 9360 and Unicode Annex #15 are normative
where the text depends on them; FIPS 180-4 is cited for SHA-256.</li>
<li><strong>Requirements that existed only as code are now text.</strong> Two reverse audits -- code read first,
then the requirement it should have mapped to -- found rejection boundaries that every shipped
verifier enforces and that no revision of this document required. They are requirements here,
transcribed rather than invented: the 128-code-point bound on <tt>id</tt> and the seven non-empty
members, and the closed <tt>ALLOW</tt>/<tt>DENY</tt> set for <tt>governance.compliance.verdict</tt>
(<xref target="field-semantics"></xref>); the three JSON member names refused at the parse boundary (<xref target="canon-params"></xref>);
the exact <tt>sig.alg</tt> literal, the Ed25519 key-type rule, the canonical key encoding and the two
public-point rejections (<xref target="payload-binding"></xref>); the UTF-8 round trip before a <tt>kid</tt> is resolved
(<xref target="protected-header"></xref>); and the checkpoint's closed object, value shapes and chain equality
(<xref target="checkpoint"></xref>). One of them is not stated by one implementation, which <xref target="implementation-status"></xref>
records. The general rule of <xref target="absence"></xref> -- that a verifier's own failure to authenticate or
understand an input MUST NOT become a finding against the subject of the verification -- is new
as a rule; the two requirements it generalizes were already in <xref target="chain-axes"></xref> and <xref target="checkpoint"></xref>.</li>
<li><strong>Conformance classes are new, and they change how every MUST is read.</strong> -00 and earlier -01
drafts said &quot;a verifier&quot; and meant at least three different things, which made some requirements
unsatisfiable by a single-receipt implementation and others meaningless to a chain one.
<xref target="classes"></xref> defines Receipt, Chain and Evidence Verifier; each normative section states its class;
and no class inherits another's obligations. Two implementers can now build to the <em>same class</em>
and know they were asked for the same checks -- which is not the same as being able to interoperate
from this document alone. For the Receipt and Chain classes the inputs are defined here and
agreement is testable on shared vectors. For the Evidence class it is not: <xref target="not-defined-here"></xref>
lists the artifacts that layer runs over and this revision defines none of their wire formats, so
two Evidence Verifiers cannot be given common inputs or compared on their outputs by reading this
text (<xref target="open-questions"></xref>).</li>
<li><strong>What this profile does not define is now a section rather than a scatter.</strong> <xref target="not-defined-here"></xref>
lists the objects behind the Evidence-Verifier requirements -- identity manifest, approval and
outcome artifacts, trust configuration, shared digest, result schema -- and says plainly that
their formats live in the companions and the reference implementation. An unimplementable MUST is
worse than none; the requirements that remain are the ones a reader can act on.</li>
<li><strong>The non-equivocation claim is narrowed to what registration actually buys.</strong> The Abstract,
<xref target="registration"></xref> and <xref target="security-considerations"></xref> previously asserted that registration supplies
non-equivocation. It supplies a third party's signed proof of registration. Non-equivocation
needs consistency proofs and monitoring, which this profile does not specify and places no
requirement on anyone to perform -- and a Transparency Receipt handed to an offline party <em>by the
prover</em> is prover-supplied evidence, exactly as <xref target="chain-axes"></xref> says of a checkpoint. The document's
own argument now applies to its own escape hatch.</li>
<li><strong>Registration binds more than the envelope.</strong> <xref target="registration"></xref> now requires a Transparency
Service to verify the payload's own schema and native signature, and to check <tt>sub</tt> against
<tt>scope.chain</tt>. Without the first, a trusted registrant can obtain a log entry for a receipt nobody
in the chain signed; without the second, a chain-A receipt can be registered under chain-B.</li>
<li><strong>Two further chain-level conditions are named.</strong> The cross-tenant splice rule of
<xref target="optional-members"></xref> is a set property the axis table did not carry; it is now
<tt>chain-tenant-split</tt>. And every shipped verifier already rejects a mid-chain key change for one
agent while no revision had ever named it: that is <tt>chain-key-discontinuity</tt>, the condition that
stops a trusted-but-unrelated key from extending another party's chain. The set of chain
conditions is now six, and the two checkpoint-derived results that earlier drafts of this revision
counted among them are on a separate axis: a checkpoint is a separate object answering a separate
question, and nothing on that axis can fail a chain (<xref target="chain-axes"></xref>).</li>
<li><strong>An older checkpoint over a longer chain is no longer truncation.</strong> <xref target="chain-axes"></xref> distinguishes a
consistent extension from a truncation, and <xref target="implementation-status"></xref> records that the reference
implementation reports one verdict for both.</li>
<li><strong>The re-canonicalization requirement was impossible as written</strong> -- it demanded that a
serialization omitting two members equal the bytes those members are in. <xref target="payload-binding"></xref> now
states the check the implementation actually performs: <tt>JCS(parse(bytes)) == bytes</tt> over the whole
payload.</li>
<li><strong>Field semantics are pinned</strong> (<xref target="field-semantics"></xref>): the notation legend, <tt>spec</tt> exactness, the
absence of identifier namespaces, what <tt>ts</tt> means, and the cross-field rules that stop one record
saying two things -- including the simulation-marker rule, which -00 left open.</li>
<li><strong>Timestamps have a grammar</strong> (<xref target="optional-members"></xref>): which spellings are accepted, why the
spelling is signed, and why a verifier must not normalize one.</li>
<li><strong>The relay attack is in the threat model</strong> (<xref target="security-considerations"></xref>). The two-signature design
permits any holder of a trusted key to re-wrap someone else's receipt; that was the one attack the
design directly enables and the one the section omitted.</li>
<li><strong>Parser resource bounds are named as the implementer's responsibility</strong>, since this profile sets
none and requires a complete chain from genesis.</li>
<li><strong>Two RFC 9943 section pinpoints were wrong and are corrected.</strong> An earlier -01 draft cited
&quot;Section 6.12&quot; and &quot;Section 6.13&quot; for the <tt>kid</tt> and CWT Claims requirements. RFC 9943 has no such
subsections -- its Section 6 runs 6, 6.1, 6.2, 6.3 -- and both requirements live in the body of
Section 6. The citations now say Section 6. The error entered through a summarized reading of the
RFC rather than its text; every external section pinpoint in this revision has since been checked
against the RFC's own published text.</li>
<li><strong>The deterministic-CBOR pin was too coarse.</strong> &quot;Section 4.2&quot; named the whole Deterministically
Encoded CBOR section; the requirement this profile depends on is the Core Deterministic Encoding
Requirements of Section 4.2.1, which is now what it cites.</li>
<li><strong>A quoted claim about another protocol's specification was removed.</strong> The Introduction quoted a
sentence about non-repudiation attributed to an agent protocol, with no primary citation to
support it. The surrounding point does not need it, and the paragraph now states the gap in
neutral technical terms. The OWASP entry is cited to its own project page, listing MCP08:2025 by
its published title; the severity rating an earlier draft asserted is not on that page and is
gone.</li>
<li><strong>The intended status CHANGED, and it is a change rather than an editorial detail.</strong> The posted
-00 carried &quot;Intended status: Informational&quot;; this revision carries &quot;Intended status: Standards
Track&quot;. The document now defines wire-format acceptance rules that two implementations must agree
on, which is why the header moved -- and the move is not settled: <xref target="open-questions"></xref> states the
case for Experimental instead, and the header records the current intention while the working
group decides, not a conclusion the author reached alone.</li>
<li><strong>The SCITT Architecture reference was a work-in-progress draft.</strong> It was published as
<xref target="RFC9943"></xref> in June 2026 and is cited as an RFC.</li>
<li><strong>The equivalence section is gone, not edited.</strong> -00 Section 8 mapped this profile's fields onto
another individual draft's field catalogue. It changed no requirement in either document, so it is
removed rather than carried forward; <xref target="relationship-to-other-work"></xref> still names that work and says
a mapping is available to whoever needs to port one.</li>
</ul>
</section>
</section>

<section anchor="ack"><name>Acknowledgments</name>
<t>This revision is the product of a public review thread on the SCITT mailing list in July 2026,
archived at <eref target="https://mailarchive.ietf.org/arch/browse/scitt/?q=noa-scitt-ai-agent-receipt"/>. The
reviewers named here disagreed usefully, contributed concrete text, and in one case ran their own
implementation against this profile's adversarial corpus rather than arguing about it. Errors that
remain are the author's.</t>

<ul spacing="compact">
<li><strong>Carlos Hernandez</strong> separated a controller's report from the physical effect and asked for a
machine-checkable non-claim rather than a caveat in prose: <xref target="three-claims"></xref>, <xref target="purpose"></xref> and
<xref target="separate-artifacts"></xref>.</li>
<li><strong>Iman Schrock</strong> (EMILIA Protocol) put pre-execution authorization in front of both other claims,
supplied the substitution negative case, argued the signer relationship belongs in a field, and
cautioned against treating two digest constructions as equal without a shared vector:
<xref target="authorization"></xref>, <xref target="who-signed"></xref> and <xref target="digest-boundary"></xref>.</li>
<li><strong>Steven Mih</strong> held the thread to shared conformance vectors rather than shared vocabulary alone,
which is the discipline <xref target="implementation-status"></xref> commits to.</li>
<li><strong>Joel Hillier</strong> (Certisyn, Inc.) ran an independent reconciliation implementation against the
published corpora and reported the numbers, including the failure no per-receipt verifier can
reach: <xref target="chain-axes"></xref>, <xref target="canon-params"></xref> and the indeterminate-control discussion in
<xref target="digest-boundary"></xref>.</li>
</ul>
</section>

</back>

</rfc>
