A SCITT Profile for AI-Agent Action Receipts
draft-noa-scitt-ai-agent-receipt-01
This document is an Internet-Draft (I-D).
Anyone may submit an I-D to the IETF.
This I-D is not endorsed by the IETF and has no formal standing in the
IETF standards process.
| Document | Type | Active Internet-Draft (individual) | |
|---|---|---|---|
| Author | Tora Toraman | ||
| Last updated | 2026-08-14 | ||
| RFC stream | (None) | ||
| Intended RFC status | (None) | ||
| Formats | |||
| Stream | Stream state | (No stream defined) | |
| Consensus boilerplate | Unknown | ||
| RFC Editor Note | (None) | ||
| IESG | IESG state | I-D Exists | |
| Telechat date | (None) | ||
| Responsible AD | (None) | ||
| Send notices to | (None) |
draft-noa-scitt-ai-agent-receipt-01
SCITT T. Toraman
Internet-Draft NordenSoft
Intended status: Standards Track 15 August 2026
Expires: 16 February 2027
A SCITT Profile for AI-Agent Action Receipts
draft-noa-scitt-ai-agent-receipt-01
Abstract
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.
Status of This Memo
This Internet-Draft is submitted in full conformance with the
provisions of BCP 78 and BCP 79.
Toraman Expires 16 February 2027 [Page 1]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
Internet-Drafts are working documents of the Internet Engineering
Task Force (IETF). Note that other groups may also distribute
working documents as Internet-Drafts. The list of current Internet-
Drafts is at https://datatracker.ietf.org/drafts/current/.
Internet-Drafts are draft documents valid for a maximum of six months
and may be updated, replaced, or obsoleted by other documents at any
time. It is inappropriate to use Internet-Drafts as reference
material or to cite them other than as "work in progress."
This Internet-Draft will expire on 16 February 2027.
Copyright Notice
Copyright (c) 2026 IETF Trust and the persons identified as the
document authors. All rights reserved.
This document is subject to BCP 78 and the IETF Trust's Legal
Provisions Relating to IETF Documents (https://trustee.ietf.org/
license-info) in effect on the date of publication of this document.
Please review these documents carefully, as they describe your rights
and restrictions with respect to this document. Code Components
extracted from this document must include Revised BSD License text as
described in Section 4.e of the Trust Legal Provisions and are
provided without warranty as described in the Revised BSD License.
Table of Contents
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1. Scope and the adjacent slot . . . . . . . . . . . . . . . 6
1.2. Relationship to other work . . . . . . . . . . . . . . . 6
1.3. Requirements Language . . . . . . . . . . . . . . . . . . 7
1.4. Actors . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.5. What this profile does not define . . . . . . . . . . . . 10
2. Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 11
3. Receipt structure (payload) . . . . . . . . . . . . . . . . . 15
3.1. Field semantics and cross-field rules . . . . . . . . . . 17
3.2. Canonicalization parameters . . . . . . . . . . . . . . . 20
3.3. Optional members, and what their absence means . . . . . 22
4. COSE Signed Statement profile . . . . . . . . . . . . . . . . 24
4.1. Protected header . . . . . . . . . . . . . . . . . . . . 25
4.2. Payload binding, and the two signatures . . . . . . . . . 27
4.3. Carrying a receipt as an RFC 9943 Signed Statement . . . 31
5. Hash-chaining and completeness . . . . . . . . . . . . . . . 33
5.1. Chain-level failure axes . . . . . . . . . . . . . . . . 34
5.2. Checkpoint structure . . . . . . . . . . . . . . . . . . 40
6. Identity binding . . . . . . . . . . . . . . . . . . . . . . 43
6.1. Who signed is not who approved . . . . . . . . . . . . . 45
Toraman Expires 16 February 2027 [Page 2]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
7. Registration in a Transparency Service . . . . . . . . . . . 48
8. What a receipt does not claim about the external world . . . 51
8.1. Three claims, three independent failures . . . . . . . . 51
8.2. Validity and sufficiency are separate results . . . . . . 52
8.3. Absence is not a negative claim . . . . . . . . . . . . . 54
8.4. Why the base record is not extended . . . . . . . . . . . 55
9. Pre-execution authorization and post-execution evidence . . . 56
10. The shared action digest boundary . . . . . . . . . . . . . . 57
11. Policy replay is out of scope . . . . . . . . . . . . . . . . 59
12. Implementation status . . . . . . . . . . . . . . . . . . . . 60
13. Security Considerations . . . . . . . . . . . . . . . . . . . 73
14. Privacy Considerations . . . . . . . . . . . . . . . . . . . 77
15. Open questions for the working group . . . . . . . . . . . . 78
16. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 81
17. References . . . . . . . . . . . . . . . . . . . . . . . . . 81
17.1. Normative References . . . . . . . . . . . . . . . . . . 81
17.2. Informative References . . . . . . . . . . . . . . . . . 83
Appendix A. Changes since -00 . . . . . . . . . . . . . . . . . 85
A.1. Interface stability, and the two corrections a citing
document must act on . . . . . . . . . . . . . . . . . . 85
A.2. What the author committed to on 23 July, item by item . . 90
A.3. Corrections this revision makes to -00's own text . . . . 91
Appendix B. Acknowledgments . . . . . . . . . . . . . . . . . . 96
Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 97
1. Introduction
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: _what actually happened, and who can prove it?_
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.
This document defines a *receipt*: 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
*and* the key that signed the envelope, which need not be the same
(Section 4.2). Either way the check needs no access to the agent,
the vendor, or any running service. It is deliberately _narrow_. 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.
Toraman Expires 16 February 2027 [Page 3]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
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 *not* establish -- and, where the
industry needs those things established, points at the separate
documents that do it.
*Three documents, one system.* 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:
* *This profile -- the record.* One signed record per action, hash-
chained (each record carries a fingerprint of the one before it,
so a deletion _between two presented records_ is visible -- a
deletion from the end is not, which is what Section 5.1 is about),
and carriable as a Signed Statement for registration in a
transparency log (Section 4.3).
* *The action digest [I-D.toraman-noa-action-digest], published 14
August 2026 -- the binding.* 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.
* *Settlement evidence [I-D.toraman-noa-settlement-evidence],
published 14 August 2026 -- the independent witness.* 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.
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 (Section 8.1), and no one of
them is asked to carry the others' weight.
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 _accountability_ layer --
a standardized, tamper-evident, issuer-authenticated record of an
agent's actions -- open. OWASP's MCP Top 10 lists "Lack of Audit and
Telemetry" as MCP08:2025 [OWASP-MCP]. Several recent efforts emit
signed PERMIT/DENY _decision_ receipts for agent actions; what is
missing is a single, SCITT-native, COSE-based profile for *per-
action* 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.
Toraman Expires 16 February 2027 [Page 4]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
This profile does not define a new signature container. It defines
an application payload profile carried in a COSE_Sign1 [RFC9052], so
the outer signature can be verified by conforming COSE
implementations that support the fully-specified Ed25519 algorithm
identifier used by this profile. Section 4.3 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.
*What this revision adds.* Review on the SCITT mailing list in July
2026 found that the single phrase "real-world outcome" in -00 hides
three claims that a consumer must be able to fail separately: whether
a named person authorized this exact action _before_ it ran, what a
downstream machine _reported_ afterwards, and whether the intended
_physical_ effect actually occurred. Section 8 and Section 9 pull
them apart and require each to be reported as a result a verifier
renders, not a caveat a reader has to remember. Section 10 draws the
boundary around the value that lets records about one action be
joined at all. Appendix A 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.
The base record noa.receipt/0.1 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 *not*
follow that a -00 receipt is accepted by a -01 verifier -- acceptance
is a separate question, answered in the next paragraph.
It does *not* guarantee that every -00 receipt is _valid_ 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
(Section 2); the object is closed against unknown members
(Section 3); a timestamp must be a valid RFC 3339 date-time and not
merely shape-correct (Section 3.3); id is bounded at 128 code points,
seven members must be non-empty strings and a present
governance.compliance.verdict must be ALLOW or DENY (Section 3.1);
sig.alg must be the exact literal ed25519 (Section 4.2); and the
cross-field rules of Section 3.1 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 (Section 3.3).
Toraman Expires 16 February 2027 [Page 5]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
For a document adopting this profile's chain convention by reference
(Section 1.2), the practical statement is therefore: the
_construction_ is unchanged; the _acceptance rules_ are stricter.
1.1. Scope and the adjacent slot
Two related SCITT-AI drafts occupy _different_ slots: an EU AI Act
Article 50 _disclosure_ profile (what content was generated and
disclosed) and a _session-archive_ format (a portable bundle of a
whole agent session). This profile is orthogonal to both: it defines
the *per-action signed receipt* -- one signed record per agent
action, carried bare or in a COSE_Sign1 (Section 4), and holding the
action, the recorded principal class, the recorded verdict, and any
policy identity the issuer chose to record.
Three slots adjacent to this one are named here because this revision
defines its boundaries against them rather than against prose: a
_reconciliation_ layer that admits heterogeneous attestations about
one action and reports per-source verdicts [I-D.hillier-scitt-arp];
an _executor-side admission boundary_ that decides whether an action
may be consumed [I-D.schrock-action-evidence-boundary]; and a
_correlation value_ that lets records produced by parties with no
shared schema commit to the same action projection
[I-D.toraman-noa-action-digest]. None of the three is a dependency
of this profile, and this profile is not a dependency of them.
1.2. Relationship to other work
* *SCITT Architecture* [RFC9943]: 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.)
* *Signed decision receipts for machine-to-machine access control*
[ACTA]: 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.
* *draft-dawkins-scitt-ai-article50* [AI-ART50], *draft-stone-aivs*
[AIVS], *draft-marques-asqav-compliance-receipts* [ASQAV], *draft-
nivalto-agentroa-route-authorization* [AGENTROA]: 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 _of_ 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.
Toraman Expires 16 February 2027 [Page 6]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
* *draft-kamimura-scitt-vcp* [SCITT-VCP]: a domain SCITT profile
(algorithmic-trading audit) whose structure this document follows.
* *draft-wilder-scitt-physical-site-engage-receipt* [PSER]: a
physical-site engagement profile that adopts the hash-chain
convention of Section 5 by reference. That section's number,
title and anchor are unchanged from -00. *Two sentences of its
text were factually wrong in -00 and are corrected in this
revision*; Appendix A gives the before and after, and an
implementer of [PSER] should read that entry before relying on
-00's wording. Its new subsection Section 5.1 adds Chain-
Verifier-class requirements that -00 did not state: a document
that adopts Section 5's convention to _build_ receipts is
unaffected, while a _verifier_ claiming conformance to -01 takes
on those obligations.
* *The action digest and one of its consumers*
[I-D.toraman-noa-action-digest],
[I-D.toraman-noa-settlement-evidence], 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; Section 10
states what a receipt does and does not contribute to such a
construction.
1.3. Requirements Language
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
"OPTIONAL" in this document are to be interpreted as described in BCP
14 [RFC2119] [RFC8174] when, and only when, they appear in all
capitals, as shown here.
1.4. Actors
Requirements in this document are written in one of two forms. Most
*name their actor* 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 *about an
artifact* -- "a receipt MUST contain no member other than those
shown", "sig.value MUST be canonical base64", "the kid SHOULD be
carried in the protected header" -- a shorthand used deliberately for
schema and encoding constraints, because writing each of them twice
would bury them.
An artifact-form sentence binds the *Producer* 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
Toraman Expires 16 February 2027 [Page 7]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
Producer MAY is a permission, and the corresponding verifier duty is
to ACCEPT what that permission allows, not to enjoy the same
latitude. Section 4 shows the shape — kid MAY be carried in the
unprotected header, and a verifier MUST resolve it from either
bucket.
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.
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 _future revision_ 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.
+===============+================+=================================+
| Actor | Holds / is | Is required to |
| | given | |
+===============+================+=================================+
| *Producer* | the signing | build receipts to Section 3, |
| (Issuer) | key, and the | Section 3.1 and Section 3.2, |
| | facts it | and sign them per Section 4.2 |
| | records | |
+---------------+----------------+---------------------------------+
| *Receipt | one receipt; a | check that one object: shape, |
| Verifier* | keyring; | canonical form, the recomputed |
| | optionally an | chain.hash, the signatures it |
| | identity | is given, and -- when a |
| | manifest | manifest is supplied -- |
| | | attribution |
+---------------+----------------+---------------------------------+
| *Chain | a presented | everything above for each |
| Verifier* | set of | member, plus the set-level |
| | receipts; a | conditions of Section 5.1 |
| | keyring; | |
| | optionally a | |
| | checkpoint and | |
| | an identity | |
Toraman Expires 16 February 2027 [Page 8]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
| | manifest | |
+---------------+----------------+---------------------------------+
| *Evidence | all of the | everything above, plus |
| Verifier* | above, plus | authorization, outcome, signer |
| (not a | the separately | relationship and sufficiency |
| conformance | signed | for that intent -- described |
| role in this | artifacts of | here, but NOT testable against |
| revision) | Section 8 and | this document, because those |
| | Section 9, a | artifacts, that configuration |
| | trust | and the result representation |
| | configuration, | are all left undefined |
| | and the | (Section 1.5). Nothing in this |
| | consumer | revision can be conformant or |
| | intent it was | nonconformant _as an Evidence |
| | asked to | Verifier_; the passages |
| | answer | addressed to it are analysis, |
| | | and a future revision or |
| | | companion must define its |
| | | inputs and outputs before the |
| | | role can carry conformance |
+---------------+----------------+---------------------------------+
| *Transparency | a submitted | apply the Registration Policy |
| Service* | Signed | of Section 7, which states |
| | Statement, and | which of its checks are |
| | a means of | conditional on what it can |
| | resolving the | resolve |
| | registrant's | |
| | verification | |
| | key | |
+---------------+----------------+---------------------------------+
| *Relying | a verifier's | read that result within the |
| Party* | result, and | limits Section 8 states; where |
| | any receipts | it compares, indexes or alerts |
| | it chose to | on receipt fields itself, apply |
| | keep | the byte-comparison and parsing |
| | | rules of Section 3.1 and |
| | | Section 13 |
+---------------+----------------+---------------------------------+
| *Deployment* | the running | choose the configurations this |
| (Operator) | system: | profile leaves open -- |
| | producers, | identifier syntax, NFC mode, |
| | verifiers, | resource limits, checkpoint |
| | keys, and what | issuance and freshness, what is |
| | is presented | registered -- and present |
| | or registered | complete chains for |
| | | verification |
+---------------+----------------+---------------------------------+
Toraman Expires 16 February 2027 [Page 9]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
Table 1
Three rules govern the table, and each closes a way of misreading it.
* *Inputs are part of the obligation, and an input may be empty.* 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
("when a checkpoint is supplied ... MUST"), 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 *empty* one is still that actor, still
conformant, and simply cannot authenticate anything. That is the
state Section 5.1 means by "holds no keyring" -- an accepted input
with nothing in it, not an implementation that refuses the input
and thereby declines the role.
* *A wider verifier inherits the narrower one's checks, and nothing
else.* 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.
* *One role per claim of conformance.* 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.
1.5. What this profile does not define
Several requirements in Section 6.1, Section 8, Section 9 and
Section 10 are addressed to the *Evidence Verifier* over objects
whose wire formats are *not* specified here. (The attribution rule
of Section 6 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:
* the *identity manifest* (binding agent.id to authorized key
identifiers): its encoding, distribution, signing, versioning and
revocation rules;
* the *approval artifacts* of Section 9 -- hold, decision, execution
grant, consumption and uncertainty records -- and therefore grant
identifiers, single-use nonces, audiences and expiries;
* the *outcome artifacts* of Section 8 -- controller-reported
outcome and physical observation;
Toraman Expires 16 February 2027 [Page 10]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
* the *trust configuration* from which a signer relationship is
derived, and the digests that identify it;
* a *shared action digest*, defined separately in
[I-D.toraman-noa-action-digest];
* the *result schema* in which a verifier reports any of the above.
This revision names result _tokens_ in several places -- the six
chain conditions and the two named checkpoint-axis outcomes, the
four absence results, the relationship vocabulary, the intents,
UNTRUSTED, and the per-receipt outcomes VALID, MALFORMED and
TAMPERED -- so that implementations describing the same finding
use the same word. *Two* results are deliberately *not* tokenised
in this revision: the conflict result of Section 8.2 and the
unproven-authorization result of Section 9. A third case that
might look like one is not: the indeterminate comparison of
Section 10 is reported with Section 8.3's INDETERMINATE, 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 (Section 15). 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.
Where such an object is absent, the corresponding requirement is not
a requirement a conforming implementation can fail: it has nothing to
evaluate, and Section 8 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 Section 12 -- not here.
2. Terminology
_Definitions bind every actor. Where an entry carries a requirement
it names its own actor: the entries below address the *Producer*
(what it MUST emit), the *Receipt Verifier* (what it MUST reject),
the *Relying Party* (what it MUST NOT read into a value) and the
*Deployment* (what it selects)._
* *Action Receipt:* the application-layer record defined by this
document -- a signed record attesting one agent action, presented
either *bare* (the canonical JSON payload plus its native
signature) or *enveloped* in a COSE_Sign1 (Section 4). Both
presentations are defined here. It becomes an RFC 9943 _Signed
Statement_ in the enveloped form, when that envelope also carries
the protected claims of Section 4.3 -- necessary conditions, with
the boundary noted there. *An Action Receipt is NOT a SCITT
Receipt.* The two documents that define the other object use it at
Toraman Expires 16 February 2027 [Page 11]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
different widths, and the difference matters. [RFC9942],
Section 3 defines a _Receipt_ 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.
[RFC9943] 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 *Transparency
Receipt* wherever it appears (Section 7), 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 "receipt"
without qualification inside a SCITT context, it means the Action
Receipt, and it says so.
* *Principal class:* the _category_ of authority the issuer recorded
the action as being taken on behalf of. agent.principal carries
one of exactly four enumerated values -- HUMAN, SERVICE, POLICY,
SANDBOX_SIM -- 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. SANDBOX_SIM marks a simulated
action.
* *Lifecycle verdict:* the value of governance.verdict, drawn from a
closed set of exactly seven uppercase tokens. (-00 called this a
_terminal_ verdict. It is not: DEFERRED records that no decision
has been made, and ALLOWED records a permission a later attempt
may still consume. The name is corrected here rather than carried
forward.) The tokens are: ALLOWED, BLOCKED, DEFERRED, EXECUTED,
FAILED, ROLLED_BACK, SIMULATED. 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: ALLOWED
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 (Section 9); EXECUTED
records that the action was dispatched or attempted there, and
nothing about what happened downstream (Section 8.1). EXECUTED
covers both "dispatched" and "attempted": those are different
states, this field does not separate them, and neither means the
action took effect. The remaining five record a refusal
(BLOCKED), a decision not yet made (DEFERRED), a failure at the
governed boundary (FAILED), an action the issuer records as
compensated (ROLLED_BACK -- the profile can check neither the
original effect nor the compensation), and a simulation
(SIMULATED).
Toraman Expires 16 February 2027 [Page 12]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
* *Simulation markers, and the rule that keeps them honest:* three
members can say something about simulation -- agent.principal ==
SANDBOX_SIM, governance.verdict == SIMULATED, and
governance.sandboxed. A producer MUST make them agree: if any one
of them marks the action as simulated, governance.sandboxed MUST
be true, and a verifier MUST reject a receipt in which
governance.sandboxed is false 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
(Section 8). Without the rule even the internal contradiction
would pass, and a reader could not tell which of two markers to
believe.
* *Risk class:* the value of action.riskClass, drawn from a closed
set of exactly five uppercase tokens: LOW, MEDIUM, HIGH, CRITICAL,
IRREVERSIBLE. 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
*Deployment* that gates approval authority on risk -- for example
requiring a higher approver tier for CRITICAL and IRREVERSIBLE
than for HIGH -- 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 (Section 9).
* *Governance mode:* the value of governance.mode, drawn from a
closed set of exactly four lowercase tokens: off, shadow,
approvals_on, on. 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 governance.verdict and the artifacts of
Section 9.
* *Policy:* a decision rule set with a stable content hash
(policyHash) 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
Toraman Expires 16 February 2027 [Page 13]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
description of intended practice, not a requirement it can test.
A receipt carries that hash only when the optional commitment of
Section 3.3 is present; governance.ruleId alone is a label, not a
commitment.
* *Keyring:* 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 "trusted key" means "a key in the keyring the
verifier was given".
* *Identity manifest:* an out-of-band, operator-vouched binding of
agent.id to its authorized key identifier(s) (kid), used to
authenticate _which agent_ acted, not merely that a trusted key
signed.
* *Genesis-bound chain:* a per-scope hash chain whose checkpoint
authority is bound to the _opener_ of the chain -- precisely, to
the keys an identity manifest authorizes for the agent.id of the
receipt at sequence 0 (Section 5.2), 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.
* *Transparency Service (TS):* as defined by the SCITT Architecture
[RFC9943].
* *Pre-execution authorization:* a statement, made _before_ an
action runs, that a named, accountable principal approved _that
exact action_ under stated bounds. It is a statement about what
may happen.
* *Post-execution evidence:* a statement, made _after_ an action was
dispatched, about what some party observed. It is a statement
about what did happen, under that party's own semantics.
* *Controller-reported outcome:* the terminal status a downstream
system reported for a request, under that system's own semantics.
It is evidence about a _report_, not about an effect.
* *Physical completion:* whether the intended real-world effect
actually occurred, as observed by an identified witness under a
criterion fixed before execution.
* *Consumer intent (purpose):* the question a relying party is
asking of a presented record -- for example "is this an
authorization?", "is this independent outcome proof?".
Cryptographic validity is evaluated against the artifact;
sufficiency is evaluated against the intent, and the two results
are separate (Section 8.2).
* *Authorization signer / outcome signer:* the key that signed the
authorization record, and the key that signed an outcome statement
about the same action. Their _relationship_ is a property a
verifier derives, not a property either signer may assert about
itself (Section 6.1).
Toraman Expires 16 February 2027 [Page 14]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
* *Shared action digest:* a value, computed under a pinned
construction, that lets records produced by different parties be
joined as committing to the same _action projection_. Correlation
only, and never a statement that the action was authorized; see
Section 10.
3. Receipt structure (payload)
_Every requirement below names its actor: construction rules bind the
*Producer*, validation rules the *Receipt Verifier*, the tenant rule
of Section 3.3 the *Chain Verifier*, and the comparison rules for
identifiers and timestamps the *Relying Party* (Section 1.4)._
The receipt payload is the JSON Canonicalization Scheme (JCS)
[RFC8785] 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 Section 3.2: producers MUST emit NFC, and verifiers MUST
NOT normalize. A verifier MAY be configured to _refuse_ a non-NFC
receipt, but a verifier that accepts one MUST NOT repair it, and in
either mode a *Relying Party* MUST NOT infer that a verified
receipt's strings are NFC (Section 3.3). The fields:
Toraman Expires 16 February 2027 [Page 15]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
{
"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>" }
}
*The object is closed.* 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 noa.receipt/0.1 -- that is what makes the
hash construction of Section 4.2 reproducible across implementations,
and it is why Section 8.4 carries new claims in separate artifacts
instead.
A ? marks a member that MAY be absent; every other member is
REQUIRED, including sig. No member of this record is added, removed,
or re-typed by this revision; -00's figure omitted sig and did not
mark the optional members, and both omissions are corrected here to
match the shipped schema (Section 3.3).
Toraman Expires 16 February 2027 [Page 16]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
*A Producer MUST NOT place raw prompts, tool arguments or secrets in
any member*, 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 (id, agent.id, agent.model, action.id,
action.canonical, governance.ruleId, governance.approval.by,
action.rollbackRef) are constrained in length and emptiness at most
(Section 3.1) 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
Section 14 says what follows. paramsHash MAY be hmac-sha256:<hex>
with a tenant-scoped key where a plain SHA-256 over a low-entropy
value (an amount, an id, a boolean) would be guessable.
*These commitments are per-producer, and this profile does not make
them comparable.* Neither action.paramsHash nor the three hashes of
governance.compliance 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. Section 10 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 hmac-sha256 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.
chain.hash is "sha256:" followed by the lowercase hex of SHA-256 over
the JCS serialization of the receipt with chain.hash and sig.value
removed; Section 4.2 pins that construction byte-exactly, together
with the signature computed over the same digest. Receipts in one
scope.chain are linked by prevHash; prevHash is null only at the
genesis receipt (seq == 0).
3.1. Field semantics and cross-field rules
_The cross-field rules address the *Receipt Verifier*; the emission
obligations they imply address the *Producer*. The tenant rule of
Section 3.3 addresses the *Chain Verifier*._
Toraman Expires 16 February 2027 [Page 17]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
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.
*Notation.* In the figure, null denotes the JSON null literal, never
the four-character string "null"; a quoted alternative such as
"sha256:<hex>|null" means "either that string form or JSON null".
Angle brackets mark a value the issuer supplies.
*The figure's types are normative.* A Receipt Verifier MUST reject a
receipt in which a member's JSON type differs from the one shown,
rather than coerce it: <bool> means the JSON literals true or false
and never the strings "true"/"false" or the numbers 1/0; <int> means
a JSON number that is an integer within the range Section 3.2 fixes;
every other member is a string except where null is shown as an
alternative. Three shapes are fixed exactly, because a later
comparison depends on them:
* chain.seq MUST be an integer and MUST NOT be negative;
* chain.hash, chain.prevHash when not null, and each of
governance.compliance's policyHash, readSetHash and inputsHash
MUST be "sha256:" followed by exactly 64 lowercase hexadecimal
digits;
* action.paramsHash MUST be that same form or "hmac-sha256:"
followed by exactly 64 lowercase hexadecimal digits.
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.
*spec.* The exact string noa.receipt/0.1. 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 spec and its own signing domain (Section 8.4).
*id, and identifiers generally.* id 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 scope.chain, agent.id, action.id,
action.canonical, governance.ruleId, action.rollbackRef and
governance.approval.by. 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 (Section 13).
Toraman Expires 16 February 2027 [Page 18]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
*Two syntactic bounds are pinned, because every implementation
already enforces them and no document said so.* Both bind the
*Producer* on construction and the *Receipt Verifier* on validation:
* id 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.
* Seven members MUST be non-empty strings, and a verifier MUST
reject a receipt in which any of them is the empty string: id,
scope.chain, agent.id, action.id, action.canonical, sig.kid and
sig.value. governance.approval.by is deliberately not among them
-- it is an opaque label this profile never authenticates
(Section 6.1), and forbidding one of its values would suggest the
others mean something. The checkpoint object of Section 5.2
carries the same rule for three further members, which is ten
string boundaries across the two objects.
*ts.* The time the _issuer recorded the receipt_. 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 (Section 3.3).
*action.canonical.* 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; action.id names the specific tool or operation
invoked.
*Cross-field rules a verifier MUST enforce.* These are the
combinations that would let one record say two things:
* governance.sandboxed MUST be true whenever agent.principal is
SANDBOX_SIM or governance.verdict is SIMULATED (Section 2);
* action.rollbackRef MUST be absent or null unless action.reversible
is true, and a governance.verdict of ROLLED_BACK MUST be
accompanied by action.reversible: true;
* where governance.compliance.verdict is present it records the
policy evaluation's own result, which is a different question from
governance.verdict; 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 ALLOW or DENY, and a
verifier MUST reject any other value rather than carry an
unrecognized string through into a report a relying party will
read.
Toraman Expires 16 February 2027 [Page 19]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
*What no rule constrains, stated so nobody assumes it.*
governance.approval may accompany any lifecycle verdict, and its at
may be later than ts: this profile places no temporal invariant on
them, because a signer-chosen timestamp cannot establish that an
approval preceded an action (Section 6.1). A verifier MUST NOT infer
ordering from these fields.
3.2. Canonicalization parameters
A digest is only interoperable if the parameters that produced it are
pinned. Naming a canonicalization ("canonical JSON") 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 *for the receipt
object as a whole*. One carve-out belongs next to the claim rather
than three sections away: the encoding of the action sub-object is
not fixed _for a party computing its own digest over that sub-
object_, because an optional member may be absent or present-as-null
and the two produce different bytes (Section 10). Inside a receipt
that choice is signed and therefore settled; across producers
computing an independent digest it is not.
+===============+==============================================+
| Parameter | Value for noa.receipt/0.1 |
+===============+==============================================+
| Serialization | JCS [RFC8785] |
+---------------+----------------------------------------------+
| Member sort | UTF-16 code unit (RFC 8785); NOT Unicode |
| | code point -- the two diverge for astral |
| | characters, which encode as surrogates |
| | D800-DBFF and sort BEFORE E000-FFFF |
+---------------+----------------------------------------------+
| Number | RFC 8785; integers only, abs(n) <= 2^53 - 1; |
| rendering | non-integer and non-finite values MUST be |
| | rejected |
+---------------+----------------------------------------------+
| Unicode | NONE applied by the canonicalizer, ever. |
| normalization | Producers MUST emit NFC ([UAX15]); verifiers |
| | MUST NOT normalize before hashing. A |
| | verifier MAY refuse a non-NFC receipt |
| | outright (see Section 3.3); what it MUST NOT |
| | do is silently repair one |
+---------------+----------------------------------------------+
| String | RFC 8785: " (U+0022) and \ (U+005C) escaped; |
| escaping | the short forms \b \t \n \f \r used where |
| | defined; every other control character below |
Toraman Expires 16 February 2027 [Page 20]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
| | U+0020 escaped as \u00xx; every other code |
| | point emitted literally as UTF-8 |
+---------------+----------------------------------------------+
| Unpaired | MUST be rejected (they would collapse to |
| surrogates | U+FFFD at the UTF-8 hashing step, mapping |
| | distinct inputs onto one digest) |
+---------------+----------------------------------------------+
| Duplicate | MUST be rejected at the parse boundary, not |
| member names | resolved last-wins |
+---------------+----------------------------------------------+
| Forbidden | __proto__, prototype and constructor MUST be |
| member names | rejected at the parse boundary, in every |
| | JSON document a verifier parses -- receipt, |
| | keyring, identity manifest, checkpoint -- |
| | and not filtered after parsing |
+---------------+----------------------------------------------+
| Digest | SHA-256 ([FIPS180-4]), lowercase hex, |
| | sha256: prefixed. action.paramsHash MAY |
| | instead be HMAC-SHA-256 ([RFC2104]) under a |
| | tenant-scoped key, written hmac-sha256:<hex> |
+---------------+----------------------------------------------+
| Timestamps | RFC 3339 [RFC3339] date-time, with Z or a |
| | numeric offset -- the accepted spellings are |
| | pinned in Section 3.3, which permits both. |
| | A verifier MUST reject a value that is not a |
| | valid RFC 3339 date-time, and valid syntax |
| | still does not make a timestamp truthful |
+---------------+----------------------------------------------+
| Domain | the receipt signature is over the UTF-8 |
| separation | bytes of "NOA-Receipt-v0.1-sig:" followed by |
| | the raw SHA-256 of the hash input, pinned |
| | byte-exactly in Section 4.2; the checkpoint |
| | signature uses a distinct tag (Section 5.2), |
| | so neither can be replayed as the other |
+---------------+----------------------------------------------+
Table 2
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 (Section 3) --
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.
Toraman Expires 16 February 2027 [Page 21]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
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
Section 4.2 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.
action.paramsHash 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. Section 10
states what a construction must carry before any two parties may
compare digests at all.
3.3. Optional members, and what their absence means
Seven members of Section 3 MAY be absent: scope.tenant, agent.model,
action.rollbackRef, governance.ruleId, governance.approval,
governance.compliance, and -- inside a present compliance -- its
verdict. 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 -- agent.model,
action.rollbackRef, governance.ruleId, governance.approval and
governance.compliance -- may also be present-as-null, which a
verifier MUST treat as the value null and not as absence: the two
encodings produce different canonical bytes and therefore different
hashes and signatures.
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.
* *scope.tenant* MAY be absent, and the security-bearing rule is
stated first (this one is *Chain-Verifier-class*, since it
compares receipts to one another): *a conforming Chain Verifier
MUST reject a transition between two present, unequal scope.tenant
values within one scope.chain.* 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 scope.tenant
as a fresh start, so A -> absent -> B is the same cross-tenant
splice as A -> B and MUST receive the same verdict, while A ->
absent -> A (the same tenant resuming) and absent -> A
(enrichment) remain valid. Without the carry-forward rule, an
optional field becomes a splice channel.
Toraman Expires 16 February 2027 [Page 22]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
* *governance.compliance* MAY be present. When present it carries a
commitment to the policy's published identity (policyHash), to the
policy's closed read-set (readSetHash), and to the recorded
decision inputs (inputsHash) -- 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 Section 11 still governs. Its presence
also does not establish that the referenced policy was adequate or
in force.
*On rejecting non-NFC input.* 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.
*On timestamps.* ts and governance.approval.at are RFC 3339 [RFC3339]
date-time values, and a conforming verifier *MUST reject a value that
is not a valid RFC 3339 date-time*. A digit-and-separator shape check
is not sufficient: 2026-13-45T99:99:99Z matches the shape while lying
outside the grammar's own numeric ranges, and a receipt carrying it
is malformed.
Because these values are inside the hashed payload, the _spelling_ 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:
* the T and Z separators MAY be upper or lower case, as RFC 3339
permits;
* a numeric offset (+HH:MM / -HH:MM) is permitted as well as Z; a
receipt is not required to be written in UTC, and -00:00 carries
the meaning RFC 3339 gives it;
* 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;
* a Producer MUST NOT emit a date-time whose seconds field reads 60,
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: [RFC3339], Section 5.7 admits 60 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 2030-06-30T23:59:60Z is a valid
Toraman Expires 16 February 2027 [Page 23]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
date-time 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 :59 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 Section 15 records it as a decision a working
group may reverse.
A verifier MUST NOT normalize a timestamp before hashing, for the
same reason it MUST NOT normalize anything else (Section 3.2).
Relying parties comparing timestamps across receipts MUST parse them
into instants first: byte equality and instant equality are different
questions here.
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.
governance.ruleId 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
governance.compliance, the receipt's "policy identity" is exactly
that label and nothing stronger. (-00's terminology defined a
policyHash that -00's payload never carried; this subsection resolves
that inconsistency in favour of what the wire format actually does.)
This profile adds no member to noa.receipt/0.1. A claim that this
record does not carry travels as a separate signed artifact, linked
by hash -- see Section 8.4 for why that choice is load-bearing rather
than stylistic.
4. COSE Signed Statement profile
*A receipt may be presented in two forms, and this profile defines
both.* A receipt's _payload_ is the JSON object of Section 3, and its
native signature (Section 4.2) is computed over that object; a
receipt presented as payload plus native signature, with no COSE
envelope, is a *bare receipt*. A receipt carried inside a COSE_Sign1
is an *enveloped receipt*, and only an enveloped receipt can become a
Signed Statement (Section 4.3). Both forms are legitimate
presentations: the reference implementation's chain path verifies
bare receipts and never touches COSE (Section 12), 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 chain.hash
and the native signature, and reports that no envelope was present.
Toraman Expires 16 February 2027 [Page 24]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
Whether a SCITT-native profile should _require_ the envelope is left
open (Section 15).
4.1. Protected header
_Every requirement below names its actor: what a *Producer* MUST
emit, and what a *Receipt Verifier* -- given one receipt and a
keyring (Section 1.4) -- MUST check. No set and no evidence
artifacts are needed._
An enveloped receipt is a COSE_Sign1 carried as the *tagged* 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 alg; 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 kid in the protected header while
also permitting it in the unprotected one). In detail:
* *alg (label 1) = -19 (Ed25519)* as registered in the IANA COSE
Algorithms registry by [RFC9864], Section 2.2 (the fully-specified
Ed25519 identifier; [RFC8032] Section 5.1 parameter set). Issuers
MUST use -19. *Verifiers MUST reject every other alg value*
(algorithm-confusion defense). That includes the polymorphic
EdDSA identifier -8, which [RFC9864], Section 4.2.2 marks
Deprecated (it was registered in [RFC9053]). 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 crv = 6
(Ed25519), the curve registered for Ed25519 in the COSE Elliptic
Curves registry ([RFC9053], 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 alg value claims. [RFC9864] makes alg = -19 self-
disambiguating, so a verifier MUST NOT rely on the key's crv alone
to determine the algorithm.
* *A key identifier sufficient to resolve the verification key.* kid
(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 kid 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 kid MAY be used to *resolve* the verification key,
and MUST NOT be reported as the emitter's *identity*, because it
Toraman Expires 16 February 2027 [Page 25]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
is not covered by the signature and can be swapped without
breaking it (Section 6). [RFC9052] treats kid as a hint that need
not be unique; this profile narrows that for a verifier's own key
store. Where one kid 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. kid is an opaque byte
string in [RFC9052] and this profile does not narrow that; what it
does require is that no verifier resolve it through a *lossy
decode*. A verifier whose key store is keyed by text MUST decode
the kid 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.
* *Certificate references are OPTIONAL and additional, never a
substitute.* A deployment using a PKI MAY additionally carry x5t
(label 34) or x5chain (label 33) as defined by [RFC9360]. A
receipt MUST NOT rely on them as its only key identifier, because
a verifier conforming to this profile is required to resolve kid
and is not required to process certificates. The reference
implementation ignores both headers and refuses a receipt carrying
neither a protected nor an unprotected kid (Section 12).
* *Unknown headers are ignored, critical ones are fatal.* The mere
presence of an additional registered header parameter -- kid,
content type, CWT_Claims, or crit itself -- is never on its own a
reason to reject a COSE_Sign1. What decides the outcome is crit
(label 2). *This profile requires* a verifier to ignore any header
parameter it does not understand that crit does not list, rather
than reject the object -- that rule is this document's, not
[RFC9052]'s, which defines crit without imposing it. What
[RFC9052], Section 3.1 does state is the fatal direction: a crit
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 crit lists a header parameter it cannot
process. Section 4.3 adds one further rejection rule, for
CWT_Claims appearing in both buckets.
The protected header MUST satisfy the Core Deterministic Encoding
Requirements of [RFC8949], 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.
Toraman Expires 16 February 2027 [Page 26]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
4.2. Payload binding, and the two signatures
_Construction rules address the *Producer*; verification rules
address the *Receipt Verifier*, which is given the receipt and a
keyring (Section 1.4)._
The payload is the JCS serialization from Section 3, carried
*attached* 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
Sig_structure, so a detached receipt is not self-contained, and
chain.hash -- a digest over the receipt with chain.hash and sig.value
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
chain.hash; 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 Section 12.)
A receipt in this profile carries *two independent signatures*, and
conflating them is the failure this subsection exists to prevent.
1. The *native receipt signature*, sig.value, is computed over a
domain-separated preimage, fixed byte-exactly by the construction
below.
2. The *outer COSE_Sign1 signature* covers the RFC 9052
Sig_structure over the protected header and the payload -- and
the payload is the whole receipt _including_ its native sig
member. The external_aad field of that structure is the *empty
byte string* in this profile; a verifier MUST use a zero-length
external_aad 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.
The native construction, in full:
Toraman Expires 16 February 2027 [Page 27]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
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)))
|| denotes octet-string concatenation, and base64 is the standard
alphabet with padding ([RFC4648], Section 4). Two encoding rules
close variants that would otherwise let one signature be written two
ways: sig.value 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 [RFC4648], 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 S 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 sig.value is excluded from the hash input (Section 4.2): a
permissive decoder would let two byte-different receipts share one
chain hash. The digest that is signed and the digest that appears in
chain.hash 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 (Section 5.2) is distinct from it.
*The native algorithm label is checked before any cryptography.*
sig.alg MUST be the exact lowercase string ed25519. 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.
*The verification equation is pinned.* A verifier MUST check the
equation [S]B = R + [k]A' -- the cofactorless check that [RFC8032],
Section 5.1.7 names as "sufficient, but not required" beside the
cofactored [8][S]B = [8]R + [8][k]A'. Both are permitted by RFC 8032
Toraman Expires 16 February 2027 [Page 28]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
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 _validity itself_ 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. Section 15 records
that the working group may revisit the choice; what it may not do is
leave it unmade.
Determinism is a product of this equation together with the encoding
rules around it -- the canonical S above, the canonical base64 that
carries the signature, and the public-key rules below -- and not of
the equation alone. Section 12 reports which shipped verifiers
satisfy the pinned equation and which do not.
*The verification key itself.* 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:
* The resolved key MUST be an Ed25519 key ([RFC8032], 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 sig.alg is the same algorithm-confusion defect Section 4.1
closes on the envelope path.
* 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 2^255
- 19. A public key that is one of the eight canonical small-order
Ed25519 point encodings MUST also be rejected. Both rules are
about the *key*, 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.
* 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 (Section 1.5), 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.
Toraman Expires 16 February 2027 [Page 29]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
The relationship between the two signatures is normative:
* 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. *A Receipt Verifier (Section 1.4)
given an enveloped receipt MUST run both*, 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.
* The two signatures MAY be produced by different keys. Where they
are, the COSE kid identifies the party that emitted _this
envelope_ and sig.kid identifies the party that signed _this
receipt into its chain_. A relying party MUST NOT collapse the two
identities, and neither may a verifier: they are two claims about
two different parties, and Section 6 says what each is checked
against rather than letting one stand for both. A verifier MUST
report every identifier it reports *as an identity*, saying which
claim each belongs to -- always the native sig.kid, and the outer
kid when it came from the protected bucket. An outer kid taken
from the unprotected bucket resolved a key and named nobody
(Section 4.1), and MUST NOT be reported as the emitter's identity.
* *chain.hash MUST be recomputed, and a mismatch MUST be rejected.*
A Receipt Verifier MUST compute "sha256:" followed by the
lowercase hex of SHA-256 over HASH-INPUT -- the same input the
native signature covers -- and compare it to the receipt's
chain.hash. If they differ the receipt MUST be rejected as
tampered. This check is not optional and is not implied by either
signature: HASH-INPUT removes chain.hash, so sig.value does not
cover it, and the outer COSE signature that does cover it may have
been applied by a different party (Section 4.2). Without this
recomputation a receipt whose chain.hash is an arbitrary value
passes every other Receipt-Verifier check, and every artifact that
references a receipt by hash (Section 8.4) inherits the forgery.
* *The two checks consume different inputs, and the difference is
normative.* The outer COSE signature is verified over the payload
*bytes exactly as received*: a verifier MUST NOT parse, re-
serialize, re-order or normalize them first. The native signature
and chain.hash are verified over a *derived* input: the payload is
parsed, the two named members are omitted, the result is re-
serialized under Section 3.2, and that string is hashed. A
verifier MUST run both and MUST NOT substitute one input for the
other.
* *The canonicality check is over the whole payload, not over the
derived input.* A verifier MUST parse the received payload bytes,
re-serialize the _complete_ parsed object under Section 3.2, and
require the result to equal the received bytes: JCS(parse(bytes))
Toraman Expires 16 February 2027 [Page 30]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
== bytes. 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.
4.3. Carrying a receipt as an RFC 9943 Signed Statement
_The mapping binds the *Producer*. The evaluation rule at the end
binds whichever actor evaluates a receipt as a Signed Statement, and
names it there._
A COSE_Sign1 built to Section 4.1 alone is a valid signed object, but
it is *not* by itself a Signed Statement in the sense of [RFC9943].
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 kid MUST be present in the protected header whenever neither x5t
nor x5chain 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 "a
signed receipt" and "a registrable Signed Statement" is a checkable
property rather than a hopeful sentence.
To be carried as a Signed Statement under this profile, a receipt
MUST additionally have:
+==============+===================================================+
| Requirement | Value for this profile |
+==============+===================================================+
| CWT_Claims | REQUIRED ([RFC9943], Section 6) |
| header | |
| parameter | |
| (label 15) | |
| in the | |
| *protected* | |
| header, | |
| encoded as | |
| specified in | |
| [RFC9597], | |
| Section 2 | |
+--------------+---------------------------------------------------+
| iss (CWT | the Issuer of the Signed Statement: the authority |
| claim 1) | on whose behalf the receipt is emitted. It is a |
| | signer-asserted string until something binds it |
| | to the verification key -- Section 7 requires a |
Toraman Expires 16 February 2027 [Page 31]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
| | Transparency Service to make that binding and |
| | states that this profile defines no mechanism for |
| | it. This profile does not require iss 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 |
+--------------+---------------------------------------------------+
| iss when x5t | additionally MUST be a string conforming to the |
| or x5chain | URI syntax, of length 1 to 8192 ([RFC9943], |
| is in the | Section 6) |
| protected | |
| header | |
+--------------+---------------------------------------------------+
| sub (CWT | the scope.chain identifier, so one chain is one |
| claim 2) | subject. scope.chain carries no namespace of its |
| | own (Section 3.1), so two issuers can choose the |
| | same string: a Transparency Service MUST scope |
| | subjects by issuer rather than treat sub as |
| | globally unique, and a chain carrying several |
| | agents remains one subject |
+--------------+---------------------------------------------------+
| kid in the | REQUIRED. [RFC9943], Section 6 makes this |
| *protected* | conditional on the absence of x5t/x5chain; this |
| header | profile requires a resolvable kid unconditionally |
| | (Section 4.1) and requires it protected for |
| | registration (Section 7), so the condition never |
| | relaxes it here |
+--------------+---------------------------------------------------+
Table 3
The CWT_Claims header parameter MUST occur exactly once across the
two header buckets, as [RFC9597], 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.
*These claims are necessary, and this profile does not claim they are
sufficient.* [RFC9943] describes a Statement as carrying a media type
that tells a Transparency Service what it is holding, and this
revision registers none (Section 16) while leaving the protected
content type a SHOULD. So a receipt that carries iss and sub 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 Section 15, not a
further claim here.
Toraman Expires 16 February 2027 [Page 32]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
A verifier or Transparency Service evaluating a receipt *as a Signed
Statement* MUST reject one that lacks protected CWT_Claims carrying
both iss and sub, per [RFC9943], 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.
A receipt carried as a Signed Statement SHOULD also carry a protected
content type (label 3) describing the payload. This revision
registers no media type (Section 16), 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 *Deployment*
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.
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; Section 12 states it as
a named conformance gap rather than leaving a reader to discover it
at registration time.
5. Hash-chaining and completeness
_This section's number, title and anchor are unchanged from -00, and
the construction it defines is unchanged. *Two sentences of -00's
paragraph were factually wrong and are corrected here* rather than
reproduced and contradicted twenty lines later. Appendix A states
the before and after, for the benefit of a document that adopts this
section by reference (Section 1.2)._
Toraman Expires 16 February 2027 [Page 33]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
Each receipt carries a monotonic chain.seq (genesis = 0) and
prevHash. A Chain Verifier checking a presented chain MUST verify:
each prevHash equals the prior receipt's chain.hash; the genesis
receipt has prevHash == null; and, when a signed checkpoint is
present *and an identity manifest is supplied*, that the checkpoint
was signed by one of the keys that manifest authorizes for the
genesis receipt's agent.id, so that a foreign checkpoint produced by
a re-heading attacker is rejected -- without a manifest that binding
does not hold (Section 5.2). This profile detects in-band tampering
_within_ a presented chain, and detects tail truncation *only against
an authenticated checkpoint* (Section 5.1), never from the presented
set alone; it does NOT, by itself, detect *equivocation* (an issuer
signing two divergent chains) -- that requires registration in a
SCITT Transparency Service or an equivalent external witness.
5.1. Chain-level failure axes
_Section 5 and this subsection address the *Chain Verifier*
(Section 1.4): a Receipt Verifier is given no set, cannot evaluate
these conditions, and does not fail them._
_This subsection is NEW in -01. Section 5 keeps its number, title
and anchor because another profile adopts it by reference
(Section 1.2); two of its sentences are corrected in this revision
and Appendix A states which; everything below adds Chain-Verifier-
class requirements that -00 did not state, and Appendix A lists each
of them._
The checks above are properties of a receipt SET, not of any receipt.
*Six conditions* 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 "invalid".
A checkpoint is deliberately *not* 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 Section 8.3's rule applied to this
section: a finding about one object is never a finding about another.
Toraman Expires 16 February 2027 [Page 34]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
+==========================+=======================================+
| Axis | Failure |
+==========================+=======================================+
| chain-link-broken | a prevHash does not equal the prior |
| | receipt's chain.hash; a non-genesis |
| | receipt carries prevHash == null; or |
| | the genesis receipt carries a |
| | prevHash that is not null (Section 5 |
| | requires it to be) |
+--------------------------+---------------------------------------+
| chain-sequence-duplicate | two receipts in one scope.chain carry |
| | the same chain.seq |
+--------------------------+---------------------------------------+
| chain-sequence-gap | the presented chain.seq values are |
| | not contiguous, or do not begin at 0. |
| | A conforming Chain Verifier requires |
| | the complete chain from genesis; see |
| | below |
+--------------------------+---------------------------------------+
| chain-scope-mismatch | the presented set does not all carry |
| | one scope.chain. A Chain Verifier is |
| | handed a set _as presented_ and |
| | checks that it is one chain; it does |
| | not assume it. A supplied checkpoint |
| | that names a different chain is *not* |
| | this condition -- it is a finding |
| | about the checkpoint (Section 5.2) |
+--------------------------+---------------------------------------+
| chain-tenant-split | two present, unequal scope.tenant |
| | values appear within one scope.chain |
| | (Section 3.3) |
+--------------------------+---------------------------------------+
| chain-key-discontinuity | two receipts in the presented chain |
| | carry the same agent.id and different |
| | sig.kid |
+--------------------------+---------------------------------------+
Table 4
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.
Toraman Expires 16 February 2027 [Page 35]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
*The checkpoint axis, which is not a chain condition.* A checkpoint
answers one question: _has the tail of this chain been cut?_ 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:
* *answered* -- a checkpoint was supplied, was structurally valid,
named this chain, and authenticated. The verifier compared it to
the presented head. The comparison yields either _consistent_ --
the head matches, or the presented chain is a consistent extension
of it as described below -- or the failure checkpoint-head-
conflict: 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.
* *unanswered* -- no checkpoint was supplied. The verifier MUST
report the tail question as unanswered and MUST NOT report it as
an absence of truncation (Section 5.2).
* *unusable* -- a checkpoint was supplied and could not be used: it
was malformed, it named another chain, or it did not authenticate
(Section 5.2). The verifier MUST report _which_, MUST report the
tail question as unanswered, and MUST NOT convert any of it into a
finding about the receipts. checkpoint-unverified is this
document's identifier for the authentication case.
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
_answered_ and _unusable_ -- never whether a keyring happened to be
supplied. Section 12 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.
The distinction between the conditions is operational, not cosmetic:
chain-sequence-duplicate and chain-scope-mismatch 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 chain-link-broken
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.
Toraman Expires 16 February 2027 [Page 36]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
The six labels above are this document's identifiers for the six
_conditions_, and checkpoint-head-conflict and checkpoint-unverified
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 [I-D.hillier-scitt-arp];
that alignment was measured against this profile's own adversarial
corpus (see Section 12).
*Completeness is required, not optional.* A Chain Verifier MUST
reject a presentation that does not begin at seq == 0, and a
*Deployment* 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 0..n-1, though
only one of the five is an independent oracle for the others
(Section 12) -- and a presentation of, say, seq 1 and seq 2 is
refused as a missing genesis rather than accepted as a partial view.
Verification of a _range_ that does not start at genesis is therefore
not a capability of this profile: it is neither specified here nor
implemented anywhere, and Section 12 lists it as such. -00 said "a
verifier checking a range of receipts" and left the boundary of a
"range" 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.
*What a checkpoint comparison can and cannot show.* A mismatch
between the presented head and a checkpoint's headHash can reveal
truncation _relative to that checkpoint_ -- 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
(Section 13). 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;
Section 7 states exactly what it is worth to an offline holder.
*An older checkpoint over a longer chain is not truncation.* A
checkpoint asserts a head at the moment it was minted; a chain that
has grown since then legitimately presents a _later_ head. A
verifier MUST therefore distinguish two mismatches. Where the
checkpoint's headHash appears in the presented chain at exactly its
highestSeq, and the presented head's chain.seq is greater, the
presented chain *extends* the checkpoint consistently: this is not
Toraman Expires 16 February 2027 [Page 37]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
checkpoint-head-conflict, 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 checkpoint-head-conflict: 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 Section 8.3 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 Section 12.
*Why the unauthenticated case turns on authentication, and not on
inputs.* The rule above -- that a checkpoint the verifier did not
authenticate yields checkpoint-unverified 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
checkpoint-unverified is the indeterminate result that names the
reason instead. Section 8.3 states the general form of the rule,
over every input rather than this one; Section 12 measures two places
where a shipped verifier breaks it.
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 Section 13 states what an authenticated one is
and is not worth.
*One agent, one key, within one presented chain.* A Chain Verifier
MUST reject a presented chain in which two receipts share an agent.id
and differ in sig.kid, reporting chain-key-discontinuity. The
condition needs no keyring: it compares the receipts to one another.
This is the condition that catches the ordinary form of chain
extension, and what it catches is narrower than "extension is
closed". Consider a party that holds any key the verifier trusts and
appends receipts to another party's chain under the victim's own
agent.id: the links are intact, there is no sequence gap, the
Toraman Expires 16 February 2027 [Page 38]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
scope.chain 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 sig.kid while claiming the same
agent.
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:
* *It fires within one agent.id, and chains may carry several.* An
attacker holding another trusted key can append receipts under a
_new_ agent.id 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 _which agents may appear in a
chain_ -- an append-authority binding this profile does not define
(Section 1.5). The identity manifest binds an agent.id 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.
* *The comparison is on identifiers, and that is what it detects.*
The condition compares sig.kid 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 _different_ keys presented under one kid 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 -- Section 7 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.
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 Section 5.2: key
rotation is not expressible inside a chain, and a deployment that
rotates a signing key starts a new one.
*Sequence orders a chain; timestamps do not.* chain.seq and prevHash
are the only ordering this profile defines. ts is signer-chosen
(Section 3.3) 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 ts is using a field no check constrains. Where a
deployment needs temporal ordering it MUST obtain it from something
other than these records.
Toraman Expires 16 February 2027 [Page 39]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
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 (Section 1.5) -- only that the
conditions be individually recoverable from it.
*A condition that holds is not a note; it is a failure.* 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 chain.seq 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 *not*
conformant here. Whether a _future_ revision should permit it is an
open question (Section 15); this one answers it in the strict
direction so that two verifiers cannot disagree about the same bytes
today.
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
Section 13).
5.2. Checkpoint structure
_The structure and signing rules address the *Producer* of a
checkpoint; the authentication and scoping rules address the *Chain
Verifier*, and apply only when a checkpoint is supplied._
A _checkpoint_ 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 Section 5.1 compares against.
{
"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>" }
}
Toraman Expires 16 February 2027 [Page 40]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
Its signature mirrors the receipt construction of Section 4.2, and is
pinned here to the same byte-exactness:
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"
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.
*The object is closed, and a Chain Verifier MUST validate its
structure before authenticating it.* A checkpoint MUST carry exactly
the members shown above and no others, at either level, and sig MUST
carry exactly alg, kid and value. 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: CHECKPOINT-HASH-INPUT
copies every member of the object except sig.value, so a smuggled
member sits *inside* 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:
* spec MUST be the exact string noa.checkpoint/0.1;
* chain MUST be a non-empty string, and a Chain Verifier MUST
compare it to the scope.chain of the presented set. A checkpoint
naming another chain is not evidence about this one, and the
finding is *about the checkpoint*: 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
(Section 5.1), for the reason Section 8.3 gives: nothing about the
presented receipts failed;
* highestSeq MUST be an integer, at least 0, and within the range
Section 3.2 fixes for numbers;
* headHash MUST be "sha256:" followed by 64 lowercase hexadecimal
digits;
Toraman Expires 16 February 2027 [Page 41]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
* ts MUST be a valid RFC 3339 date-time, under the same rule and for
the same reason as a receipt's (Section 3.3);
* sig.alg MUST be the exact string ed25519; sig.kid and sig.value
MUST be non-empty strings; and sig.value MUST satisfy the encoding
rules of Section 4.2.
A checkpoint failing any of these value rules is malformed, and so is
one whose chain does not match. Every such failure is a finding
*about the checkpoint*: it MUST NOT establish checkpoint-head-
conflict, 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 (Section 5.1) -- the same outcome as
being given no checkpoint at all, because that is what it effectively
has.
*Which key may sign it.* 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
_unauthenticated_ checkpoint rather than an absent one: it MUST
report every checkpoint-derived conclusion as unauthenticated
(Section 5.1), and MUST NOT fall back to comparing heads. Key-level
authentication alone, however, only establishes that _some_ trusted
key asserted this head. To bind the assertion to the chain, a Chain
Verifier _that was supplied an identity manifest_ (Section 6) MUST
require the checkpoint's sig.kid to be one of the keys that manifest
authorizes for the agent.id of the *genesis receipt* -- the receipt
at seq == 0, the chain opener. A checkpoint signed by any other
trusted key MUST be rejected as UNTRUSTED -- the result defined in
Section 6 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.
Two scoping rules follow, and both are limits rather than features.
The comparison is between two *native* key identifiers -- the
checkpoint's own sig.kid, and the identifiers the manifest authorizes
for the genesis receipt's agent.id -- 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 kid is the one the
manifest authorizes here. And the guarantee is opener-scoped: on a
chain carrying more than one agent.id, the opener's checkpoint
certifies the opener's view of the head and does not certify a co-
Toraman Expires 16 February 2027 [Page 42]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
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.
*What a checkpoint does not commit to, and what follows.* 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:
* *Freshness is not self-evident.* ts 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.
* *The guarantee is opener-scoped, and a co-agent's tail is not
covered.* On a chain carrying more than one agent.id, a checkpoint
authorized for the opener says nothing about receipts signed by a
co-agent -- the requirement for that case is stated once, above.
* *Key identifiers are pinned within a presented chain.* The
reference implementation binds each agent.id to the first sig.kid
it presents and rejects any later change as a key swap
(Section 12). 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.
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 Section 8.3;
a Chain Verifier that uses it is reading a definition, not inheriting
an Evidence-Verifier obligation. Those are different answers, which
is what Section 8.3 exists to keep apart.
6. Identity binding
_Attribution addresses the *Receipt Verifier*, conditional on an
identity manifest being supplied. The relationship rules of
Section 6.1 address the *Evidence Verifier* and depend on artifacts
listed in Section 1.5._
Toraman Expires 16 February 2027 [Page 43]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
agent.id 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 agent.id to its authorized key
identifiers.
*Each signature attributes a different party, and the manifest
answers only one of the two questions.* An enveloped receipt carries
two identifiers, and treating either as a substitute for the other is
the failure this subsection exists to prevent:
* The *native* sig.kid attributes the *agent*: it is the key that
signed this receipt into its chain, and it is the identifier
agent.id makes a claim about. When a manifest is supplied, a
verifier MUST check the (agent.id, native sig.kid) pairing against
it, and MUST reject an unauthorized pairing as UNTRUSTED -- a
result distinct from both "valid" and "tampered": the bytes are
intact and the signature verifies, and the key that produced it is
not one this agent.id 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 Section 5.1 separates its
conditions.
* The *outer* COSE kid attributes the *party that emitted this
envelope* -- 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.
* Therefore two rules, and they are the ones that close the
laundering. A verifier MUST NOT use an authorized outer kid to
satisfy the agent check: an envelope signed by a key that happens
to be authorized for agent.id says nothing about who signed the
receipt inside it. And a verifier MUST NOT 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.
A checkpoint has one signature and one claim: the manifest check is
against the checkpoint's own sig.kid, under the opener-binding rule
of Section 5.2.
Without a manifest, attribution is key-level only -- "some key the
verifier trusts signed this" and nothing about which agent -- and a
verifier MUST surface that limitation to the relying party.
Toraman Expires 16 February 2027 [Page 44]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
6.1. Who signed is not who approved
governance.approval.by names an approver. The receipt's signature
establishes that the _issuer_ 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 governance.approval as pre-execution authorization by the
named party. Section 9 states what would be required instead.
*Two levels, and the difference between them is the whole answer.*
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.
* *A bare receipt, checked by a receipt verifier.*
governance.approval.by 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 _at this layer_: 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.
* *A complete evidence presentation.* Where a deployment also
produces the approval artifacts of Section 9, 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 _and_ the
verdict receipt, and a verifier can require all three to be the
same key. What becomes checkable at this level is narrower than
"a human decided": it is that _a key enrolled as an approver
signed a decision bound to this action_, 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.
* *An issuing gate MAY close the gap at emission time, and this
profile recommends it.* Nothing prevents the party that mints a
receipt from requiring governance.approval.by 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
_issuer knows_, not what a later reader can check: the receipt
still carries an opaque string, and no member records that the
gate ran (Section 8.1). 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 Section 9
travel with it. The reference implementation's gate and grant
signer both enforce the control (Section 12).
Toraman Expires 16 February 2027 [Page 45]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
*A Receipt Verifier or Chain Verifier that reports anything about an
approval MUST state that its result rests on the opaque name alone.*
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 Section 9 -- 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 approval field be read as
the second. *Neither level establishes that a named human physically
held the signing device*: 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.
Section 12 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.
*The rest of this subsection is NON-NORMATIVE in this revision.* It
runs over the signed outcome artifacts and the trust configuration
Section 1.5 lists as undefined here, and over a "request signer" 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 Section 15 records the work of turning it into
requirements somewhere it can be tested.
Where an outcome about the action is _itself_ signed, independence is
decided by comparing the *outcome signer* against *two* other
parties, because a deployment that separates them has two distinct
keys and collapsing them hides a real dependency:
1. the *request signer* -- 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
2. the *authorization signer* -- the key that approved it (the
enrolled approver device key that signs the decision artifact and
the verdict receipt).
A verifier has to derive and report *each* 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 _emitted_ the receipt answers a third question
and is not a substitute for either. Both relationships have to be
Toraman Expires 16 February 2027 [Page 46]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
derived by the verifier from its own trust configuration. A self-
declared relationship -- a producer writing "independent" about
itself -- is not evidence of independence, and an undetermined
relationship cannot be upgraded to an independent one.
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 (Section 1.5), 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:
+=================================+============================+
| Relationship | Meaning |
+=================================+============================+
| SAME_SIGNING_KEY | one key signed both |
+---------------------------------+----------------------------+
| SAME_ADMINISTRATIVE_PARTY | distinct keys under one |
| | administrative registry |
+---------------------------------+----------------------------+
| SEPARATE_ROLE_SAME_ORGANIZATION | distinct authorized |
| | purposes, one organization |
+---------------------------------+----------------------------+
| INDEPENDENT_ORGANIZATION | distinct organizations, |
| | established from the |
| | configuration |
+---------------------------------+----------------------------+
| UNKNOWN | the configuration does not |
| | establish a relationship |
+---------------------------------+----------------------------+
Table 5
Two rules make this testable rather than decorative.
Toraman Expires 16 February 2027 [Page 47]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
* *A verifier should not offer a value its configuration cannot
derive.* 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
INDEPENDENT_ORGANIZATION from what it can return, rather than
returning it on weak evidence.
* *SAME_SIGNING_KEY is decided on key material, not on a key
identifier.* 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.
UNKNOWN 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 (Section 8.2), no result other than
INDEPENDENT_ORGANIZATION 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. Section 12 reports a
shipped instance of this determination, including the value it
deliberately does not offer.
7. Registration in a Transparency Service
_This section addresses the *Transparency Service*, except the first
sentence, which addresses the submitting *Deployment*._
A receipt carrying the protected claims of Section 4.3 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.
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:
1. MUST verify the outer COSE_Sign1 signature, and MUST verify it
*with a key it has bound to the Issuer named in iss* -- the
resolved key or certificate belongs to that Issuer, or an
explicit delegation from that Issuer to the signing key is
presented and checked. [RFC9943], Section 6.3 states the same
requirement in its own terms: the signature is verified with the
verification key _of the Issuer_. A service that cannot make that
binding MUST NOT record the statement as that Issuer's;
2. MUST verify the protected header conforms to Section 4.1 -- an
alg of -19 and deterministic CBOR -- and MUST require a
*protected* kid for registration specifically. A receipt whose
kid is only in the unprotected header conforms to Section 4.1 and
is not registrable under this policy: registration binds a
Toraman Expires 16 February 2027 [Page 48]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
subject to an issuer, and an identifier the signature does not
cover cannot do that. Producers intending to register MUST
protect the kid;
3. MUST verify that protected CWT_Claims are present and carry both
iss and sub;
4. MUST verify that the payload is a well-formed receipt under
Section 3; and, *where it can resolve the native signer's key*,
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 Section 4.2
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 (Section 2) -- so where the service cannot
resolve it, *it MUST refuse the registration*. 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;
5. MUST verify that sub equals the payload's scope.chain, so that
the subject a log indexes is the chain the statement is actually
about; and
6. *The issuer/submitter relationship is the sixth item, and it is
guidance rather than a requirement*, because this document cannot
make it testable: a Transparency Service applies its own policy
to the relationship between the Issuer named in iss 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.
Toraman Expires 16 February 2027 [Page 49]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
*What iss is worth, and where it stops.* This profile defines no
issuer key discovery: no registry, no well-known location, no
delegation format. Item 1 therefore states a requirement whose
_mechanism_ 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 -- iss is an *issuer-asserted string*:
it names the authority the signer claims to speak for, and nothing in
the envelope proves the relationship. A *Transparency Service*, and
any *Receipt Verifier* that reads iss 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.
A Transparency Service knows this policy applies by agreement with
the registrant, not by inspection: no media type is registered for
the payload (Section 16), so a service cannot dispatch on content
type alone. Until one exists, a deployment states the profile out of
band.
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 "not checked".
Without item 5 an issuer can register a chain-A receipt under sub =
chain-B, 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 [RFC9942]'s terms that is a proof about the log's verifiable data
structure, not the COSE countersignature header parameter of
[RFC9052]; the two are different constructs, and this profile means
the first everywhere it says Transparency Receipt.
*What registration is worth, stated exactly.* An inclusion proof
establishes membership in _the view that log served_. 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.
*The offline case gets the same scepticism this document applies to
checkpoints.* A Transparency Receipt handed to an offline relying
party _by the prover_ is prover-supplied evidence, exactly like the
Toraman Expires 16 February 2027 [Page 50]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
checkpoint of Section 5.2: a better signer, and still no way for a
party that never queries the log to detect a log that served two
views.
Two further honesty notes. Nothing in Section 5 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
(Section 12): the policy above is a specification, not a report of an
executed interop.
8. What a receipt does not claim about the external world
_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 Section 8.1 and the intent machinery of Section 8.2 -- because
their inputs are artifacts Section 1.5 lists as undefined; the
absence vocabulary of Section 8.3 is normative, because a verifier's
own results are something this document can constrain._
A receipt in this profile is emitted at the _governed boundary_: 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.
8.1. Three claims, three independent failures
The following are distinct claims. They may share one action digest
as a join key (Section 10), 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 (Section 1.5):
1. *Pre-execution authorization* -- a named, accountable principal
approved _this exact action_ before it ran.
2. *Controller-reported outcome* -- an identified downstream system
reported a terminal status for the request, under its own
semantics.
3. *Physical completion* -- an identified witness observed the
intended effect, against a criterion fixed before execution.
A receipt under this profile carries NONE of claims 2 and 3, and
carries claim 1 only as the signer-asserted label described in
Section 6.1. A deployment MAY apply the producer-side control
Section 6.1 recommends -- refusing to issue unless
governance.approval.by names the key that signed the approval -- but
Toraman Expires 16 February 2027 [Page 51]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
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, *scoped to what a
receipt establishes*. A *Producer* 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 *on that basis*, and where a consumer asked
for one it MUST report that the receipt does not establish it. For
claims 2 and 3 Section 8.2 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
Section 8.2. 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 Section 8.2 describes.
In particular, governance.verdict == EXECUTED 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 "succeeded" attests success under its own semantics; that
is a report, and a report is not an effect.
8.2. Validity and sufficiency are separate results
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:
* *artifact validity* -- signature, canonical form, chain position,
identity binding;
* *sufficiency for the consumer's intent* -- whether what was
presented answers the question asked.
*One of the four intents is normative here and three are not*, and
the line is drawn by what this document defines. A verifier that
supports intents at all MUST answer BASE_ACTION_RECEIPT from receipt
validity alone, and MUST NOT report it as satisfied for an object
that fails Section 3 or Section 4: 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 Section 1.5 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. *Those three rows and the paragraphs that
follow the table are NON-NORMATIVE in this revision*, for that reason
-- with one exception, labelled where it appears at the end of this
Toraman Expires 16 February 2027 [Page 52]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
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:
+=====================+=========================================+
| Intent | Satisfied only by |
+=====================+=========================================+
| BASE_ACTION_RECEIPT | a receipt valid under Section 3 and |
| | Section 4 |
+---------------------+-----------------------------------------+
| CONTROLLER_OUTCOME | an authenticated controller-outcome |
| | artifact bound to this action. |
| | Section 8.4 describes what such an |
| | artifact must say; *this revision |
| | defines no wire format for it* |
| | (Section 1.5) |
+---------------------+-----------------------------------------+
| PHYSICAL_COMPLETION | an authenticated physical-observation |
| | artifact bound to this action, from a |
| | witness the verifier trusts for that |
| | purpose. Same status: described in |
| | Section 8.4, *not defined here* |
+---------------------+-----------------------------------------+
| INDEPENDENT_OUTCOME | one of the two artifact-based intents |
| | above -- CONTROLLER_OUTCOME or |
| | PHYSICAL_COMPLETION -- whose signer the |
| | verifier derives as |
| | INDEPENDENT_ORGANIZATION (Section 6.1). |
| | A receipt cannot satisfy it: the |
| | receipt's issuer is the party whose |
| | account is in question, so it is never |
| | independent of itself |
+---------------------+-----------------------------------------+
Table 6
Only the first is satisfiable from a receipt alone. The other three
need artifacts Section 1.5 lists as undefined in this revision, and
INDEPENDENT_OUTCOME is additionally unsatisfiable by any verifier
that cannot derive organizational independence -- which, per
Section 12, 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.
A verifier that supports intents should support at least these four
and report a _named_ 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
Toraman Expires 16 February 2027 [Page 53]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
"invalid" 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 (Section 1.5).
*One rule here is normative, because it constrains what may be said
about artifacts this document does define.* A verifier MUST NOT
report an unqualified "physical completion proven". The strongest
defensible result is proven _to the stated policy_, naming the trust
configuration that made it so, and a receipt never supplies it
(Section 8.1).
8.3. Absence is not a negative claim
A verifier MUST keep the following distinct, and MUST NOT collapse
them:
+================+==========================================+
| Result | Meaning |
+================+==========================================+
| NOT_CLAIMED | the producer made no claim on this point |
+----------------+------------------------------------------+
| NOT_PROVIDED | a claim may exist, but the verifier was |
| | not given acceptable evidence of it |
+----------------+------------------------------------------+
| NOT_APPLICABLE | the point does not arise for this action |
+----------------+------------------------------------------+
| INDETERMINATE | evidence was presented but does not |
| | settle the question |
+----------------+------------------------------------------+
Table 7
"The verifier did not receive acceptable evidence" is not "no
evidence exists", 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 Section 5.1 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;
Section 12 records a shipped verifier that carries no such code by
design.
Toraman Expires 16 February 2027 [Page 54]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
*A finding names the object that failed, and no other.* 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:
*a failure to authenticate or to understand one object MUST NOT
produce a finding about a different object.* The subject of the
finding is the object that failed.
The rule is about _whose_ failure is being reported, not about
whether failures may be reported at all. A receipt whose own
signature does not verify is TAMPERED -- 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
checkpoint-unverified, a finding about the checkpoint, and never a
finding about the chain (Section 5.1); a checkpoint whose structure
or scope is wrong is a finding about the checkpoint (Section 5.2); 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.
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 Section 12 records two measured places where a shipped
implementation does exactly that.
8.4. Why the base record is not extended
A machine-checkable non-claim carried as a field of the base receipt
-- for example a physical_completion member defaulting to "none" --
was proposed on the SCITT list and is *not* adopted here, for two
reasons that are worth stating rather than asserting.
First, mechanics: adding a member to noa.receipt/0.1 changes the JCS
bytes of every receipt that carries it, and therefore the receipt
hash, the native signature, the prevHash 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.
Second, semantics: a default value on a frozen record cannot
distinguish "the producer considered physical completion and claims
nothing" from "the producer has never heard of the question". A
default is not a statement.
Toraman Expires 16 February 2027 [Page 55]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
The claims of Section 8.1 therefore belong in *separate signed
artifacts*, 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
(Section 10) -- 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.
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.
9. Pre-execution authorization and post-execution evidence
_This section is addressed to the *Evidence Verifier* (Section 1.4),
over artifacts this profile does not define (Section 1.5), and is
*NON-NORMATIVE in its entirety*: 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 --
Section 8.1 states the non-claims a receipt cannot escape, and
Section 8 states what a bare receipt cannot establish. A Receipt or
Chain Verifier neither satisfies nor fails anything in this section._
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
_this exact action_. 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.
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 Section 1.5, 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: ts
cannot supply the ordering, because it is signer-chosen
(Section 3.3). They are written as what a companion defining those
artifacts has to require.
Toraman Expires 16 February 2027 [Page 56]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
* A verifier presented with a *post-execution record* where a *pre-
execution authorization* 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.
* A *machine policy decision* is not a human authorization. A
receipt carrying governance.verdict == ALLOWED 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: "a named human authorized
it" 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 (Section 1.5). The requirement that does exist is the non-
claim, and it lives where its input is a receipt: Section 8.1
forbids presenting a receipt as evidence that a named human
authorized the action. Per Section 8.3, an unproven result is
also not a finding that no human approved.
* An authorization is bound to *one execution attempt* 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 _this_ 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.
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. Section 12
reports what the reference implementation does today, so that the gap
between this profile and that implementation is a measurement rather
than an impression.
10. The shared action digest boundary
_The first rule below binds the *Producer* that emits a digest -- it
must travel with a construction identifier -- and every actor that
compares one, in practice an *Evidence Verifier*. The second rule
binds whoever reads the comparison._
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.
Toraman Expires 16 February 2027 [Page 57]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
*Determinability, not assumption.* *This rule -- the one paragraph
below, and nothing else in this section -- is NON-NORMATIVE here*:
the digest it governs is not a member of this record and is defined
by a companion (Section 1.5), 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 *indeterminate*: "these records were
not shown to be about the same action", never "these records are
about different actions". That is the same distinction Section 8.3
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.
*Correlation is not attestation.* Digest equality establishes that
two records commit to the same _action projection_ -- 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 (Section 8.1). A digest is a join key, and a join key
that is mistaken for an authorization claim is the substitution
Section 9 forbids.
*Two rules do stay normative here, because their subject is this
record.* action.paramsHash does not satisfy the first rule above and
MUST NOT be used as a join key (Section 3.2); and a verifier MUST NOT
treat equality of any digest as evidence that an action was
authorized, which is the substitution Section 9 is about. Nor does
the base record carry a member for a shared digest: noa.receipt/0.1
is closed (Section 3), so a correlation value travels _beside_ a
receipt in whatever artifact references it, never inside one. A
deployment that wants correlation does not modify the receipt.
A construction meeting both rules is published:
[I-D.toraman-noa-action-digest] fixes the projection members, the
domain tag, the preimage, the digest, and a published conformance
corpus (counted in Section 12). One consumer of it is
[I-D.toraman-noa-settlement-evidence]. This profile does not restate
that construction, does not require it, and would apply the same two
rules to any other.
Toraman Expires 16 February 2027 [Page 58]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
*On indeterminate positive controls.* In an independent
reconciliation run reported on the SCITT list on 27 July 2026
(https://mailarchive.ietf.org/arch/msg/scitt/zWZBQXXSGV-
vgbTeOLTjlb9DGLg/), a _positive_ 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.
Three things follow, and they are different things.
* *What is now pinned.* 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 (Section 12).
* *What that does not do.* 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.
* *What is still open on our side, for a digest we did not define.*
A party computing the reconciliation layer's own subject digest
over a receipt's action object cannot do so reproducibly today,
because this profile has never fixed that object's encoding:
rollbackRef 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 action 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.
11. Policy replay is out of scope
_This section addresses the *Producer* and every verifier: none of
them may present a receipt as carrying a re-derivable verdict._
A deterministic, offline *policy-replay* 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 Section 3.3 carries hashes of a
policy, its read-set, and the recorded inputs; it does not carry the
replay _construction_ (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 *Producer*
Toraman Expires 16 February 2027 [Page 59]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
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.
12. Implementation status
_This section is to be removed by the RFC Editor before publication,
per [RFC7942]. 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._
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
https://github.com/NordenSoft/noa. Unless explicitly stated
otherwise, the measurements below are from main commit 4764cab.
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 vectors.json whose
entry array is counted, which is why counting files there gives the
wrong answer), the conformance run (node impl-py/conformance.mjs),
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.
*Algorithm identifier.* The implementation emits the fully-specified
Ed25519 identifier -19 ([RFC9864]) and rejects every other alg,
including the polymorphic EdDSA identifier -8, which [RFC9864],
Section 4.2.2 marks Deprecated (it was registered in [RFC9053]).
There is one emission site and one verification gate; no code path
emits -8 and no legacy-compatibility flag exists.
The protected header it builds is the deterministic CBOR map {1: -19,
4: kid} -- two members, because the kid is in the _signed_ bucket, so
attribution cannot be swapped without breaking the signature. Its
bytes therefore begin a2 01 32 04 ..., the trailing part being the
kid byte string; a measured envelope's header reads a2 01 32 04 49
... for a nine-octet kid. A forged envelope carrying -8 under a
Toraman Expires 16 February 2027 [Page 60]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
genuine Ed25519 signature -- the only construction that tests the alg
pin rather than the signature -- is rejected at the product API with
"protected header alg is not Ed25519 (-19, RFC 9864)"; so are -7, alg
as a text string, a non-canonical two-byte encoding of -19, a
duplicated alg label, and an absent alg.
*One implementation, stated as such.* The -19/-8 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 (cbor2 + PyCA cryptography) reads alg = -19 from a shipped
vector, verifies the Ed25519 signature over the RFC 9052
Sig_structure, and rejects a tampered payload -- but that corpus
holds one vector and it is a _positive_ one. At the pinned snapshot
that vector's own metadata still carried the stale a10132 header
string beside an envelope whose protected header holds both alg and
kid, so the file contradicted itself; df777a6 corrected it. There is
no cross-implementation NEGATIVE vector for -8; that refusal is
proven only inside this implementation's own tests, and closing it is
one of the open vector commitments recorded below.
*The two signatures.* receiptToCose wraps the JCS-canonical receipt
as the COSE_Sign1 payload; receiptFromCose verifies the COSE
signature, re-canonicalizes the payload and requires byte equality
with the signed bytes, and validates the receipt shape -- it does
*not* verify the native sig.value or any chain property, which is the
separate verifyChain path. The two checks are genuinely independent,
which is what Section 4.2 says they are; what no entry point does is
run _both_, recorded as a gap below.
*Chain-level axes.* All six chain conditions of Section 5.1 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 "key swap", in
both the TypeScript and the separately implemented Python verifier.
What it gets wrong is the *checkpoint axis*, on both of its named
outcomes: checkpoint-unverified is not distinguished at all, and
checkpoint-head-conflict is reported over-broadly by treating a
legitimate extension as one. Both are in the gap list below.
scope.tenant drift across one chain is fail-closed by default, and
absence of the member does not reset the comparison.
*On the word "separately".* 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
Toraman Expires 16 February 2027 [Page 61]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
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.
*Cross-implementation conformance, run rather than cited.* node impl-
py/conformance.mjs, executed at the pinned 4764cab snapshot on
2026-08-14, ran *99 checks* and exited *0*, 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.
*Conformance gaps against this revision's own rules, at the pinned
snapshot.* 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. "All five" 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.
*One measurement covers the first three rows, and it was run rather
than reasoned.* Five receipts were built, each carrying
action.riskClass: CRITICAL on a wire.transfer action, and each was
presented as identical bytes to all five verifiers: (A)
agent.principal: SANDBOX_SIM with governance.sandboxed: false and
governance.verdict: EXECUTED; (B) governance.verdict: SIMULATED with
governance.sandboxed: false; (C) action.reversible: false with
action.rollbackRef: "snap_1"; (D) governance.verdict: ROLLED_BACK
with action.reversible: false; (E) ts: "2026-13-45T99:99:99.000Z".
*All 25 combinations returned VALID.*
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 CRITICAL, and they can be
argued in both directions afterwards -- the issuer can say the record
Toraman Expires 16 February 2027 [Page 62]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
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 paramsHash
(Section 3), 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.
* *G1 -- contradictory simulation markers are accepted.* Section 3.1
requires governance.sandboxed: true whenever agent.principal is
SANDBOX_SIM or governance.verdict is SIMULATED. 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.
* *G2 -- contradictory rollback markers are accepted.* Section 3.1
requires action.rollbackRef to be absent or null unless
action.reversible is true, and a ROLLED_BACK verdict to be
accompanied by action.reversible: true. Neither relationship is
checked by any shipped verifier; cases C and D verify in all five.
* *G3 -- a timestamp's shape is checked, its meaning is not.*
Section 3.3 requires a verifier to reject a value that is not a
valid RFC 3339 date-time. 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 *not*
a wire-format change. What it changes is the set of already-
signed receipts an existing verifier accepts, which the pinned
source calls "an API change, never a format change" -- 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.
* *G4 -- no entry point is a conforming Receipt Verifier.*
Section 4.2 requires a Receipt Verifier given an enveloped receipt
to run both signature checks and to report which produced its
result. receiptFromCose verifies the outer COSE signature, re-
canonicalizes the payload and validates its shape but never
touches sig.value; 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.
Toraman Expires 16 February 2027 [Page 63]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
* *G5 -- whole-payload canonicality is proven on one path only.*
Section 4.2 requires JCS(parse(bytes)) == bytes over the bytes as
received. receiptFromCose performs exactly that comparison; the
bare chain path in all five is handed parsed values and cannot
prove the original bytes were canonical.
* *G6 -- an unauthenticated checkpoint produces an accusation.*
Section 5.1 requires checkpoint-unverified and forbids a
truncation or tampering finding. With a keyring, all five answer
TAMPERED; the four that carry a reason string give "checkpoint not
authenticated against keyring" 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 TAMPERED in
all five -- measured, on identical bytes -- under the reference's
message "tail truncated/extended". Neither branch produces the
indeterminate result this revision requires, and both are the
violation of Section 8.3 in its clearest form: a file anyone can
hand the verifier becomes a finding against someone else's
records. It is also the shape Section 5.1 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.
* *G7 -- a legitimate extension is reported as a head failure.*
Section 5.1 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 -- "tail truncated/extended",
which also names a cause Section 5.1 no longer permits the
comparison to name; the Go verifier returns the status alone, so
there the two cases are indistinguishable even in principle.
* *G8 -- no Signed Statement mode exists, on either side, and that
is a capability absence rather than a rule broken.* The producer
emits {1: -19, 4: kid} and no CWT_Claims, so what it mints is not
registrable (Section 4.3); the verifier ignores label 15 as a non-
critical header, and a committed test shows it accepting a
protected header carrying a malformed 15: bstr as a perfectly good
generic COSE_Sign1. No code path anywhere evaluates a receipt *as
a Signed Statement*. This profile requires no Producer to
implement Signed Statement production -- bare receipts are a
defined presentation (Section 4) -- 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.
* *G9 -- only the first condition that holds is reported.*
Section 5.1 requires a verifier to report every condition that
holds. All five return on the first failure they find, so a set
Toraman Expires 16 February 2027 [Page 64]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
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.
* *G10 -- one implementation cannot distinguish the axes at all.*
Section 5.1 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 MALFORMED, TAMPERED and UNTRUSTED is recoverable from its
output. In all five, the remaining two conditions are conflated
by G6 and G7, so six is also the ceiling.
* *G11 -- an ambiguous kid cannot be handled at all.* Section 4.1
requires a verifier to try every key a kid 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 kid string to *one* key, so the requirement is not
expressible without changing that type: no implementation can
satisfy it today and none reports the case.
* *G12 -- a public option turns off a MUST.* Section 3.3 requires a
Chain Verifier to reject a transition between two present, unequal
scope.tenant values. The shipped verifier exposes
requireTenantConsistency, which defaults to refusing such a chain
but can be set to false -- 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.
* *G13 -- the two identifiers are returned, and nothing says which
claim each carries.* Section 4.2 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 kid alongside the parsed
receipt, whose sig.kid 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 "this key
emitted the envelope" from "this key signed the receipt into its
chain", and the native signature is not verified on that path at
all. G21 is the sharper form of the same defect.
* *G14 -- the attribution limitation is surfaced by one
implementation of five.* Section 6 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.
Toraman Expires 16 February 2027 [Page 65]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
* *G15 -- an unanswered tail question is reported by one
implementation of five.* Section 5.2 requires a verifier given no
checkpoint to report the tail question as unanswered rather than
to omit it. Only the TypeScript result carries a tailChecked
field; the other four return ordinary chain validity with no
distinct absence result.
* *G16 -- the opener-scoped qualification is missing in two
implementations.* Section 5.2 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.
* *G17 -- the approval reporting level does not exist, and the
obligation that would need it is not triggered.* Section 6.1 binds
a verifier *that reports something about an approval*: 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 governance.approval 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.
* *G18 -- the Transparency Service role is not implemented, which is
coverage absence, not nonconformance.* Section 7 requires a
Transparency Service to check the outer signature, the protected
kid, the CWT claims, a well-formed payload, the native signature
where the key is resolvable, and sub == scope.chain. No
implementation performs them, because none claims that role -- and
Section 1.4 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.
* *G19 -- an unknown non-critical header can be fatal.* Section 4.1
requires a verifier to ignore a header it does not understand that
crit 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.
* *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.* Section 4.2 pins the
cofactorless check [S]B = R + [k]A'. Read from source: the Python
verifier computes exactly that equation; the Rust verifier calls
verify_strict, which its own header names as cofactorless; and the
Toraman Expires 16 February 2027 [Page 66]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
Go verifier calls the standard library, whose implementation
carries the comment [S]B = R + [k]A and byte-compares the
recomputed point against the signature's first half. The
TypeScript reference verifies through node:crypto, so the equation
it executes is the one its *linked OpenSSL* 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 *in that pairing*. 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 "cofactored" 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
[UNVERIFIED] -- recorded that way rather than assumed in either
direction. *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*, and the
correction is recorded here rather than quietly dropped: a
register that over-accuses is as unreliable as one that under-
reports.
* *G21 -- the COSE path checks the manifest against the wrong
signature.* Section 6 now requires the agent claim to be checked
against the *native* sig.kid, and forbids an authorized outer kid
from standing in for it. The shipped COSE path does the opposite:
it pairs receipt.agent.id with the *outer* envelope kid and never
checks the native sig.kid against the manifest at all (src/cose/
receipt-cose.ts, the identity-binding block). Two consequences
follow, and both were *run against the built package rather than
reasoned about*. Legitimate relay -- native signature by the key
the manifest authorizes for alice, envelope signed by an unrelated
relay: ok:false, "agent \"alice\" is not authorized for signing
key \"k-relay\"", refusing a presentation Section 6 says MUST NOT
be refused on that ground. Laundering -- native signature by a
key authorized for nobody, claiming alice, wrapped by the key the
manifest does authorize for alice: ok:true, with the returned
payload's native sig.kid naming the unauthorized key. Both
verdicts are the reverse of the rule. The chain path is not
affected -- it checks the native sig.kid -- 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.
*Behaviour the reference enforces that this revision does not
require.* 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
Toraman Expires 16 February 2027 [Page 67]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
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 sig.alg literal; the key type,
encoding and public-point rules; the id bound; the non-empty-string
members; the forbidden member names; compliance.verdict; and the
UTF-8 round trip before a kid is resolved. What remains:
* *S1 -- a signing-key lifecycle.* Section 5.2 says this revision
defines no rotation or revocation mechanism. The reference has
one: its keyring may be a noa.signing-key-lifecycle/0.1 document
in which each key carries a retiredAt, 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 _divergence from the profile_
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
crv = 6 requirement of Section 4.1 is checked by inspecting the
encoded key, which the reference does.
* *S2 -- unused keyring entries are shape-checked.* 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
(Section 1.5), so both behaviours conform and the two answers are
both correct.
* *S3 -- a non-UTF-8 COSE kid refuses the object.* Section 4.1 now
requires a verifier not to *resolve* 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.
Toraman Expires 16 February 2027 [Page 68]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
* *S4 -- deterministic CBOR is enforced envelope-wide.* Section 4.1
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.
* *S5 -- input ceilings that differ, which is not the same as
ceilings that are missing.* 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
(impl-go/parse.go), the C# verifier inherits JsonDocument's
default maximum depth of 64, and the Rust verifier relies on
serde_json'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 *four
different ceilings*, and a document between the tightest and the
loosest is refused by some and accepted by others. Section 13
assigns limits to the *Deployment* 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 "no equivalent fixed limits"; that was false, and this row is
the correction.
* *S6 -- non-NFC handling.* 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. Section 3.3
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.
*Divergences among the five that the conformance harness cannot see.*
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.
* *D1 -- one trust document, two verdicts, and one of them is an
accusation.* An honest two-receipt chain signed by one key was
presented to all five verifiers twice: once with a plain {kid:
publicKey} keyring, once with this package's own noa.signing-key-
lifecycle/0.1 trust document carrying *the same key* with
retiredAt: null. Plain keyring: VALID in all five. Lifecycle
document: VALID in TypeScript, TAMPERED in Python, Go, Rust and
C#, the Python reason reading "unknown kid k-life at seq 0". The
four do not know the format, read it as a flat map, fail to find
the kid, and conclude the receipt was signed outside the trust
root -- so a legitimate trust document holding the correct key
Toraman Expires 16 February 2027 [Page 69]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
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 Section 8.3. No file under conformance/ mentions the
format, so no runner ever feeds it to them.
* *D2 -- one implementation does not state the key-canonicality
check.* TypeScript, Python, Rust and C# each carry the y >= q
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. Section 4.2 makes
that rejection a MUST as of this revision, so the Go verifier does
not state a check it is now required to make.
* *D3 -- two implementations have no channel for a required
statement.* 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.
* *D4 -- COSE exists once.* The envelope path is implemented only in
the TypeScript reference, so nothing on it -- the alg pin, crit
handling, deterministic encoding, kid resolution -- has cross-
implementation evidence in either direction.
*What the harness can and cannot settle, stated once.* conformance/
MATRIX.md 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.
*Two claims in the reference implementation's own comments are
retracted by this revision and not yet corrected there.* src/cose/
receipt-cose.ts calls its output a "COSE_Sign1 Signed Statement" and
promises "the external non-equivocation anchor NOA's self-signed
chain lacks". Neither survives this revision: Section 4.3 shows what
a Signed Statement additionally requires and the register above
records that the implementation emits none of it, while Section 7
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.
Toraman Expires 16 February 2027 [Page 70]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
*Conformance corpora, counted at the pinned snapshot.*
conformance/vectors/attack/ 16 files and conformance/vectors/
malformed/ 9 (25 adversarial in total); conformance/action-digest/ 33
vectors, 34 in the repository since df777a6; packages/rail-
x402/conformance/settlement-evidence/ 72 (1 accept, 57 reject, 14
control); packages/evidence/conformance/ 77 bundles. Alongside them,
conformance/golden/ answers a different question: whether a real past
release's own signed output still verifies today.
*What a third party measured.* On 27 July 2026 an independent
reconciliation implementation ran against this corpus at a pinned
commit and reported on the SCITT list
(https://mailarchive.ietf.org/arch/msg/scitt/zWZBQXXSGV-
vgbTeOLTjlb9DGLg/): *23 of 23* adversarial files then published
produced at least one integrity-class divergence, with cross-chain-
splice 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 *22 of 22* 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
Section 10 makes normative. The corpus has since grown from 23
adversarial files to 25.
*What is still missing.* The purpose-specific negative vectors that
Section 8, Section 8.2 and Section 9 call for are not in the public
corpus, with one exception: the attribution-substitution vector
landed at df777a6. 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 alg pin.
*Not implemented, stated plainly.* 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 (Section 4.2, Section 5.1); the rest
are absences with nothing owed against a rule. The distinction is
the one Section 8.3 insists on, applied to this section.
1. *No Signed Statement mode exists anywhere in the reference
implementation, on either side.* The producer emits {1: -19, 4:
kid} and no CWT_Claims (label 15), so what it mints does not
meet Section 4.3 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
Toraman Expires 16 February 2027 [Page 71]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
*ignores* label 15 as a non-critical header, and a committed
test shows it accepting a protected header carrying a malformed
15: bstr as a perfectly good generic COSE_Sign1. No code path
anywhere evaluates a receipt _as a Signed Statement_.
2. *Certificate-based key resolution is permitted but not
implemented.* Section 4.1 lets a deployment carry x5t or x5chain
in addition to kid; the verifier ignores both headers and
resolves kid alone. Nothing is excluded -- it is simply not
built.
3. *There is no detached-payload mode.* The parser requires the
payload element to be a byte string and refuses a null one; the
producer always attaches. Section 4.2 describes that and
nothing more.
4. *No range-presented verification.* Every implementation requires
the complete chain from seq == 0; a presentation starting at seq
1 is refused as a missing genesis, not verified as a partial
view. Section 5.1 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.
5. *No controller-outcome artifact and no physical-observation
artifact.* The vocabulary of Section 8.1 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 _shape_ (Section 8.2, Section 8.3), not an
answer to the actuator case the review thread was about.
6. *No settlement-evidence enrolment registry at the pinned 4764cab
snapshot* -- 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 _required_, so at this snapshot its absence
costs nothing and its presence buys no upgrade. Subsequent
implementation work is outside the measurements reported here.
7. *The relationship signal is not surfaced in bundle verdicts*,
and no level of this implementation can derive organizational
independence at all.
8. *The bundle verifier never reaches a network service*, 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.
9. *No published reproducible demonstration of registration with an
external SCITT Transparency Service.*
Toraman Expires 16 February 2027 [Page 72]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
10. *The purpose-specific negative vectors of Section 12 are not
written*, including a cross-implementation negative for the alg
pin. The attribution-substitution vector that was open in
earlier drafts of this list is no longer among them: it landed
at df777a6.
The boundary item 1 leaves is worth stating in the sharpest available
words: *"the signature verifies" and "an RFC 9943 Signed Statement
validates" are different properties, and this implementation
currently establishes only the first.* 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 Section 4.3 and [RFC9597], Section 2,
which defines label 15 as a CWT Claims map that occurs exactly once
across the two header buckets.
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.
13. Security Considerations
_Analysis binds every actor. Where a sentence carries a requirement
it names its actor; the requirements in this section fall on the
*Producer* (what MUST NOT appear on a receipt), on verifiers (what
they MUST NOT report), and on the *Relying Party* (how results may be
read)._
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.
"Policy identity" is one of the optional ones: both carriers may be
absent (Section 3.3), 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 HUMAN 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.
Toraman Expires 16 February 2027 [Page 73]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
Equivocation -- one issuer signing two divergent histories and
showing each to a different party -- is not detectable by any check
in this profile. Section 5.1 catches a fork only when both branches
are _presented together_, 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 (Section 7), 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 alg = -19 and pinned-curve requirements of
Section 4. Raw prompts, tool arguments, and secrets MUST NOT appear
on a receipt; only hashes (optionally HMAC for low-entropy values)
are carried.
*The scope of a checkpoint check is the scope of the key that signed
it.* 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 agent.id -- a
set, not one key (Section 5.2) -- 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 agent.id, the opener's
checkpoint does not certify a co-agent's tail.
A verifier never normalizes a non-NFC string, and this profile sets
no default for whether it rejects one (Section 3.3); 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 (Section 3.2, Section 3.3), two receipts may
carry agent.id or action.id 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 *not* an
Toraman Expires 16 February 2027 [Page 74]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
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.
*A trusted key can try to extend someone else's chain, and one
condition stops part of it.* Every receipt in a chain is individually
valid; nothing in a receipt says who is entitled to add the _next_
one. A party holding any key the verifier trusts can therefore
append well-formed receipts to another party's chain -- correct
prevHash, contiguous sequence, same scope.chain, same tenant -- and,
*where the appended receipts claim the victim's own agent.id*, every
condition but one stays silent. The one that fires is chain-key-
discontinuity (Section 5.1): those receipts carry a different sig.kid
under that same agent.id. 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 *new agent.id* trips no condition at all, because this
profile defines no append authority for a chain (Section 5.1,
Section 1.5); and an attacker whose second key is registered under
the victim's *kid* defeats a comparison made on identifiers rather
than on resolved key material. Where the attacker instead reuses the
victim's own agent.id _and_ sig.kid, it is signing with the victim's
key, which is a key-compromise problem this profile cannot address.
*A receipt can be re-wrapped by anyone who holds a trusted key.* The
payload is self-contained and self-signed, and Section 4.2 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
_content_ is safe; what it can do is present someone else's record as
something it emitted, and -- if the verifier attributes from the
outer kid -- have that attribution land on itself. What stops that
is the attribution model of Section 6, and it is worth being exact
about which half does the work. The manifest binds agent.id to
authorized keys and is checked against the *native* sig.kid, 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 -- Section 6 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
Toraman Expires 16 February 2027 [Page 75]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
claim is unbound. Registration does not help here: a Transparency
Service that follows Section 7 will happily register a relayed
statement, because every signature in it is valid.
*Same-party evidence is the forensic trap this profile names rather
than solves.* 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 (Section 6.1). An implementation that renders UNKNOWN
as "independent" has defeated the control.
*Substitution across claim classes is a signature-valid attack.* Both
the post-execution record presented as authorization and the machine
decision presented as human approval (Section 9) carry genuine
signatures. An *Evidence Verifier* that returns one boolean cannot
express "signature good, claim wrong". That is a defect in what such
a result can tell its reader, not a conformance failure:
Section 8.2'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.
*Parsing attacker-supplied input is a cost this profile does not
bound.* 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 -- id, at 128 code points (Section 3.1) -- 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 (Section 5.1), which grows without bound.
A *Deployment* 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.
*An unpinned digest construction is a silent join.* 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. Section 10 requires
the comparison to fail closed and be reported as indeterminate.
*Absence of scope.tenant is not a tenancy reset* (Section 3.3); a
verifier that treats it as one accepts a cross-tenant splice that
carries no tampering evidence of its own.
Toraman Expires 16 February 2027 [Page 76]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
14. Privacy Considerations
_This section addresses the *Producer* and the *Deployment*
(Section 1.4); verifiers inherit no obligation from it._
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.
*Raw parameters are forbidden, not prevented.* This profile prohibits
a *Producer* from placing raw prompts, tool arguments, secrets or
other sensitive parameters anywhere on a receipt (Section 3), and
action.paramsHash carries a digest in place of the values. What the
format cannot do is enforce it: several members are unconstrained
strings (Section 3.1), 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 _open_ the commitment is a matter for the
producer's own contract, not for this profile: Section 3 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.
*A hash of a low-entropy value is not a privacy control.* 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 hmac-sha256:<hex> 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.
*What remains linkable, by design.* A receipt still carries
scope.chain, agent.id, action.id, action.canonical, timestamps and,
where present, governance.approval.by. 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 *Deployment* 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.
Toraman Expires 16 February 2027 [Page 77]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
*Registration publishes, to the extent the log retains.* 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. [RFC9943], Section 6.2 contemplates
statements whose payload is carried as a hash rather than in full --
and *that construction is not conformant with this profile as
specified*: Section 4.2 requires the receipt itself to be the
attached payload, and Section 7 requires a Transparency Service to
validate that payload as a receipt and to check sub against its
scope.chain. 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 ([RFC9597], Section 3), so iss and sub are visible
to every party that can read the log. A deployment SHOULD decide
what may be published _before_ it registers: transparency rests on an
append-only sequence, which [RFC9943], 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.
*Signer relationship reporting is a disclosure too.* The trust-
configuration digests that Section 6.1 requires alongside a derived
relationship commit to a policy and a registry snapshot; they do not
disclose their contents, and a *Producer* SHOULD keep it that way
rather than inlining registry material into a result a relying party
may forward.
15. Open questions for the working group
_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._
* *The verification equation is pinned in this revision, and the
working group may revisit which one.* Section 4.2 pins the
cofactorless check together with the canonical signature scalar,
the canonical public-key encoding and the rejection of the eight
small-order encodings; [RFC8032], 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
Toraman Expires 16 February 2027 [Page 78]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
open is _which_ of the two the working group wants. The evidence,
taken from this document's own measurements (Section 12, 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 "it matches what ships". Either can be made
deterministic; neither can be left unmade.
* *The request signer is defined by example, which is why the text
that needs it is not normative.* Section 6.1 derives a signer
relationship against the _request signer_, 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.
* *Bare or enveloped?* Section 4 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.
* *Duplicate sequence numbers, in a future revision.* 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 (Section 5.1). 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 *future* revision should permit acceptance after de-
duplication, and under what reporting requirement, is the open
question; it is not open in this one.
* *Where the Evidence-Verifier layer belongs, now that this revision
has stopped requiring it.* Section 6.1, Section 8.1, Section 8.2,
Section 9 and Section 10 each mark the passages that run over
artifacts Section 1.5 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.
Toraman Expires 16 February 2027 [Page 79]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
* *Should the two untokenised results be named?* Section 1.5 lists
them: the conflict result of Section 8.2 and the unproven-
authorization result of Section 9. 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.
* *"Issuer" is overloaded across three different parties.*
Section 1.4 aliases the native Producer as the Issuer; Section 4.3
uses iss for the authority the outer Signed Statement is emitted
on behalf of; Section 6.1 calls the native signer the issuer; and
a relay that wraps someone else's receipt is a fourth party using
the same envelope position (Section 13). 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 Section 6
forbids. Whether these should be distinct named roles, and which
of them iss denotes when they differ, is a naming decision with
security consequences and it belongs to the working group.
* *This profile forbids the leap-second representation, and that is
a decision to review.* Section 3.3 requires a Producer not to emit
a date-time whose seconds field reads 60, and a verifier to reject
one. The alternative this revision rejected was to admit 60 and
require nobody to validate it, which is not implementable offline:
[RFC3339], Section 5.7 admits 60 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 date-time -- 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.
* *Key rotation, and what it currently costs.* Section 5.1 requires
one key per agent.id within a presented chain, which is what
closes the chain-extension attack; the price is stated in
Section 5.2: 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 (Section 12). Whether a rotation or
Toraman Expires 16 February 2027 [Page 80]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
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.
* *Standards Track with no IANA action, or Experimental?* The header
of this revision reads Standards Track, which is itself a change
from -00's Informational (Appendix A); it records the author's
current intention, not a decision taken for the working group.
Section 16 requests nothing, so there is no media type for the
payload, and Section 4.3's content type 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.
16. IANA Considerations
_This section carries no normative requirement on any actor._
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 content type of Section 4.3 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 -19 (Ed25519) and the existing COSE/CWT
header parameters. The vocabularies named in Section 6.1,
Section 8.2, and Section 8.3 are shared spellings for _reporting_,
not an interoperable output format -- this revision defines no result
schema (Section 1.5), 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.
17. References
17.1. Normative References
[FIPS180-4]
National Institute of Standards and Technology, "Secure
Hash Standard (SHS)", FIPS PUB 180-4, August 2015,
<https://csrc.nist.gov/pubs/fips/180-4/upd1/final>.
Toraman Expires 16 February 2027 [Page 81]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
[RFC2104] Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed-
Hashing for Message Authentication", RFC 2104,
DOI 10.17487/RFC2104, February 1997,
<https://www.rfc-editor.org/info/rfc2104>.
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119,
DOI 10.17487/RFC2119, March 1997,
<https://www.rfc-editor.org/info/rfc2119>.
[RFC3339] Klyne, G. and C. Newman, "Date and Time on the Internet:
Timestamps", RFC 3339, DOI 10.17487/RFC3339, July 2002,
<https://www.rfc-editor.org/info/rfc3339>.
[RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data
Encodings", RFC 4648, DOI 10.17487/RFC4648, October 2006,
<https://www.rfc-editor.org/info/rfc4648>.
[RFC8032] Josefsson, S. and I. Liusvaara, "Edwards-Curve Digital
Signature Algorithm (EdDSA)", RFC 8032,
DOI 10.17487/RFC8032, January 2017,
<https://www.rfc-editor.org/info/rfc8032>.
[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
May 2017, <https://www.rfc-editor.org/info/rfc8174>.
[RFC8785] Rundgren, A., Jordan, B., and S. Erdtman, "JSON
Canonicalization Scheme (JCS)", RFC 8785,
DOI 10.17487/RFC8785, June 2020,
<https://www.rfc-editor.org/info/rfc8785>.
[RFC8949] Bormann, C. and P. Hoffman, "Concise Binary Object
Representation (CBOR)", STD 94, RFC 8949,
DOI 10.17487/RFC8949, December 2020,
<https://www.rfc-editor.org/info/rfc8949>.
[RFC9052] Schaad, J., "CBOR Object Signing and Encryption (COSE):
Structures and Process", STD 96, RFC 9052,
DOI 10.17487/RFC9052, August 2022,
<https://www.rfc-editor.org/info/rfc9052>.
[RFC9053] Schaad, J., "CBOR Object Signing and Encryption (COSE):
Initial Algorithms", RFC 9053, DOI 10.17487/RFC9053,
August 2022, <https://www.rfc-editor.org/info/rfc9053>.
Toraman Expires 16 February 2027 [Page 82]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
[RFC9360] Schaad, J., "CBOR Object Signing and Encryption (COSE):
Header Parameters for Carrying and Referencing X.509
Certificates", RFC 9360, DOI 10.17487/RFC9360, February
2023, <https://www.rfc-editor.org/info/rfc9360>.
[RFC9597] Looker, T. and M.B. Jones, "CBOR Web Token (CWT) Claims in
COSE Headers", RFC 9597, DOI 10.17487/RFC9597, June 2024,
<https://www.rfc-editor.org/info/rfc9597>.
[RFC9864] Jones, M.B. and O. Steele, "Fully-Specified Algorithms for
JSON Object Signing and Encryption (JOSE) and CBOR Object
Signing and Encryption (COSE)", RFC 9864,
DOI 10.17487/RFC9864, October 2025,
<https://www.rfc-editor.org/info/rfc9864>.
[RFC9942] Steele, O., Birkholz, H., Delignat-Lavaud, A., and C.
Fournet, "CBOR Object Signing and Encryption (COSE)
Receipts", RFC 9942, DOI 10.17487/RFC9942, June 2026,
<https://www.rfc-editor.org/info/rfc9942>.
[RFC9943] Birkholz, H., Delignat-Lavaud, A., Fournet, C., Deshpande,
Y., and S. Lasker, "An Architecture for Trustworthy and
Transparent Digital Supply Chains", RFC 9943,
DOI 10.17487/RFC9943, June 2026,
<https://www.rfc-editor.org/info/rfc9943>.
[UAX15] The Unicode Consortium, "Unicode Standard Annex #15:
Unicode Normalization Forms", 2025,
<https://www.unicode.org/reports/tr15/>.
17.2. Informative References
[ACTA] Farley, T., "Signed Decision Receipts for Machine-to-
Machine Access Control", Work in Progress, Internet-Draft,
draft-farley-acta-signed-receipts-02, June 2026,
<https://datatracker.ietf.org/doc/draft-farley-acta-
signed-receipts/>.
[AGENTROA] Michalak, J., "Agent Route Origin Authorization
(AgentROA): A Cryptographic Policy Enforcement Framework
for AI Agent Actions", Work in Progress, Internet-Draft,
draft-nivalto-agentroa-route-authorization-01, April 2026,
<https://datatracker.ietf.org/doc/draft-nivalto-agentroa-
route-authorization/>.
Toraman Expires 16 February 2027 [Page 83]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
[AI-ART50] Dawkins, V., "A SCITT Profile for EU AI Act Article 50
Transparency Receipts", Work in Progress, Internet-Draft,
draft-dawkins-scitt-ai-article50-00, May 2026,
<https://datatracker.ietf.org/doc/draft-dawkins-scitt-ai-
article50/>.
[AIVS] Stone, B., "AIVS: Agentic Integrity Verification
Standard", Work in Progress, Internet-Draft, draft-stone-
aivs-00, March 2026,
<https://datatracker.ietf.org/doc/draft-stone-aivs/>.
[ASQAV] Gomes Marques, J. A., "Compliance Profile of Signed Action
Receipts for AI Agents", Work in Progress, Internet-Draft,
draft-marques-asqav-compliance-receipts-07, July 2026,
<https://datatracker.ietf.org/doc/draft-marques-asqav-
compliance-receipts/>.
[I-D.hillier-scitt-arp]
Hillier, J. D., "Attestation Reconciliation Protocol",
Work in Progress, Internet-Draft, draft-hillier-scitt-arp-
03, August 2026, <https://datatracker.ietf.org/doc/draft-
hillier-scitt-arp/>.
[I-D.schrock-action-evidence-boundary]
Schrock, I., "The Action Evidence Boundary for
Consequential Agent Effects", Work in Progress, Internet-
Draft, draft-schrock-action-evidence-boundary-03, August
2026, <https://datatracker.ietf.org/doc/draft-schrock-
action-evidence-boundary/>.
[I-D.toraman-noa-action-digest]
Toraman, T., "The NOA Action Digest: a Domain-Separated
Correlation Value for Human-Approved Agent Actions", Work
in Progress, Internet-Draft, draft-toraman-noa-action-
digest-01, August 2026, <https://datatracker.ietf.org/doc/
draft-toraman-noa-action-digest/>.
[I-D.toraman-noa-settlement-evidence]
Toraman, T., "Settlement Evidence for Human-Approved Agent
Payments", Work in Progress, Internet-Draft, draft-
toraman-noa-settlement-evidence-01, August 2026,
<https://datatracker.ietf.org/doc/draft-toraman-noa-
settlement-evidence/>.
[OWASP-MCP]
OWASP, "OWASP MCP Top 10", Entry MCP08:2025, Lack of Audit
and Telemetry, 2025,
<https://owasp.org/www-project-mcp-top-10/>.
Toraman Expires 16 February 2027 [Page 84]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
[PSER] Wilder, R., "A SCITT Profile for Physical-Site Engagement
Receipts", Work in Progress, Internet-Draft, draft-wilder-
scitt-physical-site-engage-receipt-00, July 2026,
<https://datatracker.ietf.org/doc/draft-wilder-scitt-
physical-site-engage-receipt/>.
[RFC7942] Sheffer, Y. and A. Farrel, "Improving Awareness of Running
Code: The Implementation Status Section", BCP 205,
RFC 7942, DOI 10.17487/RFC7942, July 2016,
<https://www.rfc-editor.org/info/rfc7942>.
[SCITT-VCP]
Kamimura, T., "A SCITT Profile for Verifiable Audit Trails
in Algorithmic Trading: The VeritasChain Protocol (VCP)",
Work in Progress, Internet-Draft, draft-kamimura-scitt-
vcp-03, July 2026, <https://datatracker.ietf.org/doc/
draft-kamimura-scitt-vcp/>.
Appendix A. Changes since -00
_This section is to be removed by the RFC Editor before publication._
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.
A.1. Interface stability, and the two corrections a citing document
must act on
Section 5 keeps its number, its title and its XML anchor (hash-
chaining-and-completeness), so both numeric and fragment references
from [PSER] continue to resolve, and the construction it defines --
chain.seq, prevHash, the linkage rule -- is unchanged.
*Two sentences of its paragraph were wrong in -00 and are corrected
in this revision.* A document that adopted Section 5 by reference
inherited both, so the change is stated here in the terms an
implementer needs:
* _Checkpoint authority._ -00 said the authority "is bound to the
genesis key (the key authorized at seq == 0), so a foreign
checkpoint produced by a re-heading attacker is rejected". That
binding does not exist on its own: it requires an identity
manifest, and it binds to the _set_ of keys the manifest
authorizes for the genesis receipt's agent.id. -01 says so, and
Toraman Expires 16 February 2027 [Page 85]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
says plainly that without a manifest the property does not hold.
*An implementation that relied on -00's sentence believes it
rejects a re-heading attacker and does not.*
* _Tail truncation._ -00 said the profile "detects in-band tampering
and tail truncation _within_ a presented chain". 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. *An implementation that relied on -00's sentence
believes a clean verification result means nothing was deleted
from the end. It does not.*
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. *Subsections
5.1 and 5.2 are NEW, and the principal requirements they add are:*
* detect all six chain conditions, each decided from the presented
set alone (MUST), and report them distinguishably (MUST);
* 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);
* require the complete chain from seq == 0, refusing a presentation
that begins elsewhere (MUST);
* 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);
* refuse to let an unauthenticated checkpoint establish a head
conflict (MUST NOT), reporting checkpoint-unverified instead;
* state whether the checkpoint behind any tail conclusion was
authenticated (MUST);
* report every condition that holds rather than the first found
(MUST);
* never report a chain-level condition as a per-receipt failure
(MUST NOT);
* never infer completeness from the absence of these conditions
(MUST NOT);
* (this one binds a *Deployment*, not a Chain Verifier) take
temporal ordering from something other than ts (MUST), since no
check constrains it;
* on a multi-agent chain, never report an unqualified tail check
(MUST NOT) and state whose view the check covers (MUST);
* never treat a checkpoint's age as bounded by anything in the
object (MUST NOT), since ts is signer-chosen and this profile
defines no expiry;
Toraman Expires 16 February 2027 [Page 86]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
* validate a supplied checkpoint's structure before authenticating
it (MUST) -- closed object, spec, the four value shapes and the
sig sub-object -- and never turn a malformed checkpoint into a
finding about the chain (MUST NOT);
* compare a supplied checkpoint's chain to the presented scope.chain
(MUST), report a mismatch as a finding about the checkpoint, and
never let it fail an otherwise valid presented chain (MUST NOT);
* 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;
* when an identity manifest is supplied, require a checkpoint's
sig.kid to be authorized for the genesis receipt's agent.id and
reject any other trusted key as UNTRUSTED (MUST);
* when no checkpoint is supplied, report the tail question as
unanswered (MUST) rather than as an absence of truncation (MUST
NOT);
* reject a presented chain in which two receipts share an agent.id
and differ in sig.kid (MUST), reporting chain-key-discontinuity;
* where it _chooses_ 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);
* 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);
* (*Deployment*) present a chain for verification from genesis
(MUST), since a Chain Verifier refuses a presentation that begins
elsewhere;
* (*Deployment*) never assume the outer envelope's kid 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;
* (*Deployment*) 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.
*This list is not exhaustive, and this revision no longer claims that
it is.* 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 *Deployment* as marked, one conditional on an option a
verifier need not take. None constrains a Producer or changes how a
chain is _built_, 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.
Toraman Expires 16 February 2027 [Page 87]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
*One documentation narrowing, disclosed rather than buried.* -00 said
"a verifier checking a range of receipts MUST verify ..." and never
said what a range may be. 5.1 now states that a conforming verifier
requires the complete chain from seq == 0 and refuses a presentation
that begins elsewhere. That is a narrowing *of the text, not of any
behaviour*: 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 0..n-1, and a presentation of
seq 1,2 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 Section 12 instead of being
implied by silence.
The same care applies to the gap condition itself: a correctly linked
chain presenting 0 then 2 was underspecified in -00 and is a chain-
sequence-gap 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.
*What happened to every -00 section, checked against the posted text
rather than remembered.* Sections 1 through 7 keep their -00 numbers.
The rest moved, and one was removed:
Toraman Expires 16 February 2027 [Page 88]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
+======================+===================================+
| In -00 | In -01 |
+======================+===================================+
| 1-7 | unchanged numbers |
+----------------------+-----------------------------------+
| 8 -- Relationship to | *removed.* It changed no |
| ACTA signed receipts | requirement, and a field-by-field |
| (equivalence), with | mapping to one other individual |
| its mapping table | draft did not earn a section here |
+----------------------+-----------------------------------+
| 9 -- Policy-replay | 11 |
| out of scope | |
+----------------------+-----------------------------------+
| 10 -- Security | 13 |
| Considerations | |
+----------------------+-----------------------------------+
| 11 -- IANA | 16 |
| Considerations | |
+----------------------+-----------------------------------+
| Appendix A -- | 12, promoted into the body where |
| Implementation | RFC 7942 places it |
| status | |
+----------------------+-----------------------------------+
| Appendices B and C | 17.1 and 17.2 |
| -- the two reference | |
| lists | |
+----------------------+-----------------------------------+
| -- | 8, 9 and 10 are new (external- |
| | world non-claims, authorization |
| | vs evidence, the digest boundary) |
+----------------------+-----------------------------------+
| -- | *14, Privacy Considerations, is |
| | new*, and -00 had no privacy |
| | section at all |
+----------------------+-----------------------------------+
| -- | *15, Open questions for the |
| | working group, is new* |
+----------------------+-----------------------------------+
| -- | Appendix A (this change log) and |
| | Appendix B (acknowledgments) are |
| | new |
+----------------------+-----------------------------------+
Table 8
The record format itself is untouched: no member of noa.receipt/0.1
is added, removed, or re-typed.
Toraman Expires 16 February 2027 [Page 89]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
A.2. What the author committed to on 23 July, item by item
The author's reply to the thread
(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.
+=========================+=======================================+
| Committed | Where it landed |
+=========================+=======================================+
| 1. Separate | Section 8.1 |
| authorization, | |
| controller-reported | |
| outcome and physical | |
| completion | |
+-------------------------+---------------------------------------+
| 2. Define trust | Section 8.2, Section 8.3 |
| semantics and failure | |
| behaviour for each | |
| claim independently | |
+-------------------------+---------------------------------------+
| 3. Make signer | Section 6.1; derived and machine- |
| relationships machine- | readable in the reconciler |
| readable when signed | (Section 12), *not yet surfaced in |
| outcome evidence is | bundle verdicts*, and never derivable |
| present | as organizational independence today |
+-------------------------+---------------------------------------+
| 4. Add purpose- | The attribution-substitution vector |
| specific negative | landed at df777a6 (corpus 33 -> 34); |
| conformance vectors | the rest are named and committed in |
| | Section 12 and *not yet written* -- |
| | stated as open, not as done |
+-------------------------+---------------------------------------+
| 5. Define the shared | Section 10, construction published |
| action-digest | separately |
| construction rather | [I-D.toraman-noa-action-digest] |
| than assume equivalence | |
| with action.paramsHash | |
+-------------------------+---------------------------------------+
| 6. Clarify the | Section 4.2 |
| relation between the | |
| native receipt | |
| signature and the outer | |
| COSE envelope | |
+-------------------------+---------------------------------------+
| 7. Resolve the policy- | Section 3.3 |
| identity inconsistency | |
Toraman Expires 16 February 2027 [Page 90]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
| in the base payload | |
+-------------------------+---------------------------------------+
| 8. Say clearly that a | Section 5, Section 5.1 and Section 13 |
| self-signed chain | |
| cannot prove an unseen | |
| tail does not exist | |
+-------------------------+---------------------------------------+
| 9. Replace "non- | Abstract and Section 1 now say |
| repudiable" with the | issuer-authenticated / signature- |
| narrower wording | verifiable |
+-------------------------+---------------------------------------+
| 10. Update the stale | Section 12: -19 is emitted, -8 |
| algorithm-migration | rejected under a genuine signature, |
| note | note corrected rather than carried |
| | forward -- and the stale _byte |
| | string_ an earlier revision quoted is |
| | corrected in the same place |
+-------------------------+---------------------------------------+
Table 9
A.3. Corrections this revision makes to -00's own text
* *The protected-header key-identifier rule CHANGED, and this is a
change rather than a correction.* -00 required the protected
header to contain "a key identifier sufficient to resolve the
verification key: kid (label 4), *or* a certificate reference (x5t
label 34 / x5chain label 33) where a PKI is used" -- a
disjunction, not a contradiction. -01 makes a resolvable kid
mandatory and demotes the certificate references to additional-
only, because a verifier conforming to this profile is required to
resolve kid and is not required to process certificates. A
producer that satisfied -00 by carrying x5t alone is *not*
conformant to -01. Section 4 now says kid 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 kid
may resolve a key but never names an identity (Section 6). It
also states that additional registered headers MUST NOT cause
rejection unless listed in crit.
* *Terminology defined a policyHash the payload never carried.*
Section 3.3 resolves it in favour of what the wire format does:
governance.ruleId is a label, the optional governance.compliance
member is where a policy commitment lives, and neither makes a
receipt replayable.
Toraman Expires 16 February 2027 [Page 91]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
* *scope.tenant was shown as always present.* It is optional, and
Section 3.3 states the rule that matters: absence MUST NOT reset a
tenancy comparison, or an optional field becomes a splice channel.
* *-00 said the commitment fields were not carried at all*, in the
section that is now Section 11. They may be; what remains
unspecified is the replay _construction_. The prohibition on
presenting a receipt as carrying a re-derivable verdict is
unchanged.
* *The implementation-status byte string was wrong.* An earlier
revision quoted a10132 as the protected header this implementation
emits. It is not: that encodes {1: -19} alone, and the emitted
header is the two-member map {1: -19, 4: kid} -- the kid having
been moved into the signed bucket, which the same document's own
Section 4 says. The identifier claim was right and the byte-level
claim was not; Section 12 now states the map, and the paired
a10127 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.
* *The profile called its objects SCITT Signed Statements and called
them registrable.* 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. Section 4.3 is new and specifies the
mapping normatively; Section 4 and Section 7 no longer assert
registrability in the present tense; and the gap heads the not-
implemented register in Section 12. This was the round-1 review's
categorical objection and it was right.
* *The payload figure omitted sig.* -00's figure ended at chain,
which made its own "JCS(receipt WITHOUT chain.hash AND WITHOUT
sig.value)" construction impossible to implement from the figure.
sig (with alg, kid, value) is now shown as the mandatory member
the schema has always required.
* *The optional-member contract listed two members; the schema has
seven.* agent.model, action.rollbackRef, governance.ruleId,
governance.approval and compliance.verdict join scope.tenant and
governance.compliance, and the figure marks each. The rule that
present-as-null is not absence -- different bytes, different hash
-- is stated with them.
* *Detached payloads were described backwards.* -00 said a detached
payload's digest is bound through chain.hash. COSE requires the
detached bytes themselves for the Sig_structure, and chain.hash is
a chain-linkage value computed over the receipt with chain.hash
and sig.value removed. The shipped parser refuses a non-byte-
string payload. Section 4.2 now defines the attached form only,
and a detached mode is future work.
Toraman Expires 16 February 2027 [Page 92]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
* *The algorithm rule contradicted itself.* -00 said verifiers MUST
reject every other alg and then that they SHOULD reject -8 unless
a legacy mode was negotiated. -8 is not a special case, no such
mode exists in any implementation, and the exception is deleted.
* *Certificate references were offered as a sufficient key
identifier.* They are now additional and OPTIONAL, with kid
required, matching a verifier that ignores x5t/x5chain.
* *The signer-relationship comparator named the wrong pair.* 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.
* *The escaping row was wrong and the normalization row was too
absolute.* JCS also escapes " and \; 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.
* *Timestamp validation was underspecified, and -01 makes the strict
reading normative.* -00 required "RFC 3339" form without saying
what a verifier must do with a string that matches the shape but
is not a valid date-time. Section 3.3 now requires rejection and
pins the accepted grammar; Section 12 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.
* *-00's NFC rule was rewritten, and that rewrite is itself a
change.* -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 Section 3.2 and Section 3.3 together.
* *Nine -00 fragment anchors were broken and are restored.* A
shortened anchor scheme in an earlier -01 draft would have broken
deep links into introduction, relationship-to-other-work,
identity-binding, policy-replay-is-out-of-scope-for-this-revision,
security-considerations, implementation-status, hash-chaining-and-
completeness, 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.
* *Six references were reclassified or added.* 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.
Toraman Expires 16 February 2027 [Page 93]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
* *Requirements that existed only as code are now text.* 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 id and the seven non-empty members, and
the closed ALLOW/DENY set for governance.compliance.verdict
(Section 3.1); the three JSON member names refused at the parse
boundary (Section 3.2); the exact sig.alg literal, the Ed25519
key-type rule, the canonical key encoding and the two public-point
rejections (Section 4.2); the UTF-8 round trip before a kid is
resolved (Section 4.1); and the checkpoint's closed object, value
shapes and chain equality (Section 5.2). One of them is not
stated by one implementation, which Section 12 records. The
general rule of Section 8.3 -- 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 Section 5.1 and
Section 5.2.
* *Conformance classes are new, and they change how every MUST is
read.* -00 and earlier -01 drafts said "a verifier" and meant at
least three different things, which made some requirements
unsatisfiable by a single-receipt implementation and others
meaningless to a chain one. Section 1.4 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 _same class_ 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: Section 1.5 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
(Section 15).
* *What this profile does not define is now a section rather than a
scatter.* Section 1.5 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.
* *The non-equivocation claim is narrowed to what registration
actually buys.* The Abstract, Section 7 and Section 13 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
Toraman Expires 16 February 2027 [Page 94]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
not specify and places no requirement on anyone to perform -- and
a Transparency Receipt handed to an offline party _by the prover_
is prover-supplied evidence, exactly as Section 5.1 says of a
checkpoint. The document's own argument now applies to its own
escape hatch.
* *Registration binds more than the envelope.* Section 7 now
requires a Transparency Service to verify the payload's own schema
and native signature, and to check sub against scope.chain.
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.
* *Two further chain-level conditions are named.* The cross-tenant
splice rule of Section 3.3 is a set property the axis table did
not carry; it is now chain-tenant-split. And every shipped
verifier already rejects a mid-chain key change for one agent
while no revision had ever named it: that is chain-key-
discontinuity, 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
(Section 5.1).
* *An older checkpoint over a longer chain is no longer truncation.*
Section 5.1 distinguishes a consistent extension from a
truncation, and Section 12 records that the reference
implementation reports one verdict for both.
* *The re-canonicalization requirement was impossible as written* --
it demanded that a serialization omitting two members equal the
bytes those members are in. Section 4.2 now states the check the
implementation actually performs: JCS(parse(bytes)) == bytes over
the whole payload.
* *Field semantics are pinned* (Section 3.1): the notation legend,
spec exactness, the absence of identifier namespaces, what ts
means, and the cross-field rules that stop one record saying two
things -- including the simulation-marker rule, which -00 left
open.
* *Timestamps have a grammar* (Section 3.3): which spellings are
accepted, why the spelling is signed, and why a verifier must not
normalize one.
* *The relay attack is in the threat model* (Section 13). 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.
* *Parser resource bounds are named as the implementer's
responsibility*, since this profile sets none and requires a
complete chain from genesis.
Toraman Expires 16 February 2027 [Page 95]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
* *Two RFC 9943 section pinpoints were wrong and are corrected.* An
earlier -01 draft cited "Section 6.12" and "Section 6.13" for the
kid 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.
* *The deterministic-CBOR pin was too coarse.* "Section 4.2" 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.
* *A quoted claim about another protocol's specification was
removed.* 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.
* *The intended status CHANGED, and it is a change rather than an
editorial detail.* The posted -00 carried "Intended status:
Informational"; this revision carries "Intended status: Standards
Track". 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: Section 15 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.
* *The SCITT Architecture reference was a work-in-progress draft.*
It was published as [RFC9943] in June 2026 and is cited as an RFC.
* *The equivalence section is gone, not edited.* -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; Section 1.2 still names that
work and says a mapping is available to whoever needs to port one.
Appendix B. Acknowledgments
This revision is the product of a public review thread on the SCITT
mailing list in July 2026, archived at
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.
Toraman Expires 16 February 2027 [Page 96]
Internet-Draft SCITT AI-Agent Action Receipts August 2026
* *Carlos Hernandez* separated a controller's report from the
physical effect and asked for a machine-checkable non-claim rather
than a caveat in prose: Section 8.1, Section 8.2 and Section 8.4.
* *Iman Schrock* (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: Section 9, Section 6.1 and Section 10.
* *Steven Mih* held the thread to shared conformance vectors rather
than shared vocabulary alone, which is the discipline Section 12
commits to.
* *Joel Hillier* (Certisyn, Inc.) ran an independent reconciliation
implementation against the published corpora and reported the
numbers, including the failure no per-receipt verifier can reach:
Section 5.1, Section 3.2 and the indeterminate-control discussion
in Section 10.
Author's Address
Tora Toraman
NordenSoft
Denmark
Email: hello@ordeliya.com
Toraman Expires 16 February 2027 [Page 97]