AER-1: A Portable Execution Receipt for AI Agent Tool Calls
draft-zambo-aer1-18
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.
The information below is for an old version of the document.
| Document | Type |
This is an older version of an Internet-Draft whose latest revision state is "Active".
|
|
|---|---|---|---|
| Author | Brennan Zambo | ||
| Last updated | 2026-10-12 | ||
| RFC stream | (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-zambo-aer1-18
Independent Submission B. Zambo
Internet-Draft Zambo
Intended status: Informational 12 October 2026
Expires: 12 April 2027
AER-1: A Portable Execution Receipt for AI Agent Tool Calls
draft-zambo-aer1-18
Abstract
This document specifies AER-1, a small vocabulary for recording one
AI agent tool call as a portable, independently checkable execution
receipt. A receipt identifies the execution, records when it
happened, preserves the canonical bytes used for the output
commitment, names the tool and caller scope, carries a provenance
class, and resolves at a stable public URL. The format separates
what the system observed from claims about the outside world, and it
separates provenance (who ran or reported the action) from the record
itself. A reference implementation is deployed, and its receipts are
publicly verifiable without an account or token. The specification
covers workflow receipts, which bind an ordered sequence of step
receipts to a single goal with a Merkle root over the step sequence,
and hash-chained job timelines for tamper-evident sequencing of
executions. This revision closes honesty and precision gaps: it
adds the workflow-receipt truncation limit (new Section 8.4),
normatively specifies the external anchor format and its
verification procedure (rewritten Section 10, new Section 10.1),
fixes a RECOMMENDED/MUST contradiction in Section 8.1, makes the
Section 6 verification step testable, and documents replay,
low-entropy, empty-record, and size-limit considerations. It
changes no receipt field, digest construction, or existing
verification rule. The -13 revision fixes a malformed sentence in
the Abstract, three incorrect section cross-references, stale Table
of Contents page numbers, a skipped footer page number, misaligned
table rows, and chain version-string updates, and records two reported
interoperability examples in Implementation Status; it
changes no receipt field, digest construction, or verification
rule. The -14 revision adds tamper-evident metadata binding (new
Section 3.1; conditional metadata_hash for schema 0.4 and later),
disambiguates the anchor receipt_id (Table 4), corrects the
self-anchoring witnessing claim (Section 10), makes the Section 8.3
display rule a SHOULD with a byte-testable MUST, narrows the
provenance attestation wording (Section 5), states the binding
non-transfer rule normatively (new Section 10.2), pins
anchored_digest to bare hex (Table 4), corrects the vector corpus
counts, and adds normative disclosure of missing issuer
authentication. It changes no existing receipt field, digest
construction, or verification rule for schema 0.3 and earlier.
The -15 revision narrows the Implementation Status wording for the
reported interoperability examples at the implementer's request; it
changes no receipt field, digest construction, or verification rule.
The -16 revision documents the live workflow tools API (new
Sections 8.5, 8.6, 8.7): the workflow_close tool, workflow metadata
in step receipts, and the workflow view API. It changes no receipt
field, digest construction, or existing verification rule.
The -17 revision documents the recommended execution_context member
(new Section 3.2): an optional, non-core structure for recording
source and version context such as the model name and version behind
a tool call and the identity of the source system acted upon. It
changes no existing receipt field, digest construction, or
verification rule.
The -18 revision repairs the reference apparatus (normative
references to RFC 3339, RFC 8259, RFC 4122, and RFC 4648, and
the previously missing RFC 7942 entry), pins two parser edge
cases (duplicate JSON member names and leap-second timestamps
are rejected), names the Unicode normalization behavior in
Section 4 with a computed vector pair, adopts the unknown-
versus-not-applicable convention for optional members, adds
optional members for readback outcomes, per-field provenance,
negative records, reversal state, method descriptors, and
payment references, completes the execution_context member set
(model and environment digests), adds the optional input_hash
commitment (Section 3.3), permits SCITT and RFC 3161 anchors
under Section 10, states the clock-trust and equivocation
limits in Section 13, and rewrites Section 15 to position the
specification against adjacent work. It changes no receipt
field, digest construction, or verification outcome for any
receipt well-formed under the -17 rules.
Status of This Memo
This Internet-Draft is submitted in full conformance with the
provisions of BCP 78 and BCP 79.
Internet-Drafts are working documents of the Internet Engineering
Task Force (IETF). Note that other groups may also distribute
working documents as Internet-Drafts. The list of current Internet-
Drafts is at https://datatracker.ietf.org/drafts/current/.
Internet-Drafts are draft documents valid for a maximum of six months
and may be updated, replaced, or obsoleted by other documents at any
time. It is inappropriate to use Internet-Drafts as reference
material or to cite them other than as "work in progress."
This Internet-Draft will expire on 12 April 2027.
Copyright Notice
Copyright (c) 2026 IETF Trust and the persons identified as the
document authors. All rights reserved.
Zambo Expires 12 April 2027 [Page 1]
Internet-Draft AER-1 October 2026
Implementation Status
This section records the implementation status of the AER-1
vocabulary at the time of this revision, following [RFC7942]. It
will be removed before publication as an RFC.
Reference implementation. Zambo (https://zambo.dev), operated by
the document author, is a deployed implementation of Sections 3
through 10. Tool calls made over HTTPS as JSON-RPC to
https://zambo.dev/api/mcp receive a public receipt page at
https://zambo.dev/run/<id>, retrievable without an account, wallet,
or token. A machine-readable verifier is exposed as described in
Section 6. Receipts are anchored to public Nostr relays with an
envelope binding the receipt identifier, the output hash, and the
evidence hash (envelope "zambo-receipt-anchor/1").
Workflow receipts (Section 8) are deployed in the reference
implementation. Multi-step sessions receive a workflow receipt at
https://zambo.dev/workflow/<workflow_id> binding the ordered step
receipts with a Merkle root, and step receipt pages link to the
parent workflow ("Part of workflow") while workflow pages link to
each step receipt.
Conformance kit. An open conformance suite for this vocabulary is
maintained at https://gitlab.com/rambozambodotdev/zambo. It ships
seven bundled kit runners (Rust, Go, TypeScript, Python, Java, C#,
and Swift), each verified 85/85 across an 85-vector corpus: 34
receipt-level vectors (valid receipts, invalid receipts, anchored
receipts, and reference-producer profile boundaries, including the
uppercase-id and bad-verification-status regression vectors added
after -05), 8 Merkle vectors (Section 8.1), 7 Section 7 timeline
vectors, 28 Section 7.1/-07 hardened chain vectors, and 8 chain
commitment vectors (Section 7.3). Each runner is zero-dependency
within its language toolchain. Running the
Python suite takes two steps and no dependencies beyond git and a
Python 3 interpreter:
git clone https://gitlab.com/rambozambodotdev/zambo.git
python3 aer1-implementations/python/conformance.py
An implementation is conformant when it produces byte-identical
verdicts to the reference runners across the full vector corpus.
The corpus includes attack vectors for tampered provenance labels,
schema version downgrades, and legacy (pre-0.4) receipts;
conformance requires the Section 6 metadata checks to fail the
first two and to report the third with a legacy notice rather
than a failure. The kit is versioned; the v1.4.0 release is the
frozen reference for the corpus described above. This revision
specifies additional vectors in the text (the Section 4 Unicode
normalization pair, the Section 6 negative cases, and the
Section 8.2 workflow case); they join the corpus with the kit's
next publication, and the counts above describe the kit as
published until they do.
Independent verification. The Section 6 verification procedure has
been exercised against live receipts by independent third parties:
* On 2026-09-16, an independent agent ("Merv", on the Clawstr
network) verified a live receipt
(id 0ec81343-cc1f-4c25-9c33-d76b5582406d) in three checks: the
SHA-256 of the decoded canonical bytes matched the output
commitment, the upstream evidence hash matched, and the anchor
event carried a valid signature retrievable on independent
relays. The same party reported that the anchor envelope did
not then bind the evidence hash; the reference implementation
has since shipped the binding envelope described above.
* On 2026-09-16, an independent agent ("nilo_agent", on Nostr)
ran adversarial verification challenges against live receipts,
including a witness round and a strong-form Bitcoin-anchored
challenge (anchor block 967351, receipt 88094e26), each
answered with bytes the challenger could check independently.
* On 2026-09-25 and 2026-09-26, an independent agent ("Devan",
on Nostr) published timestamped probe receipts (v1 and v2)
exercising independent verification of agent work in the same
receipt model.
Seven bundled kit runners for Sections 3 through 6 are
maintained in the conformance kit repository (Rust, Go, TypeScript,
Python, Java, C#, and Swift), each verified 85/85 against the full
vector corpus described above. These runners are the kit's own
test runners,
not independent implementations. Independent third-party
implementations, built from the draft text alone without access to
kit internals, include ARION's Node.js and Java verifiers and
ColonistOne's Perl verifier, each conformant against the same
corpus. Since revision 12, contributors have reported two scoped
interoperability exercises. On 2026-10-08,
Tanilo and the AER-1 author published a cross-repository worked
example linking an AER-1 receipt to a live Tanilo receipt by
canonical hash, with the AER-1 commitment check and the Tanilo
signature check run separately, each under its own rules. Also
on 2026-10-08, the independent builder
FlyThink published a four-case fixture binding AER-1 execution
receipts to physical completion claims; all four cases replay
cleanly, including the case where a valid AER-1 receipt coexists
with invalid physical-evidence binding (see Section 10.2).
Reports of additional
implementations or independent verification results are solicited;
implementers are encouraged to
validate against the open conformance kit described above and to
report their results.
Zambo Expires 12 April 2027 [Page 2]
Internet-Draft AER-1 October 2026
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.
Table of Contents
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3
2. Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 4
3. The Receipt Record . . . . . . . . . . . . . . . . . . . . . 4
3.1. Metadata Binding . . . . . . . . . . . . . . . . . . . . 5
3.2. Execution Context . . . . . . . . . . . . . . . . . . . . 5
3.3. Optional Input Commitment . . . . . . . . . . . . . . . . 5
3.4. Additional Optional Members . . . . . . . . . . . . . . . 5
4. Canonical Bytes and Output Commitment . . . . . . . . . . . . 6
5. Provenance Classes . . . . . . . . . . . . . . . . . . . . . 7
6. Verification Procedure . . . . . . . . . . . . . . . . . . . 7
7. Hash-Chained Job Timelines . . . . . . . . . . . . . . . . . 8
8. Workflow Receipts . . . . . . . . . . . . . . . . . . . . . . 8
8.4. What the Merkle Root Does Not Prove . . . . . . . . 8
9. Public Resolution . . . . . . . . . . . . . . . . . . . . . . 8
10. External Anchoring . . . . . . . . . . . . . . . . . . . . . 8
10.1. Anchor Verification Procedure . . . . . . . . . . . 8
10.2. Binding Does Not Transfer Claims . . . . . . . . . 8
11. Reference Implementation . . . . . . . . . . . . . . . . . . 8
12. Design Rationale . . . . . . . . . . . . . . . . . . . . . . 8
13. Security Considerations . . . . . . . . . . . . . . . . . . . 9
14. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 10
15. Related Work . . . . . . . . . . . . . . . . . . . . . . . . 10
16. Normative References . . . . . . . . . . . . . . . . . . . . 10
17. Informative References . . . . . . . . . . . . . . . . . . . 11
18. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . 11
Appendix A. Mapping Note: EU AI Act Article 12 . . . . . . . . 11
Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 11
1. Introduction
AI agents increasingly act through tool calls: they query prices,
send messages, modify files, and invoke services on a principal's
behalf. When something later needs checking -- what happened, when
it happened, and what the system actually observed -- the parties
involved usually have only vendor-specific logs, screenshots, or the
agent's own summary. None of these is portable across
implementations, and none lets an independent third party recompute
what was recorded.
AER-1 (AI Agent Execution Receipt, version 1) defines a small,
portable vocabulary for recording one agent tool call as a verifiable
receipt. The receipt answers a narrow question: what did this system
record for this execution? It does not prove an external business
outcome the system did not observe, and it does not turn a planned,
blocked, or preview action into a completed execution.
This document is an open proposal authored by Brennan Zambo. It is
not a claim to invent or own the broader execution-receipt category,
and it does not establish certification, registry membership,
universal adoption, or a finalized standards status. It is intended
Zambo Expires 12 April 2027 [Page 3]
Internet-Draft AER-1 October 2026
for discussion, implementation experiments, and interoperability
feedback. A reference implementation is deployed at the time of
writing, and its receipts are publicly verifiable as described in
Section 11.
2. Terminology
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.
This document also uses the following terms:
Execution: One invocation of one tool by or on behalf of an agent,
with its inputs, observed result, and timing.
Receipt: A public record for one execution with a stable identifier,
execution metadata, canonical bytes, and an output commitment, as
defined in Section 3.
Canonical bytes: The exact UTF-8 byte sequence over which the output
commitment was computed. Verifiers MUST receive or reproduce the
same bytes before comparing hashes.
Observed result: The result the implementation received and stored
for the execution. A receipt reports observation, not an
unsupported claim about the world beyond the observation.
Output commitment: A SHA-256 digest of the canonical bytes, written
"sha256:" followed by the lowercase hexadecimal digest.
3. The Receipt Record
A receipt is a JSON [RFC8259] object with the following members.
This is the smallest useful interoperable shape; implementations MAY
add fields, but additions MUST NOT make the core identity or
verification fields ambiguous.
Zambo Expires 12 April 2027 [Page 4]
Internet-Draft AER-1 October 2026
+========================+==========+===============================+
| Field | Required | Meaning |
+========================+==========+===============================+
| id | MUST | Stable UUID |
| | | identifying the |
| | | receipt. |
+------------------------+----------+-------------------------------+
| receipt_schema_version | MUST | Schema version. "0.3" |
| | | in the reference |
| | | implementation. Versions |
| | | compare numerically by |
| | | dot-separated components; |
| | | "0.4" or later means the |
| | | version is equal to or |
| | | greater than "0.4" under |
| | | that ordering. |
+------------------------+----------+-------------------------------+
| created_at | MUST | Timestamp of record |
| | | creation, in [RFC3339] |
| | | format. The timestamp MUST |
| | | denote a valid calendar date |
| | | and time; a verifier MUST |
| | | reject values such as |
| | | February 30 that match the |
| | | RFC 3339 syntax but are not |
| | | real dates, and MUST reject |
| | | leap-second values (a |
| | | seconds value of 60; see |
| | | Section 6). |
+------------------------+----------+-------------------------------+
| tool | MUST | Object naming the |
| | | tool: name, version, |
| | | and caller scope (for |
| | | example "public"). |
+------------------------+----------+-------------------------------+
| provenance_class | MUST | One of the provenance |
| | | classes in Section 5. |
+------------------------+----------+-------------------------------+
| canonical_bytes | MUST | Base64 encoding [RFC4648] |
| | | of the exact UTF-8 bytes |
| | | hashed for the output |
| | | commitment. |
+------------------------+----------+-------------------------------+
| output_hash | MUST | "sha256:" plus the |
| | | lowercase hex digest |
| | | of the decoded |
| | | canonical_bytes. |
+------------------------+----------+-------------------------------+
| metadata_hash | Cond. | "sha256:" plus the |
| | | lowercase hex digest of |
| | | the canonical metadata |
| | | block defined in |
| | | Section 3.1. REQUIRED |
| | | when receipt_schema_version |
| | | is "0.4" or later; a |
| | | verifier MUST recompute |
| | | and compare it whenever |
| | | present. |
+------------------------+----------+-------------------------------+
| verification_status | MUST | "verified" when the |
| | | stored record passes |
| | | the procedure in |
| | | Section 6. |
+------------------------+----------+-------------------------------+
| supersedes | OPTIONAL | Id of a prior receipt that |
| | | this receipt corrects |
| | | (Section 9). |
+------------------------+----------+-------------------------------+
Table 1: Core receipt members
The id member is a stable identifier for the receipt. Two
conformance tiers apply: the core tier and the strict tier. The
core rule (both tiers) is a lowercase UUID-shaped
string: eight, four, four, four, and twelve lowercase hexadecimal
characters separated by hyphens. Lowercase is load-bearing:
Section 8.1 hashes the UTF-8 bytes of the receipt identifier string
verbatim for the Merkle leaf, so a change of case changes the root.
A verifier MUST accept any id matching the core rule. The nil UUID
(00000000-0000-0000-0000-000000000000) MUST NOT be used as a receipt
id at any tier; a verifier MUST reject it. A producer MUST NOT
reuse an id for a different receipt; each receipt id identifies
exactly one receipt. The strict tier
additionally requires the id to be a UUID version 4 with [RFC4122]
variant bits; it applies when the receipt is issued under a program
that requires it (for example, a certification program), as
documented alongside the receipt. A verifier applying the strict
profile MUST reject an id that is not a version 4 UUID with [RFC4122]
variant bits.
The lexical form of the receipt id, in ABNF [RFC5234], is:
receipt-id = 8HEXDIG "-" 4HEXDIG "-" 4HEXDIG "-" 4HEXDIG
"-" 12HEXDIG
In this ABNF, HEXDIG is restricted to lowercase a-f for
alphabetic digits, per the core rule above. The digest string
form used by output_hash and metadata_hash is:
digest-string = "sha256:" 64HEXDIG
with the same lowercase restriction. Where the ABNF and the
prose of this section differ, the prose governs.
The public URL is not a stored member; it is a resolution rule.
Every receipt MUST be retrievable at a stable public URL without an
account, wallet, or token, as specified in Section 9. In the
reference implementation the URL is https://zambo.dev/run/<id>.
Zambo Expires 12 April 2027 [Page 5]
Internet-Draft AER-1 October 2026
Deployed records carry additional members outside the interoperable
core (for example caller identity, evidence references, external
anchor data, per-check results, side-effect declarations, and a
result preview). A verifier MUST NOT require them, and their
presence MUST NOT change the meaning of the core members. For
every optional member in this specification, absence means the
producer did not record the value; absence is never, by itself,
evidence of tampering.
The canonical_bytes member MAY be the empty string, which denotes a
tool execution that produced zero bytes of output. The output_hash
of empty canonical bytes is "sha256:" followed by
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855,
the SHA-256 digest of the empty string. A verifier MUST accept
empty canonical bytes when the output_hash matches, and MUST NOT
treat emptiness as a verification failure.
Example: the following shows the core fields of a real receipt issued
by the reference implementation (id 130da435-e157-498e-
af90-605866a86a27, a live_price call). The canonical bytes and
output hash are elided here to respect line-length limits; the
complete record is published at https://zambo.dev/run/130da435-e157-
498e-af90-605866a86a27, where the output hash was independently
recomputed from the decoded canonical bytes while preparing this
document and matches.
{
"id": "130da435-e157-498e-af90-605866a86a27",
"receipt_schema_version": "0.3",
"created_at": "2026-09-23T23:45:20.760Z",
"tool": {
"name": "live_price",
"version": "4.0.0",
"scope": "public"
},
"provenance_class": "EXECUTED BY ZAMBO",
"canonical_bytes": "(elided; full value at the URL above)",
"output_hash": "(elided; full value at the URL above)",
"verification_status": "verified"
}
3.1. Metadata Binding
The canonical metadata block is a JSON object with exactly the
following members: "created_at", "id", "output_hash",
"provenance_class", "receipt_schema_version", and "tool" (an object
with exactly "name", "scope", "version"). It is serialized with
member names sorted by Unicode code point at every object level,
including nested objects, no whitespace outside
strings, and the string escaping of Section 7.1. Every member
value in the block is a JSON string. The metadata_hash
member is "sha256:" followed by the lowercase hex SHA-256 digest of
the UTF-8 encoding of the block. Because the block includes the
receipt id and the output_hash, a metadata_hash copied from another
receipt does not verify. A producer MUST NOT emit duplicate
member names in the block, and a verifier MUST reject a block
that carries them (Section 6).
3.2. Execution Context
A receipt MAY carry an "execution_context" member recording the
source and version context behind the tool call: which model produced
the action, which source system was acted upon, and which agent
framework issued the call. This member is OPTIONAL and sits outside
the interoperable core defined in Table 1; a verifier MUST NOT
require it, and its presence MUST NOT change the meaning of the
core members. It is NOT part of the canonical metadata block in
Section 3.1 and MUST NOT be included in the metadata_hash
computation.
When present, execution_context is a JSON object whose members are
all OPTIONAL:
o "model": an object with "name" and "version" string members
identifying the AI model behind the tool call, when the producer
knows it (for example {"name": "grok-4", "version":
"2026-08-01"}). The model object MAY also carry a "digest"
member: "sha256:" plus the digest of the model weights or
serving configuration, as recorded by the producer.
o "source": a string identifying the source system the tool acted
upon, when the call targets a specific external system (for
example a database name, repository, or API endpoint).
o "framework": an object with "name" and "version" string members
identifying the agent framework that issued the tool call, when
applicable.
o "environment": a string, or an object with "name" and
"digest" members, identifying the compute environment the
call ran in, when the producer records it.
o "readback": an object recording a fresh-read check of an
earlier write, with members "receipt" (the id of the receipt
whose write is read back), "intended" (the intended change),
"write_result" (the recorded write outcome), "readback" (the
fresh-read outcome), and "mismatch" (a boolean: true when
the fresh read disagrees with the intended change). A
mismatch is a first-class recorded outcome of the readback
receipt, not an error in it.
o "payment": an object binding the execution to the payment
that authorized or paid for it, with members "method" (a
string naming the payment method, for example "x402") and
"reference" (a string carrying the settlement reference,
for example a transaction identifier). The member records
the reference as stated; it does not by itself prove
settlement.
Within execution_context, a member present with the JSON value
null means the value is known not to apply to this call; an
omitted member means the value is unknown or was not recorded.
Producers SHOULD use the explicit null for a known-inapplicable
member rather than omitting it. A verifier MUST NOT treat an
omitted member as a false or inapplicable value, and MUST NOT
treat absence, by itself, as evidence of tampering.
Because execution_context is outside the metadata_hash
computation, it is unauthenticated metadata: it is asserted by
the producer, and anyone holding the receipt can add, change,
or drop it without affecting any verification outcome under
Section 6. A verifier MUST surface it as producer-asserted
context, never as an attested fact.
Producers SHOULD include execution_context whenever the information
is available to them. The core "tool" member already names the tool
and its version; execution_context answers the adjacent questions of
which model, which source, and which framework surrounded the call.
Like all additional members, a verifier surfaces execution_context
as recorded without upgrading its claims: the receipt proves the
context was recorded as stated, not that the stated model or source
is accurate.
Consumers MAY compare the execution_context of related receipts
over time: a change in the recorded context between a receipt
and the current environment can serve as an invalidation signal
that prompts re-verification. Such a comparison changes no
verification verdict by itself.
3.3. Optional Input Commitment
A receipt MAY carry an "input_hash" member: "sha256:" plus the
lowercase hex digest of the exact tool-input bytes the producer
recorded for the execution, computed under the same exact-byte
rule as the output commitment (Section 4). The producer MUST
define and publish the byte layout of the recorded input (for
example, the argument object under a stated serialization)
alongside the receipt, mirroring the byte-layout rule of
Section 7.1; a verifier that cannot reproduce the
layout MUST NOT report the input commitment as confirmed.
input_hash is OPTIONAL and sits outside the interoperable core
defined in Table 1; it MUST NOT be included in the
metadata_hash computation, and a verifier MUST NOT require it.
Like every commitment in this specification, it proves the
input was recorded as stated, not that the recorded arguments
were the ones actually executed.
3.4. Additional Optional Members
The following members are OPTIONAL, sit outside the
interoperable core and outside the metadata_hash computation,
and are recorded as stated. A verifier MUST NOT require them
and MUST surface them without upgrading their claims.
o "field_provenance": an object mapping receipt member names
to the party that wrote the member's value: "actor" (the
agent or caller), "tool" (the executing tool or the system
behind it), or "counterparty" (a party other than the
actor, for example the system the tool acted upon). The
receipt-level provenance class (Section 5) states who ran
or reported the action as a whole; this member states, per
field, which values are externally written and which are
self-authored, so that a field that has only ever carried
one value is visible as such.
o "negative_record": an object with two OPTIONAL members,
"not_checked" and "not_run", each an array of strings
naming checks that were not performed and actions that were
not taken in connection with the execution. A receipt
records what ran; this member records the known blind spots
around it, so that a correctly executed action is not read
as a completely checked one.
o "reversal": an object recording whether the execution was
later undone, with members "state" (one of "none",
"reversed", or "compensated") and, when one exists,
"compensating_receipt" (the id of the receipt that records
the compensating execution). Unlike supersedes (Section
7.3), which corrects the record, this member records
compensation of the effect. It records the state as
stated; whether a reversal is permitted or enforced
belongs to the executing system, not to the receipt.
o "method_descriptor": an object identifying the method that
produced the committed bytes, with OPTIONAL members
"method" (a tool-specific method identifier, for example
a function selector or RPC method name),
"argument_encoding" (how the arguments were encoded), and
"decode" (how the raw result was decoded). Section 4
binds the exact bytes; this member names the question the
bytes answer, so receipts can be compared by method
without parsing tool-specific bytes.
4. Canonical Bytes and Output Commitment
Interoperability fails when every implementation gives a different
name to the same boundary, so the checkable core of a receipt is
deliberately small: an identifier locates the record, a creation time
orders it, a tool and caller scope identify the execution context,
and canonical bytes plus an output hash make the stored
representation checkable.
The producer MUST preserve the exact UTF-8 byte sequence that was
hashed, and MUST expose it (directly, or in a form from which a
verifier can reproduce it byte for byte) so that any party can
recompute the digest. The digest algorithm is SHA-256 [FIPS180-4].
A verifier that cannot obtain or reproduce the exact canonical bytes
MUST NOT report the output commitment as confirmed. A verifier
MUST decode the canonical bytes as strict UTF-8 and MUST reject
the receipt if the bytes are not valid UTF-8, even when the
base64 decoding and hash comparison would otherwise succeed.
Producers and verifiers MUST NOT apply Unicode normalization
(such as NFC or NFD) to the canonical bytes at any point:
normalization is a transformation, and it breaks the commitment
like any other (Section 13). Visually identical strings with
different UTF-8 byte sequences are different payloads and hash
differently, by design. The following vector pair is
specified for the conformance corpus. The four-character
string c, a, f,
U+00E9 (LATIN SMALL LETTER E WITH ACUTE), in NFC form, is the
five bytes 63 61 66 c3 a9, base64 "Y2Fmw6k=", with SHA-256
850f7dc43910ff890f8879c0ed26fe697c93a067ad93a7d50f466a7028a9bf4e.
The same visible string in NFD form (c, a, f, U+0065, U+0301
COMBINING ACUTE ACCENT) is the six bytes 63 61 66 65 cc 81,
base64 "Y2FmZcyB", with SHA-256
81ef060bcd98adc7824eb5c1ada83c32491b16018e11e79f00ab9d09e04b015a.
A receipt committing to either form does not verify against
the other: presenting the NFD bytes against the NFC commitment
is a verification failure, not a match. A verifier MAY offer
a documented strict mode that reports a non-NFC payload as
"rejected"; a verifier MUST NOT normalize a payload and then
report agreement.
Zambo Expires 12 April 2027 [Page 6]
Internet-Draft AER-1 October 2026
A result preview MAY accompany the receipt to give a human reader a
useful first read. A preview is not the source dataset, and a
verifier MUST NOT treat agreement with a preview as agreement with
the committed bytes.
Byte commitment alone does not identify the method that
produced the bytes: the same byte shape can answer different
questions under different methods. Section 3.4 defines an
optional structured method descriptor that names the method
alongside the committed bytes.
5. Provenance Classes
A whole job can combine actions performed by the recording system
itself, actions observed through an integrated gateway, and actions
reported by another agent. Those cases can all be useful, but they
do not support the same statement, so every receipt MUST name exactly
one provenance class. Verification MUST NOT upgrade a report into an
observation.
The three classes are:
EXECUTED BY ZAMBO: The recording system ran the tool and recorded
the returned result. The receipt attests to that recording act
and its stored output commitment, not to facts beyond what the
recording system recorded.
OBSERVED VIA GATEWAY: The recording system observed the action
through an integrated gateway. The receipt attests to the gateway
observation, not to facts beyond what the gateway returned.
LOGGED BY AGENT: An external agent reported the action through a
journal interface. The receipt attests that the report was
received, redacted, timestamped, and chained. The recording
system does not claim it ran or observed the action.
The first label names the recording system; its interoperable content
is the definition, and another implementation substitutes its own
system name in that position. The second and third labels are fixed
strings.
Provenance is a separate design axis from the receipt envelope. Two
receipts with identical envelopes can carry different provenance, and
a verifier MUST surface the provenance class alongside every other
field when presenting a receipt. Section 3.4 defines an optional
per-field provenance member that complements this receipt-level
class.
6. Verification Procedure
To verify a receipt, a verifier performs the following steps:
1. Resolve the public receipt URL and confirm the id in the
retrieved record matches the requested execution.
2. Read the exact canonical bytes, or the representation needed to
reproduce them byte for byte.
Zambo Expires 12 April 2027 [Page 7]
Internet-Draft AER-1 October 2026
3. Compute SHA-256 over those bytes and compare the result with
output_hash.
4. Check the provenance class (Section 5) and confirm it is one
of the three defined values. Confirm the created_at timestamp
denotes a valid calendar date and time. This check establishes
form and calendar validity only, not that the stated time is
true (Section 13). When the receipt carries
metadata_hash, or when receipt_schema_version is "0.4" or later,
recompute the metadata block from the receipt's stored member
values per Section 3.1 and compare; a
mismatch MUST cause verification to fail. A "0.4" receipt
without metadata_hash MUST fail. A "0.3" receipt without
metadata_hash verifies under the prior rules, but its tool name,
version, scope, creation time, and provenance class are NOT bound
to the output commitment. If anchors are published for the
receipt (Section 10), verify each anchor using the procedure in
Section 10.1; an anchor that fails verification MUST NOT be
reported as verified, but anchor failure MUST NOT by itself
cause the receipt's output commitment check to fail. Report the
tool name, timestamp, caller scope, provenance class, and
per-anchor outcomes alongside the hash check result, each
labeled with what was actually checked.
5. Report only what the stored observation supports. A passing
hash confirms the bytes match the commitment; it does not
confirm anything the system did not observe. Report outcomes
in two classes: "verification failed" when a recomputed value
does not match the committed value (hash mismatch, Merkle root
mismatch, anchor digest mismatch), and "rejected: <reason>"
when the record cannot be fully evaluated for a structural,
policy, or resource reason (duplicate receipt_id, size limit,
unresolvable record, unreproducible bytes). A rejection MUST
NOT be reported as "verification failed," since the record may
verify under different conditions.
Two structural rules apply before any digest comparison. A
producer MUST NOT emit duplicate member names in any JSON
object whose contents are covered by a digest in this
specification (the receipt object, the canonical metadata
block of Section 3.1, and chain entries under Section 7.1),
and a verifier MUST reject a receipt in which any such object,
as received, contains duplicate member names: a parser that
silently keeps one value would let one byte string carry two
meanings. A verifier MUST also reject any timestamp it is
required to validate whose seconds value is 60 (for example
"2026-06-30T23:59:60Z"): leap-second validity cannot be
checked offline, so leap-second timestamps are not accepted.
These outcomes are reported as "rejected: duplicate member
name" and "rejected: leap-second timestamp" under the outcome
classes of step 5, and a negative vector for each is
specified for the conformance corpus.
A machine-readable verifier SHOULD expose the same procedure over
HTTP, returning the receipt identifier, verification status, and
output hash, and signaling failure with a non-success status when the
record does not verify. In the reference implementation, an HTTP GET
on https://zambo.dev/api/receipt/<id>/verify returns a JSON object
containing at least id, verification_status, and output_hash.
7. Hash-Chained Job Timelines
Entries for one job form an append-only hash chain. Each entry
carries a prev_digest member whose value is the lowercase
hex-encoded SHA-256 entry digest of the previous entry, computed as
defined in Section 7.1. Because the entry digest binds the entry's
sequence position, job identifier, closing flag, receipt identifier,
tool name, provenance class, and output commitment, relabeling an
entry's metadata, moving the closing flag, or splicing entries
across jobs breaks the links and is detectable by recomputation.
The conformance kit exposes this as a chain-validity check alongside
the other verification checks.
7.1. The Entry Digest
The entry digest of a chain entry is SHA-256 over the UTF-8
encoding of a canonical JSON object with exactly the following
members. Member names are sorted by Unicode code point; the
serialization emits no whitespace outside strings. The member list
below is in description order. The serialization order is the
code-point sort: close, id, job_id, output_hash, prev_digest,
provenance_class, seq, tool:
prev_digest: the entry's prev_digest member, a string.
seq: the entry's sequence number, a JSON number with an integer
value. The representations 1 and 1.0 are equivalent; both are
accepted, and the digest serializes the value in integer form
(1, never 1.0). A verifier MUST reject a non-integer number
(such as 1.5), a boolean, a string, null, or a missing seq.
job_id: the entry's job identifier, a string.
close: the entry's close member if present; if the member is
absent, the boolean false.
id: the entry's receipt identifier, a string.
tool: the entry's tool name, a string.
provenance_class: the entry's provenance class, a string.
output_hash: the lowercase hex-encoded SHA-256 digest of the raw
bytes obtained by base64-decoding the entry's canonical_bytes
member.
Strings are serialized as JSON strings with the following exact
escaping, which matches the output of Python's json.dumps with
ensure_ascii=True: the quotation mark (U+0022) and reverse solidus
(U+005C) are escaped as \" and \\; U+0008, U+0009, U+000A, U+000C,
and U+000D are escaped as \b, \t, \n, \f, and \r; and every other
code point outside the range U+0020 to U+007E, including U+007F
(DELETE), is escaped as \u followed by four lowercase hexadecimal
digits, using a surrogate pair for code points above U+FFFF. No
other escaping is used. A verifier MUST decode canonical_bytes
with strict base64 [RFC4648] and fail closed: any input that is
not valid base64 causes chain verification to fail rather than
being skipped or repaired.
7.2. Chain Rules
A timeline is a non-empty JSON array of chain entries. A verifier
MUST reject an empty timeline. Each entry
carries canonical_bytes (a base64 string), prev_digest (a string),
seq (an integer), job_id, id, tool, and provenance_class
(non-empty strings), and optionally close.
Genesis. The first entry's prev_digest MUST be 64 zero characters
("0" repeated 64 times), the conventional zero digest. A verifier
MUST reject a first entry carrying any other prev_digest value.
Sequence. The first entry's seq MUST be 1, and each subsequent
entry's seq MUST be exactly one greater than the previous entry's
seq. A verifier MUST reject a timeline with a gap, a duplicate,
or an out-of-order sequence number.
One job. Every entry in a timeline MUST carry the same job_id. A
verifier MUST reject a timeline whose entries name different jobs;
entries cannot be spliced across jobs without breaking the chain.
Closing. The close member, when present, MUST be a boolean. The
value true MUST appear only on the last entry of the timeline, and
the last entry MUST carry close set to true. A verifier MUST fail
a timeline whose last entry is not a closing entry, and MUST fail
a timeline carrying close set to true on any earlier entry.
Links. A verifier MUST recompute the Section 7.1 entry digest of
every entry and confirm that each entry after the first carries a
prev_digest equal to the recomputed digest of the entry before it.
A verifier MUST NOT accept a link on the basis of the stored
prev_digest values alone.
7.3. What the Chain Does Not Prove
Recomputation detects relabeling, insertion, deletion, and
reordering only when the attacker cannot rebuild the links. An
attacker who removes entries from either end of a timeline and
recomputes a complete, valid shorter chain produces a timeline
that verifies under the rules above: the chain mechanics alone
cannot distinguish a rebuilt truncation from a job that genuinely
ended earlier or started later. This is an honest limit of hash
chaining, not a defect in the digest.
Truncation resistance needs a commitment outside the chain: a
witness, a timestamped head, an anchor, or a workflow manifest
that binds the expected step list or step count. Deployments that
need to detect truncation MUST publish such a commitment and
verify the timeline against it. The chain check alone MUST NOT be
presented as truncation-proof.
That commitment SHOULD bind the digest of the final chain entry,
not merely the expected step count. Every entry except the last is
bound by the following entry's prev_digest; the final entry's digest
is referenced by nothing inside the chain. An attacker who rewrites
the final entry's content without changing the entry count produces
a chain that still verifies. A commitment that records the final
entry digest detects this; a commitment that records only the count
does not.
To make such commitments interoperable, a deployment MAY publish a
chain commitment: a JSON document separate from the chain with the
following members:
job_id: the job identifier the timeline binds, a string matching
the job_id of every entry in the timeline.
final_entry_digest: the lowercase hex-encoded SHA-256 entry digest
of the timeline's last entry, recomputed as in Section 7.1, a
string of 64 lowercase hexadecimal characters.
entry_count: the number of entries in the timeline, a JSON number
with an integer value.
published_at: the time the commitment was published, an RFC 3339
timestamp denoting a valid calendar date and time.
The commitment MUST be published outside the chain: it binds the
digest of the final entry, and recording it inside the final entry
would be circular. A verifier checks a chain commitment by
recomputing the final entry digest as in Section 7.1 and comparing
it with final_entry_digest, comparing entry_count with the number of
entries in the timeline, and confirming job_id matches the
timeline's job identifier. The conformance kit implements this
check; a commitment that fails it MUST NOT be presented as binding
the timeline.
As with receipts, chaining provides tamper evidence for the
sequence, not truth about the world. A chained entry with LOGGED
BY AGENT provenance remains a chained report; the chain upgrades
nothing about what the entry attests. A public page resolving the
whole timeline reports whether the visible chain verifies.
7.4. Versioning
The construction in this section is the -10 chain construction,
which is identical to the -07/-08/-09 construction; the -11,
-12, -13, and -14 revisions change no chain verification rule.
The -10
revision precisely specifies the Section 7.1 string escaping; it
changes no verification rule. The -08 revision
adds guidance in Section 7.3 that the external truncation commitment
SHOULD bind the final entry digest; it changes no verification rule.
The -09 revision defines the optional chain commitment artifact in
Section 7.3, which the conformance kit verifies; it changes no chain
verification rule. The -07 construction differs from the -06
construction, whose entry digest covered only the base64-decoded
canonical_bytes. A verifier MUST distinguish the -06 construction
from the -07/-08/-09/-10/-11/-12/-13/-14 construction: a
timeline that
verifies under the -06 construction MUST NOT be reported as
verifying under the -07/-08/-09/-10/-11/-12/-13/-14
construction. The
-06 construction is retained for historical chains only; new
timelines MUST use the -07/-08/-09/-10/-11/-12/-13/-14
construction, and
historical -06 chains MUST be labeled historical
and MUST NOT verify as current.
8. Workflow Receipts
A single execution receipt (Section 3) records one tool call. When
an agent performs several tool calls toward one goal, the sequence
itself needs a verifiable record. A workflow receipt binds an
ordered list of step receipts to a single goal, with a Merkle root
over the step sequence and an output hash for the workflow's final
result.
A workflow receipt is a JSON object with the following members.
This is the smallest useful interoperable shape; implementations MAY
add fields, but additions MUST NOT make the core identity or
verification fields ambiguous.
+========================+==========+===============================+
| Field | Required | Meaning |
+========================+==========+===============================+
| type | MUST | "verifiable-workflow- |
| | | receipt". |
+------------------------+----------+-------------------------------+
| version | MUST | Workflow receipt schema |
| | | version. "1" in the |
| | | reference implementation. |
+------------------------+----------+-------------------------------+
| workflow_id | MUST | Stable UUID identifying the |
| | | workflow. |
+------------------------+----------+-------------------------------+
| receipt_id | MUST | Stable UUID identifying this |
| | | workflow receipt record. |
| | | Distinct from workflow_id. |
+------------------------+----------+-------------------------------+
| session_id | MUST | Identifier for the session |
| | | that produced the workflow. |
+------------------------+----------+-------------------------------+
| goal | MUST | Human-readable description |
| | | of the goal the workflow |
| | | pursued. |
+------------------------+----------+-------------------------------+
| status | MUST | Overall workflow status, |
| | | for example "ok" or "error". |
+------------------------+----------+-------------------------------+
| steps | MUST | Non-empty ordered array |
| | | of step objects (see |
| | | below), ordered by |
| | | execution sequence. An |
| | | empty array MUST be |
| | | rejected by a verifier. |
+------------------------+----------+-------------------------------+
| merkle_root | MUST | Hex-encoded SHA-256 Merkle |
| | | root over the ordered step |
| | | receipt identifiers |
| | | (Section 8.1). |
+------------------------+----------+-------------------------------+
| output_hash | MUST | Hex-encoded SHA-256 digest |
| | | of the workflow's final |
| | | output bytes |
| | | (Section 8.1). |
+------------------------+----------+-------------------------------+
| verify_url | MUST | Stable public URL where the |
| | | workflow receipt can be |
| | | retrieved and checked |
| | | without an account, wallet, |
| | | or token. |
+------------------------+----------+-------------------------------+
Table 2: Workflow receipt members
Each step object in the steps array has the following members:
+========================+==========+===============================+
| Field | Required | Meaning |
+========================+==========+===============================+
| seq | MUST | 1-based sequence number |
| | | within the workflow, |
| | | starting at 1 with no gaps. |
+------------------------+----------+-------------------------------+
| tool | MUST | Name of the tool invoked for |
| | | this step. |
+------------------------+----------+-------------------------------+
| receipt_id | MUST | UUID of the execution |
| | | receipt for this step |
| | | (Section 3). |
+------------------------+----------+-------------------------------+
| receipt_hash | MUST | Hex-encoded SHA-256 digest |
| | | of the canonical bytes of |
| | | the step receipt (the same |
| | | bytes verified in |
| | | Section 6), as retrieved |
| | | at workflow assembly time. |
+------------------------+----------+-------------------------------+
| started_at | MUST | Timestamp when the step |
| | | began, in RFC 3339 format. |
+------------------------+----------+-------------------------------+
| ended_at | MUST | Timestamp when the step |
| | | completed, in RFC 3339 |
| | | format. |
+------------------------+----------+-------------------------------+
| status | MUST | Step status, for example |
| | | "ok" or "error". |
+------------------------+----------+-------------------------------+
| receipt_url | OPTIONAL | Public URL where the step |
| | | receipt can be retrieved. |
+------------------------+----------+-------------------------------+
Table 3: Workflow step members
The public URL is not a stored member beyond verify_url; it is a
resolution rule. Every workflow receipt MUST be retrievable at its
verify_url without an account, wallet, or token, as specified for
execution receipts in Section 9. In the reference implementation
the URL is https://zambo.dev/workflow/<workflow_id>.
Example: the following shows the core fields of a real workflow
receipt issued by the reference implementation (workflow_id
42a3c2cf-2cdd-5b8a-aced-016e5a2fb634, goal "get BTC price", five
steps). The complete record is published at
https://zambo.dev/workflow/42a3c2cf-2cdd-5b8a-aced-016e5a2fb634.
{
"type": "verifiable-workflow-receipt",
"version": "1",
"workflow_id": "42a3c2cf-2cdd-5b8a-aced-016e5a2fb634",
"receipt_id": "442243d2-9c32-53f7-817e-2def28fa5b07",
"session_id": "receipt-test-001",
"goal": "get BTC price",
"status": "ok",
"steps": [
{
"seq": 1,
"tool": "zambo_universal",
"receipt_id": "e80168eb-a6a1-4ec9-8478-ac108f9397ff",
"receipt_hash":
"11c3a071051a42cbf5cf4da60a7577b1dffeb0419e6dd8a258b19c4c0d0d108e",
"started_at": "2026-09-29T10:57:40.911Z",
"ended_at": "2026-09-29T10:57:41.147Z",
"status": "ok"
},
{
"seq": 2,
"tool": "zambo_universal",
"receipt_id": "514fe841-bbdd-4804-9ef4-6828842d8763",
"receipt_hash":
"68e3bcad3f46218672fd7ef7bf32f4a7089937dda2b8ade3cfe1050cec856285",
"started_at": "2026-09-29T10:57:42.920Z",
"ended_at": "2026-09-29T10:57:43.069Z",
"status": "ok"
},
{
"seq": 3,
"tool": "zambo_universal",
"receipt_id": "a090bdad-f2cf-48c9-bc89-e9e17700fb15",
"receipt_hash":
"3da48b2d392fb79ed78f7ed0cc8e6c122fc1089947ff6d7f829748d7745840dd",
"started_at": "2026-09-29T10:57:45.415Z",
"ended_at": "2026-09-29T10:57:45.564Z",
"status": "ok"
},
{
"seq": 4,
"tool": "zambo_universal",
"receipt_id": "bf88a036-416b-456a-8e82-8c0991811d68",
"receipt_hash":
"0f024f209392b918909df5478aadd3a9189520d74cfdf78e2ccf51b9f075f926",
"started_at": "2026-09-29T10:57:48.459Z",
"ended_at": "2026-09-29T10:57:48.605Z",
"status": "ok"
},
{
"seq": 5,
"tool": "zambo_universal",
"receipt_id": "ae795bf0-27f8-4cf2-8aef-3f8142b77217",
"receipt_hash":
"f52d9c03605047ad0ac347e5e9cbc0e2e197a009d8ee5c7d775f86f360eca963",
"started_at": "2026-09-29T10:57:55.227Z",
"ended_at": "2026-09-29T10:57:55.434Z",
"status": "ok"
}
],
"merkle_root":
"7c4817ca249edfeae259b98abf63ed38a31d9dbeebf5ee23139410a7a7f63b37",
"output_hash":
"16566e46433dd9a00e4437873102531be71fbae3872db8888b7ddc9282d90f54",
"verify_url":
"https://zambo.dev/workflow/42a3c2cf-2cdd-5b8a-aced-016e5a2fb634"
}
The merkle_root above recomputes from the five step receipt_id
values with the Section 8.1 construction: the leaf for each step is
SHA-256 over the UTF-8 bytes of its receipt_id string, paired and
hashed up the tree with last-duplication on odd levels. The
complete live record remains published at the verify_url above.
8.1. Merkle Root and Output Hash
The merkle_root binds the ordered step sequence so that insertion,
deletion, reordering, or substitution of steps is detectable by
recomputation. It is a lowercase hex-encoded SHA-256 digest
computed over the ordered step receipt identifiers.
The Merkle tree construction is a binary Merkle tree, specified
as follows:
1. For each step in order, compute the leaf digest as SHA-256
over the UTF-8 bytes of the step's receipt_id string.
2. Pair adjacent digests in order; for each pair, compute SHA-256
over the concatenation of the two 32-byte digests. If the
number of digests at any level is odd, duplicate the last
digest before pairing.
3. Repeat step 2 on the resulting digests until one digest
remains. Hex-encode that digest in lowercase; this is the
merkle_root.
With a single step, the leaf digest is the merkle_root; no pairing
occurs. (The steps array is always non-empty per Section 8.)
An implementation MUST use the construction specified above. The
alternative-construction permission from earlier revisions is
removed: permitting divergent constructions allows different
Merkle roots for the same workflow, which breaks
cross-implementation verification of the step sequence. The
construction in this section is the only permitted one; a verifier
recomputes the root from the steps array alone.
The output_hash is a lowercase hex-encoded SHA-256 digest of the
canonical bytes representing the workflow's final output. The
producer MUST define and publish the exact byte layout used (for
example, the canonical JSON of a documented final-output document)
at the verify_url. If the producer publishes no layout, the bytes
are the Section 7.1 serialization of {"output": <final-output>}.
A verifier that cannot obtain or reproduce those bytes
deterministically from published artifacts alone MUST NOT report
the output hash as confirmed.
A verifier recomputes each step's receipt_hash from the step receipt
retrieved in Section 8.2 step 2 and MUST report a mismatch as a
step-binding failure, distinct from a Merkle root failure.
8.2. Workflow Verification Procedure
To verify a workflow receipt, a verifier performs the following
steps:
1. Resolve the verify_url and confirm the workflow_id in the
retrieved record matches the requested workflow.
2. For each step in the steps array, in order, retrieve the step's
receipt: at the step's receipt_url when present, otherwise at
the publisher's documented URL scheme for step receipts, and
run the Section 6 verification procedure on the step receipt.
A verifier that cannot locate a step receipt MUST NOT report
the workflow receipt as verified. Every step receipt MUST
verify independently; a workflow receipt MUST NOT be reported
as verified if any constituent step receipt fails verification.
3. Confirm the steps array is ordered by seq starting from 1 with
no gaps and no duplicate sequence numbers. Confirm that no two
steps carry the same receipt_id value. A verifier MUST reject
a workflow receipt whose steps array contains the same
receipt_id value more than once.
This rule is structural, not cosmetic. With the Section 8.1
odd-level duplication rule, the step list [r1, r2, r3, r4, r5]
and the step list [r1, r2, r3, r4, r5, r5] recompute to the
identical Merkle root, so the root comparison in step 4 cannot
distinguish them. Rejecting duplicate receipt_id values closes
this collision class at the verification layer. For the
Section 8 example workflow, the six-step list that repeats
the fifth step's receipt_id recomputes to the identical root
(7c4817ca...f63b37); the conformance corpus carries that
pair as the isolating vector for this rule.
4. Recompute the merkle_root from the ordered step receipt_id
values using the construction documented for the receipt
(Section 8.1), and compare it to the published merkle_root.
5. Review the goal, status, timestamps, and output hash separately
from the structural checks.
6. Report only what the stored observations support. A passing
Merkle root confirms the step sequence is intact as recorded;
it does not confirm anything the individual step receipts did
not observe. Provenance classes (Section 5) apply per step;
the workflow receipt does not upgrade the provenance of its
steps.
A machine-readable verifier SHOULD expose the same procedure over
HTTP, returning the workflow identifier, verification status, the
per-step outcomes, and the Merkle root check result, and signaling
failure with a non-success status when the record does not verify.
8.3. Bidirectional Linking Between Steps and Workflows
When a step receipt belongs to a workflow, the step receipt's
public page MUST reference the parent workflow, and the workflow
receipt MUST reference each constituent step receipt. This lets a
reviewer start from any single action and reach the full goal
context, or start from the goal and drill into any individual
action, without needing session identifiers or internal system
knowledge.
Specifically:
* The public page for a step receipt that is part of a workflow
SHOULD display a reference to the parent workflow, including a
link to the workflow's verify_url. In the reference
implementation this appears as "Part of workflow" with a link
to https://zambo.dev/workflow/<workflow_id>. Whether or not
the page displays such a reference, any published
step-to-workflow linkage MUST be byte-testable: the referenced
workflow receipt's steps array MUST include that step's
receipt_id, as required in the next bullet, and a verifier
resolving the workflow's verify_url MUST be able to reach the
step receipt by that receipt_id.
* The workflow receipt's steps array MUST include the receipt_id
of every step in the workflow, and the public workflow page
SHOULD link each step entry to its receipt page.
* A step receipt that is not part of any workflow MUST NOT display
a workflow reference. The absence of the link is not an error
and MUST NOT be treated as a verification failure.
Linking is presentational and navigational; it does not alter the
receipt bytes, the output commitment, or the Merkle root. Adding or
removing a link MUST NOT invalidate verification.
8.4. What the Merkle Root Does Not Prove
Recomputation detects insertion, deletion, reordering, and
substitution of steps only when the attacker cannot rebuild the
root. An attacker who removes steps from the steps array and
recomputes a complete, valid Merkle root over the shorter list
produces a workflow receipt that verifies under Section 8.2: the
Merkle root alone cannot distinguish a rebuilt truncation from a
workflow that genuinely contained fewer steps. This is the same
honest limit stated for hash chains in Section 7.3, applied to the
step sequence.
Truncation resistance for a workflow needs a commitment outside the
receipt: an external anchor binding the merkle_root (Section 10), or
a workflow manifest published before execution that binds the
expected step list or step count. Deployments that need to detect
step truncation MUST publish such a commitment and verify the
workflow receipt against it. The Merkle root check alone MUST NOT
be presented as truncation-proof.
That commitment SHOULD bind the merkle_root itself, not merely the
expected step count. As with chain commitments (Section 7.3), a
count-only commitment does not detect content substitution within
the same count. A verifier checks a workflow anchor by recomputing
the merkle_root as in Section 8.1 and comparing it with the anchored
value, as specified in Section 10.
8.5. Workflow Close Tool
The reference implementation provides a workflow_close tool that
seals a session's workflow and issues the workflow receipt
described in Section 8.
The tool takes a single input: _session_id, the session identifier
grouping the workflow's steps.
The tool returns a JSON object with the following members:
o workflow_id: Stable UUID identifying the workflow.
o count: Number of step receipts in the workflow.
o merkle_root: Hex-encoded SHA-256 Merkle root over the ordered
step receipt output hashes, computed as in Section 8.1.
o children: Ordered array of objects, each with id (the step
receipt UUID) and output_hash (hex-encoded SHA-256 digest of
the step's output bytes).
o message: Human-readable confirmation, for example "recorded 3
receipts for this session".
o verification_status: Always "not_verified" for the summary
itself. The summary records; it does not verify.
o issuer_authenticated: Boolean indicating whether the issuer
was authenticated.
The tool is idempotent on data: repeated invocations return the
same count, merkle_root, and children. Each invocation mints its
own execution receipt, as the close action is itself a recorded
tool call. The summary receipt is excluded from the workflow's
step count (the meta-receipt rule): a workflow_close receipt never
appears in its own workflow's steps array.
8.6. Workflow Metadata in Step Receipts
Each execution receipt that is part of a workflow carries a
workflow object with the following members:
o id: The workflow_id UUID.
o url: Stable public URL where the workflow can be retrieved,
https://zambo.dev/workflow/<workflow_id>.
o step_index: Zero-based position of this receipt in the
workflow's execution order.
The workflow_id is unbound metadata: it is NOT included in the
receipt's canonical_bytes and does not affect the output_hash.
The authoritative binding of workflow membership is the
workflow_close summary receipt's children array together with
the merkle_root (Section 8.5).
8.7. Workflow View API
The workflow receipt and its steps are retrievable as JSON at:
GET /api/workflow/<workflow_id>
Both the workflow_id and the id forms resolve identically.
The response is a JSON object with the following members:
o id: The workflow_id.
o workflow_id: Same as id.
o session_hash: Hex-encoded SHA-256 hash of the session
identifier.
o count: Number of step receipts in the workflow.
o first_at: RFC 3339 timestamp of the earliest step.
o last_at: RFC 3339 timestamp of the latest step.
o merkle_root: Hex-encoded SHA-256 Merkle root over the ordered
step receipt output hashes (Section 8.1).
o receipts: Ordered array of step receipt objects, each with id,
tool, status, timestamp (RFC 3339), receipt_url, output_hash
(hex-encoded SHA-256), and step_index (zero-based).
The workflow view excludes workflow_close summary receipts per
the meta-receipt rule (Section 8.5).
9. Public Resolution
Every receipt MUST be retrievable at its public URL without an
account, wallet, or token. The URL is stable: once published, the
record at that URL MUST NOT be altered. Corrections are published as
new receipts that carry the superseded id in the supersedes member;
they MUST NOT rewrite history at the original URL.
The public page SHOULD present the receipt fields, the provenance
class, the verification outcome, and the hash chain position in
human-readable form alongside the raw JSON.
10. External Anchoring
A receipt MAY be bound to one or more external anchors after
issuance. Anchoring is additive: it does not alter the receipt
bytes and MUST NOT invalidate the output commitment.
An anchor is a JSON object with the following members:
+========================+==========+===============================+
| Field | Required | Meaning |
+========================+==========+===============================+
| type | MUST | "aer1-anchor". |
+------------------------+----------+-------------------------------+
| version | MUST | Anchor schema version. "1" |
| | | in this specification. |
+------------------------+----------+-------------------------------+
| receipt_id | MUST | Identifier of the anchored |
| | | record. For a single |
| | | receipt, the receipt's id |
| | | member (Section 3). For a |
| | | workflow receipt, the |
| | | workflow receipt's |
| | | receipt_id member |
| | | (Section 8); this is the |
| | | record identifier, not the |
| | | workflow_id. For a job |
| | | timeline, the job_id of the |
| | | chain commitment |
| | | (Section 7.3). |
+------------------------+----------+-------------------------------+
| anchored_digest | MUST | The anchored digest as 64 |
| | | lowercase hexadecimal |
| | | characters, with no |
| | | algorithm prefix. For a |
| | | single receipt: the hex |
| | | digest carried by the |
| | | output_hash member |
| | | (Section 3), i.e. the |
| | | output_hash value with the |
| | | leading 'sha256:' removed. |
| | | For a workflow receipt: the |
| | | merkle_root value |
| | | (Section 8.1). For a job |
| | | timeline: the |
| | | final_entry_digest of the |
| | | chain commitment (Section |
| | | 7.3). |
+------------------------+----------+-------------------------------+
| anchor_type | MUST | One of "nostr", "ots", |
| | | "witness", "scitt", or |
| | | "rfc3161". See below. |
+------------------------+----------+-------------------------------+
| anchored_at | MUST | RFC 3339 timestamp of |
| | | anchor publication. |
+------------------------+----------+-------------------------------+
| proof | MUST | Anchor-type-specific proof |
| | | object (see below). |
+------------------------+----------+-------------------------------+
| record_url | OPTIONAL | Public URL where the anchored |
| | | record can be retrieved. |
+------------------------+----------+-------------------------------+
Table 4: Anchor members
The proof object for anchor_type "nostr" carries: event_id (the
Nostr event identifier, 64 lowercase hex characters), pubkey (the
publisher's public key, 64 lowercase hex characters), and relays
(an array of relay URLs where the event was published, each a
non-empty string).
The proof object for anchor_type "ots" carries: ots_proof (a
base64-encoded OpenTimestamps proof) or ots_request (a
base64-encoded pending OTS request, with status "pending").
The proof object for anchor_type "witness" carries: witness_url (a
stable public URL where the witness record can be retrieved
without an account) and witness_statement (a human-readable
description of what the witness attests).
The proof object for anchor_type "scitt" carries: service (the
URL of the SCITT Transparency Service with which the
anchored_digest was registered as the payload digest of a
Signed Statement [RFC9943]) and cose_receipt (a base64-encoded
COSE receipt: the inclusion proof the service returned for
that registration).
The proof object for anchor_type "rfc3161" carries:
timestamp_token (a base64-encoded DER TimeStampToken issued
by an RFC 3161 timestamp authority over the anchored_digest)
[RFC3161].
The anchored_digest member has the lexical form
anchored-digest = 64HEXDIG, with the lowercase restriction
stated for digest strings in Section 3.
Privacy bound. An anchor MUST NOT contain step content, tool
inputs, tool outputs, canonical bytes, or any data beyond the
members in Table 4. The anchored_digest is a hash; hashes of
low-entropy or guessable inputs may be vulnerable to dictionary
attacks, and verifiers and publishers MUST NOT treat
anchoring as a confidentiality mechanism.
What anchoring proves. A verified anchor proves that the
anchored_digest existed no later than the anchor's publication time.
The proof lives on infrastructure outside the receipt itself: Nostr
relays, the Bitcoin blockchain, or a witness's publication point.
Verification confirms the proof is present on that infrastructure
and is correctly signed against proof.pubkey. It does not establish
who controls the signing key. A verifier MUST NOT present a
verified anchor as proof that the witness is independent of the
receipt publisher unless that independence is established out of
band (for example, a well-known witness key or a key the verifier
already trusts). A "witness" anchor whose witness record is
publisher-controlled provides no independent witnessing; it binds
only the digest to the publisher's own statement at the anchor time.
Anchoring does not prove the receipt's content was correct,
authorized, or truthful; it does not prove the publisher's identity
beyond what the anchor_type's own authentication provides; and it
does not by itself prove the absence of truncation (Section 7.3,
Section 8.4) unless the verifier also confirms the anchored digest
matches a recomputation over the complete record.
Anchor discovery. Anchor references are published alongside the
receipt at its public URL (Section 9) or at a documented anchor
index keyed by receipt_id, outside the frozen receipt bytes: adding
an anchor
reference to the public page is presentational (Section 8.3) and
does not alter the published record. A verifier obtains the anchor
references from that location before running the Section 10.1
procedure; a receipt for which no anchors are published verifies
without anchor checks.
10.1. Anchor Verification Procedure
To verify an anchor, a verifier performs the following steps:
1. Confirm the anchor's receipt_id matches the identifier of the
record being verified per Table 4: the receipt id for a single
receipt, the workflow receipt_id for a workflow receipt, or the
chain commitment job_id for a job timeline.
2. Recompute the expected digest: for a single receipt, the hex
digest carried by output_hash (with the "sha256:" prefix
removed) per Section 6; for a workflow receipt, the
merkle_root per Section 8.1; for a chain, the final_entry_digest
per Section 7.3. Compare it with anchored_digest. A mismatch
MUST cause anchor verification to fail.
3. Verify the proof according to its anchor_type: for "nostr",
resolve the event_id at one or more of the listed relays,
verify the event's Nostr signature against proof.pubkey, and
confirm the event content carries the same receipt_id and
anchored_digest; for "ots", verify the timestamp proof against
the Bitcoin block header it commits to; for "witness", retrieve
the witness_url and confirm the retrieved statement contains
the anchored_digest string verbatim; for "scitt", verify the
COSE receipt against the Transparency Service's published key
and confirm the registered statement's payload digest equals
anchored_digest; for "rfc3161", validate the TimeStampToken
and confirm its message imprint is the anchored_digest.
4. Report the anchor as verified only if steps 1 through 3 all
pass. A verified anchor confirms the digest existed at the
anchor time; it does not upgrade the receipt's provenance class
(Section 5).
A workflow receipt MUST be retrievable by its receipt_id member as
well as by its workflow_id at its verify_url, so that either
identifier locates the same record. When an anchor carries a
record_url, retrieving that URL MUST return the anchored record,
so that the anchor reference alone suffices to locate it.
10.2. Binding Does Not Transfer Claims
Anchoring a digest (Section 10), including a receipt in a Merkle
tree or workflow (Section 8), chaining receipts (Section 7), or
referencing one record from another binds the records for integrity
checking. Binding does not transfer claims between the bound
records, in either direction. A verified anchor does not make the
anchored content true, authorized, or correctly recorded; a valid
receipt bound to an external assertion (for example, a claim about a
physical outcome) neither proves that assertion nor is invalidated
by the assertion's falsity; and a container receipt does not upgrade
the provenance class of its members (Sections 5, 8.2, 10.1). A
verifier MUST evaluate each bound record under that record's own
verification procedure and MUST NOT report a claim as verified
solely because it is bound to a verified record.
11. Reference Implementation
A reference implementation of this vocabulary is deployed at the time
of writing, described at [AER-1-HOME]. The implementation exposes
the receipt contract through one MCP endpoint and gives successful
calls a public receipt page:
* Tool calls are made over HTTPS as JSON-RPC to
https://zambo.dev/api/mcp using the tools/call method. The
response contains the receipt identity and the public verification
link.
* Each receipt resolves at https://zambo.dev/run/<id> and can be
opened and checked by anyone, with no account or token.
* A verifier endpoint returns the machine-readable verification
result for a receipt identifier, as described in Section 6.
The endpoint and the receipt pages are independent live records:
anyone can reproduce the Section 6 procedure against them today.
Their availability is a deployment fact, not a standards claim; if
the deployment moves, the canonical home of this document's latest
revision is updated accordingly.
Implementers building against this vocabulary SHOULD start from
the open conformance kit (see Implementation Status): clone
https://gitlab.com/rambozambodotdev/zambo, run the conformance
runner for their language (for example,
aer1-implementations/python/conformance.py for Python) against
the frozen vector corpus, and treat byte-identical verdicts as
the conformance bar. The kit documents the vector inventory and
the reference-producer profile boundary that separates a
core-valid receipt from one the reference producer will emit.
12. Design Rationale
AER-1 starts with a narrow record rather than a universal theory of
agent behavior. An agent may call several tools, receive information
from several providers, and hand work to another agent. A reviewer
still needs a stable way to identify one execution, understand what
the system observed, and check whether the published bytes match the
stated commitment. The draft keeps those needs separate from claims
about the outside world.
Zambo Expires 12 April 2027 [Page 8]
Internet-Draft AER-1 October 2026
The vocabulary is intentionally small because interoperability fails
when every implementation gives a different name to the same
boundary. An identifier locates the record. A creation time orders
it. A tool and caller scope identify the execution context.
Canonical bytes and an output hash make the stored representation
checkable. A result preview gives a human a useful first read
without pretending that a preview is the entire source dataset.
Provenance is a separate design axis (Section 5). A system can
execute a tool itself, observe an action through an integrated
gateway, or receive a report from another agent. AER-1 treats these
as distinct attestations because they are distinct claims, and a
verifier that cannot tell them apart cannot report honestly.
On canonicalization: AER-1 preserves the producer's exact bytes and
carries them explicitly, rather than mandating a canonicalization
scheme such as the JSON Canonicalization Scheme [RFC8785]. A
verifier MUST NOT re-serialize the record under a different scheme
and claim agreement with the commitment. The cost of this choice is
stated plainly: digests computed under different canonicalizations do
not interoperate, so cross-implementation verification requires byte-
identical canonical content.
On resolution: AER-1 chooses public URL resolution as the primary
verification experience, so that checking a receipt needs no key
distribution and no trust anchor obtained out of band. The tradeoff
is availability dependence on the publisher; deployments that cannot
accept that tradeoff can pair the record with the external anchoring
in Section 10 or with offline recomputation from the carried
canonical bytes.
13. Security Considerations
A receipt is evidence of what was recorded, not a security boundary
by itself. The following considerations apply to implementations:
* The output commitment binds the recorded bytes, not the real
world. An implementation that records attacker-controlled input
produces a verifiable receipt of attacker-controlled input.
Verifiers MUST present the observed result as observed, never as
independently true.
* Canonical bytes MUST be preserved exactly. Any transformation
(re-encoding, whitespace normalization, character set conversion)
between recording and verification breaks the commitment and MUST
cause verification to fail closed.
Zambo Expires 12 April 2027 [Page 9]
Internet-Draft AER-1 October 2026
* Public receipt URLs are stable and permanent. A receipt that
embeds personal data, credentials, or confidential business
information in its canonical bytes publishes that data to
everyone; implementations MUST NOT publish such receipts
without redacting the sensitive data first. A receipt cannot
be unpublished.
* Provenance classes are attestations by the recording system about
itself. A dishonest recorder can mislabel provenance. Consumers
who need stronger guarantees should use the external anchoring in
Section 10 or bind receipts to independent witness records.
* The Section 7 chain digest binds entry metadata (sequence
position, job identifier, closing flag, receipt identifier, tool
name, provenance class) so metadata relabeling and cross-job
splicing are detectable by recomputation. It does not make the
chain truncation-proof: a rebuilt shorter chain verifies, so
deployments that need truncation detection MUST anchor an
external commitment (Section 7.3) and MUST NOT present the chain
check alone as sufficient.
* The Section 8 Merkle root binds the step sequence so that
insertion, deletion, reordering, or substitution is detectable
by recomputation, but it does not make the workflow
truncation-proof: a rebuilt shorter step list with a recomputed
root verifies, so deployments that need truncation detection
MUST anchor the merkle_root externally (Section 8.4,
Section 10) and MUST NOT present the Merkle check alone as
sufficient.
* A standalone receipt (Section 3) is not bound to a job,
session, or principal beyond its own identifier. A receipt
presented as evidence for a different execution than the one it
records is a replay, and this specification provides no
standalone-receipt replay defense. Consumers that need replay
resistance MUST use chained timelines (Section 7, which binds
job_id per entry) or workflow receipts (Section 8, which binds
steps to a workflow_id), or bind the receipt identifier to their
own session context out of band. A bare receipt proves what was
recorded for one execution; it does not prove the execution
belongs to any particular job.
* Anchored digests are public. SHA-256 of a low-entropy input (a
boolean, a small number, a value from a guessable set) can be
reversed by enumeration. Anchoring a digest does not keep the
preimage confidential. Where the
committed bytes are sensitive but the commitment must be public,
use a high-entropy salt or a keyed commitment and document the
construction; a verifier that cannot reproduce the salted bytes
MUST NOT report the commitment as confirmed (Section 4).
* The created_at timestamp is asserted by the producer.
Verification checks its form and that it denotes a valid
calendar date and time; it does not check that the stated
time is the true time, and a verifier MUST NOT present
created_at as externally established time. The external
time evidence for a receipt is its anchors (Section 10): a
verified anchor proves the anchored digest existed no later
than the anchor's publication time. Absent an anchor, a
receipt carries no trusted time.
* A producer can equivocate: present different records, or
different chain or workflow heads, to different verifiers.
The core record does not prevent this. The mitigations
are the stable public URL (Section 9), external anchors on
third-party infrastructure (Section 10) compared against
previously anchored values rather than against counts
alone, and, for deployments that need a single-history
guarantee, registration with a transparency service as a
"scitt" anchor (Section 10).
* Chain verifiers MUST distinguish the
-07/-08/-09/-10/-11/-12/-13/-14 construction
construction from the historical -06 construction and MUST NOT
report a -06 chain as satisfying the
-07/-08/-09/-10/-11/-12/-13/-14 construction
rules (Section 7.4).
* Verification endpoints MUST NOT leak information beyond the
receipt record itself, and MUST rate-limit verification requests
to prevent the endpoint from becoming an oracle for probing non-
public executions.
* This specification sets no maximum size for canonical_bytes,
steps arrays, or job timelines. Implementations MAY set local
size limits and reject records exceeding them, but a rejection
on size grounds MUST be reported as "rejected: size limit" and
MUST NOT be reported as "verification failed," since the record
may verify correctly under a higher limit. Implementations
SHOULD document their limits alongside the verifier.
* For receipts without metadata_hash (schema "0.3" and earlier),
the output commitment binds the recorded bytes but not the
metadata: a relabeled copy with identical canonical bytes
verifies under Section 6. A forger can also strip
metadata_hash and rewrite the schema version to "0.3".
Consumers that need tamper-evident metadata MUST require
metadata_hash (schema "0.4" or later), or use chained timelines
(Section 7) or workflow receipts (Section 8).
* Issuer authentication is not part of this specification.
Anyone can mint a well-formed receipt for any tool name, any
provenance class, and any bytes; verification proves the
recorded bytes were not altered after minting, not who minted
them and not that the minting party was authorized to speak
for the named tool. A verifier MUST NOT present a passing
check as proof of issuer identity. Consumers that need to
bind a receipt to its issuer MUST use an out-of-band trust
root or a signature scheme documented alongside the receipt;
this specification defines no signature scheme. Without such
binding, the receipt is self-asserted as to origin.
14. IANA Considerations
This document has no IANA actions.
15. Related Work
Action receipts for AI agents are an active area with multiple
concurrent individual proposals, occupying distinct slots.
The SCITT architecture [RFC9943] defines transparency
services that register signed statements and return inclusion
proofs; [NOA] profiles an agent action receipt as such a
signed statement. [NELSON] specifies authorization receipts:
records of what a principal permitted, a different record
from what executed. [XAIP], [SAHU], and [VAARA] specify
signed per-call receipts whose digests are computed over
canonicalized JSON [RFC8785]. [SHARIF] specifies an agent
audit trail: an operator-side log format with regulatory
mappings and transport exports, rather than publicly
resolvable per-execution records. [KLASSEN] specifies
content profiles that carry verdicts about outputs and are
designed to ride over other record formats. Naming these
slots is positioning, not endorsement.
AER-1 composes with the transparency-service slot rather than
competing with it: an AER-1 anchored_digest MAY be registered
with a SCITT Transparency Service, and the resulting
inclusion proof is carried as an anchor of type "scitt"
under Section 10. AER-1 core verification neither requires
nor is changed by that registration.
AER-1 differs in three ways. First, the receipt is identified by a
UUID and resolved at a stable public URL, with verification offered
as a public HTTP endpoint rather than offline recomputation. Second,
it carries provenance (executed, observed-via-gateway, logged-by-
agent) as a first-class field, so a verifier can distinguish what the
recording system did from what it merely recorded. Third, a
reference implementation is deployed whose receipts are publicly
verifiable without an account, key, or token. AER-1 standardizes the
narrow per-execution record and its public verification procedure,
leaving decision semantics, authorization, and settlement bindings to
other specifications.
16. Normative References
[FIPS180-4]
National Institute of Standards and Technology, "Secure
Hash Standard", DOI 10.6028/NIST.FIPS.180-4, FIPS 180-4,
August 2015.
Zambo Expires 12 April 2027 [Page 10]
Internet-Draft AER-1 October 2026
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119, March 1997,
<https://www.rfc-editor.org/rfc/rfc2119>.
[RFC3161] Adams, C., Cain, P., Pinkas, D., and R. Zuccherato,
"Internet X.509 Public Key Infrastructure Time-Stamp
Protocol (TSP)", RFC 3161, August 2001,
<https://www.rfc-editor.org/rfc/rfc3161>.
[RFC3339] Klyne, G. and C. Newman, "Date and Time on the
Internet: Timestamps", RFC 3339, July 2002,
<https://www.rfc-editor.org/rfc/rfc3339>.
[RFC4122] Leach, P., Mealling, M., and R. Salz, "A Universally
Unique IDentifier (UUID) URN Namespace", RFC 4122,
July 2005, <https://www.rfc-editor.org/rfc/rfc4122>.
[RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data
Encodings", RFC 4648, October 2006,
<https://www.rfc-editor.org/rfc/rfc4648>.
[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
2119 Key Words", BCP 14, RFC 8174, May 2017,
<https://www.rfc-editor.org/rfc/rfc8174>.
[RFC8259] Bray, T., Ed., "The JavaScript Object Notation (JSON)
Data Interchange Format", STD 90, RFC 8259,
December 2017, <https://www.rfc-editor.org/rfc/rfc8259>.
17. Informative References
[AER-1-HOME]
Zambo, B., "AER-1: AI Agent Execution Receipt (open
draft)", https://zambo.dev/aer-1, September 2026.
[KLASSEN] "draft-klassen-eu2122-content-profile-03",
Internet-Draft, Work in Progress.
[NELSON] "draft-nelson-agent-delegation-receipts-12",
Internet-Draft, Work in Progress.
[NOA] "draft-noa-scitt-ai-agent-receipt-01",
Internet-Draft, Work in Progress.
[RFC5234] Crocker, D., Ed. and P. Overell, "Augmented BNF for
Syntax Specifications: ABNF", STD 68, RFC 5234,
January 2008, <https://www.rfc-editor.org/rfc/rfc5234>.
[RFC7942] Sheffer, Y. and A. Farrel, "Improving Awareness of
Running Code: The Implementation Status Section",
BCP 205, RFC 7942, July 2016,
<https://www.rfc-editor.org/rfc/rfc7942>.
[RFC8785] Rundgren, A., Jordan, B., and S. Erdtman, "JSON
Canonicalization Scheme (JCS)", RFC 8785, June 2020,
<https://www.rfc-editor.org/rfc/rfc8785>.
[RFC9943] Birkholz, H., et al., "An Architecture for
Trustworthy and Transparent Digital Supply Chains",
RFC 9943, <https://www.rfc-editor.org/rfc/rfc9943>.
[SAHU] "draft-sahu-agent-action-receipts-00",
Internet-Draft, Work in Progress.
[SHARIF] "draft-sharif-agent-audit-trail-07",
Internet-Draft, Work in Progress.
[VAARA] "draft-sirkkavaara-vaara-receipt-13",
Internet-Draft, Work in Progress.
[XAIP] "draft-xkumakichi-xaip-receipts-03",
Internet-Draft, Work in Progress.
18. Acknowledgments
Matthew Moore provided the first substantive independent technical
review of this draft on the agent2agent list, identifying the
duplicate receipt_id Merkle collision (Section 8.2), the unpinned
Section 7 chain construction and its suffix-truncation weakness,
and the stale test-vector count. The -06 revision folded those
findings into the specification and the conformance kit. Moore
then ran eight chain attacks from the VLC-1 project against the
-06 construction and showed all eight were accepted, confirming the
digest bound too little. The -07 revision hardens the Section 7
entry digest in direct response: the digest now binds the entry's
sequence position, job identifier, closing flag, receipt
identifier, tool name, and provenance class, and the section states
the honest truncation limit his testing established. Moore then
cross-tested the -07 construction with thirty cases from
VLC-1, confirming all fourteen Section 7 attack cases produced the
specified outcomes, and identified the last-entry digest gap: the
final chain entry's digest is referenced by nothing inside the
chain, so the external truncation commitment should bind it. The
-08 revision adds that guidance to Section 7.3. The -09 revision
defines the optional chain commitment artifact in Section 7.3 and
the conformance kit check that verifies it.
ARION produced the first complete independent implementation of
AER-1, a zero-dependency Node.js verifier written from the -08
draft text alone, and filed three specification findings that the
-09 revision adopts.
ColonistOne (CMO of The Colony) produced an independent Perl
implementation of the -09
draft, verified 68/68 against the conformance kit with zero
disagreements against either reference runner, and filed three
specification findings that the -10 revision adopts: the
under-specified Section 7.1 string escaping (including the U+007F
divergence between the draft text and the reference
implementation), the Python-version-dependent timestamp check, and
nine conformance rules lacking isolating vectors.
Token Maxxer identified the workflow-receipt truncation gap that
the -12 revision closes in Section 8.4: the observation that a
hash chain's honest truncation limit applies equally to the
Section 8 Merkle root, and that the draft's silence on the point
was an inconsistency.
Leo Oliemans raised the unknown-versus-not-applicable
distinction and the write/readback outcome gap that the -18
revision addresses in Section 3.2. The agent penny filed
the findings behind Section 3.4 (per-field provenance, the
negative record, reversal state, and the method descriptor)
and the unauthenticated-metadata wording adopted in
Section 3.2. czh raised the Unicode normalization question
answered in Section 4, and dumate-scout proposed the
execution_context invalidation signal noted in Section 3.2.
The -18 revision folds those reports.
Appendix A. Mapping Note: EU AI Act Article 12 (Informative)
This appendix is informative and non-normative. Article 12
of the EU AI Act addresses record-keeping for high-risk AI
systems: such systems are to be designed so that events are
recorded automatically over the system's lifetime, at a
level of traceability appropriate to the intended purpose.
An AER-1 receipt is created automatically at execution time
and records the execution with a timestamp, the recorded
bytes, and tamper evidence, so a receipt can form part of
the records such a system keeps. A receipt alone does not
satisfy Article 12 or any other legal obligation: what must
be recorded, how long records are kept, and who may access
them are deployment and legal questions outside this
specification.
Author's Address
Brennan Zambo
Zambo
Email: brennanzambo@zambo.dev
URI: https://zambo.dev/aer-1
Zambo Expires 12 April 2027 [Page 11]