Skip to main content

AER-1: A Portable Execution Receipt for AI Agent Tool Calls
draft-zambo-aer1-18

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]