Internet-Draft Agent Act Receipt October 2026
Graham Expires 21 April 2027 [Page]
Workgroup:
Individual Submission
Internet-Draft:
draft-gaip-act-receipt-00
Published:
Intended Status:
Informational
Expires:
Author:
T. Graham
GAIP

The Agent Act Receipt: a signed, chained record of an agent's act

Abstract

Software agents act for people: they buy, book, cancel, agree, deliver, refuse, tell, ask, read and check. Each payment rail, marketplace and assistant keeps its own record of those acts, held by that party. The person the agent acted for has no record of their own. This document describes the Agent Act Receipt: one entry per act, written by the agent as digests bound to a continuity handle, signed by a running transparency service, placed in a hash chain and covered by dated anchors, with a COSE_Sign1 form of the signature and a signed export (the switch pack) the person can take to another agent. The format is published by GAIP, which runs one such service. This document makes no claim of trust, certification or adoption: a receipt shows that the service's key signed a digest at a stated time, and nothing about the act itself.

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 21 April 2027.

▲

Table of Contents

1. Introduction

Every rail that carried an agent's act in 2026 keeps a record of it with a party: the payment network keeps the authorisation, the marketplace keeps the order, the assistant's operator keeps the conversation, and some commerce protocols tell platforms to discard order data once the transaction is complete. No rail keeps the record for the person the agent acted for, and no law requires anyone to. When the person changes assistant, disputes a purchase, or asks what their agents promised in their name last month, the record is scattered across parties who each hold one piece and owe the person nothing.

The Agent Act Receipt is a small, neutral record kept on the person's side. An agent writes one entry per act, as digests: what the act was about and what its content was, each hashed by the agent before anything leaves it. The entry is bound to the agent's continuity handle by a keyed hash, so the person's agents can read the record back and nobody else can. A running transparency service signs the entry, appends it to a hash chain and publishes dated anchors over the chain heads. The service never receives the content of the act and never says whether the act happened as described; it records that an agent submitted the entry at the stated time.

Three views make the record useful: the entries bound to a handle, read back by that handle; a signed export of the whole record with the service's public keys, which the person can take to another agent ("the switch pack"); and a COSE_Sign1 [RFC9052] form of each entry's signature, so a verifier that reads COSE can check an entry without the service's JSON.

This document describes the format GAIP publishes and runs. It is an individual submission. It does not define a standard, assert conformance of any party, or claim that any assistant, rail or regulator reads or accepts these receipts. The words "transparency service" are used in the sense of [RFC9943] only: GAIP operates one instance of such a service for this format and offers it as a running instance for anyone to use or check.

1.1. Conventions and Definitions

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.

2. Terminology

Act:

Something an agent did, promised, checked or was told, on behalf of a person. The kinds this document uses are listed in Section 3.1.

Agent:

Software acting for a person (its principal), which submits entries to the service.

Continuity handle:

An opaque, bearer pair (an identifier and a secret token) a service issues to an agent on a first completed answer, which the agent presents on later calls so the service can link them. The service stores only a hash of the token. A handle names an agent session, not a person.

Entry:

One record of one act, in the form of Section 3, submitted by an agent and completed by the service.

Receipt:

The service's Ed25519 signature over an entry's digest, in the JSON form of Section 5 and the COSE form of Section 6, together with the entry's place in the hash chain and the anchors that cover it.

Service:

The transparency service (in the sense of [RFC9943]) that validates, signs, chains and anchors entries and serves them back to the handle that bound them. GAIP runs one.

Switch pack:

The signed export of every entry bound to a handle, with the service's public keys and verifier steps, in the form of Section 7.

Keyed hash:

HMAC-SHA256 under a secret the service holds, with a fixed label, over a value the service must be able to recognise again (a handle, a product identifier, an opaque reference) without keeping the value itself.

3. The Entry

An entry is a JSON object. Its schema is named by the schema_version member, gaip.act.v1. The fields below are those the GAIP service stores; a service implementing this format MUST NOT add a field that carries the content of the act or a person's identifier in clear.

3.1. Kinds

kind is one of a closed list: BOUGHT, BOOKED, CANCELLED, SUBSCRIBED, PAID_DECLARED, AGREED, DELIVERED, REFUSED, TOLD, ASKED, READ, CHECKED, REVOKED, NOTICE_MATCHED. A submission with any other kind MUST be refused. NOTICE_MATCHED is written by the service itself (see Section 4), never by an agent.

3.2. Fields the agent submits

subject_sha256:

The SHA-256, as 64 lower-case hex characters, of the agent's own reference for what the act was about. REQUIRED.

content_sha256:

The SHA-256, as 64 lower-case hex characters, of the content of the act (the order, the message, the terms). REQUIRED. The content itself MUST NOT be accepted: a submission carrying a content field is refused.

counterparty:

OPTIONAL. One of {"handle_hash": <64 hex>} (another agent's handle, by its keyed hash), {"host": <public host name>} or {"ref": <opaque text>}. A ref is stored only as its keyed hash under the label gaip.act.counterparty.hmac.v1.

mandate_sha256:

OPTIONAL. The SHA-256 of the mandate the agent acted under.

amount_declared, currency:

OPTIONAL. A decimal string (digits, at most four decimals) and an ISO 4217 code. Recorded as declared, never checked against anything.

page_url:

OPTIONAL. A public product page the service reads once, at submission, through a reader with fixed refusals (no cart, checkout or account paths; marketplaces the service's rules name are refused; robots.txt is honoured). The service records what the page showed (see page_read below), never the page.

cosigner:

OPTIONAL. {"key_id": ..., "signature": <over entry_sha256>, "host": <optional>}. Recorded as presented, with key_state KEY_KNOWN when the service's record of published keys lists that key id for the host, else KEY_NOT_KNOWN. The service does not check the cosigner's signature and says nothing about what two signatures mean between the parties.

product_ids, watch:

OPTIONAL, BOUGHT only. Product identifiers (GTIN, model number or product URL) for the recall watch (Section 4); the service keeps only their keyed hashes under the label gaip.act.product_id.hmac.v1, and the entry records only their count.

Any field outside this list MUST be refused, so that no plaintext about a person can arrive by another name.

3.3. Fields the service adds

schema_version, service, evidence_class, brand:

gaip.act.v1, ACT_LOG, ACT, and the service's name.

actor_handle_hash:

The keyed hash (label gaip.act.handle_hash.hmac.v1) of the continuity handle that bound the entry. The handle itself is never stored in the entry.

recorded_at_utc, observed_at_utc:

When the service recorded the submission (ISO 8601, UTC).

submitted_by:

AGENT for an agent's submission; GAIP (the service's own name) for an entry the service wrote itself.

page_read:

Present when page_url was given: {url, seller_domain, read_at_utc, read_status, values {name, price, currency, availability, variant}, sha256, sources_read, page_content_retained: false}.

sentence, what_this_is:

A fixed sentence the service places on every entry, answer, page and export, never reworded. GAIP's is: "GAIP records that an agent submitted this entry at the stated time, and, where shown, what a public page showed when GAIP read it. GAIP did not witness the act and does not say it happened as described."

published, data_classification, content_retained, page_content_retained:

false, PSEUDONYMOUS, false, false.

correction_route, price_gbp, affiliate_links, external_effect, outside_write:

The service's corrections address; 0; false; false; whether the submission came from outside the service's own callers.

receipt_id, observation_id, entry_sha256, signature, signature_status:

See Section 5. These sit beside the signed body and are not part of the digest.

4. The Recall Watch

A BOUGHT entry that carries product_ids with watch set to true asks the service to watch for recall notices about those products. The service keeps only keyed hashes of the identifiers and a bounded set of words from the product's name. Once a day it reads the notices that public regulators have published since its last run (in GAIP's deployment, the GOV.UK product safety alerts and the US Consumer Product Safety Commission recalls) and, when a notice's identifiers match a watched hash, appends a NOTICE_MATCHED entry to that handle's log carrying the notice's own title, date and link. A match on name words alone is recorded as a possible match, for the person to check. The service states only which notice matched and when, quoting the regulator's own words; it MUST NOT add any judgement of its own about the product.

5. The Receipt

5.1. The digest

entry_sha256 is the SHA-256, as 64 lower-case hex characters, over the canonical JSON of the entry without the fields receipt_id, observation_id, entry_sha256, signature, signature_status, cosigner, handle_hash_retention, content_retention and erasure. Canonical JSON here means: keys sorted, separators , and : with no spaces, UTF-8. receipt_id is the string ac- followed by the first twelve characters of the lower-case base32 encoding of entry_sha256.

5.2. The signature

signature is {alg: "Ed25519", kid, sig, purpose, signed_message}: an Ed25519 [RFC8032] signature, base64url without padding, over the ASCII bytes of <purpose>:<entry_sha256> where purpose is gaip.act.entry_sha256.v1. kid is the first sixteen lower-case hex characters of SHA-256 over the raw 32-byte public key. The service publishes its public keys as a JWK set [GAIP-KEYS] (kty OKP, crv Ed25519, x, alg EdDSA, a status, and for a retired key when and why). A verifier recomputes entry_sha256, rebuilds the message and checks sig with the key that carries the same kid.

5.3. The hash chain

Each signed entry is appended to the service's hash chain as a record {previous_record_sha256, observation} whose record_sha256 is the SHA-256 of its canonical JSON; observation is the entry. The record is served by its receipt_id, so anyone can check that the entry sits where the chain says it does. A retention rule may later replace the entry's content with a tombstone that keeps digests and signatures; an erasure tombstone keeps signatures and key references and no digest of the content. In both cases previous_record_sha256 and record_sha256 are unchanged, so the chain still verifies.

5.4. Daily anchors

Once a day the service signs a document holding its chain heads through a public transparency log and timestamps it, and lists the results [GAIP-ANCHORS]. An anchor shows that the listed chain-head digests existed no later than the anchor's own entries; it shows nothing about any act.

6. The COSE Form

Each entry's signature is also served as a COSE_Sign1 [RFC9052] so a verifier that reads COSE need not read the service's JSON. GAIP serves it at GET /v1/free/acts/<receipt_id>/receipt.cose with media type application/cose; cose-type="cose-sign1".

The structure is the tagged COSE_Sign1, tag 18:

18([
  protected,     / bstr: the serialised protected header /
  {},            / unprotected header: an empty map /
  payload,       / bstr: the 64 ASCII hex characters of entry_sha256 /
  signature      / bstr: 64 bytes, Ed25519 /
])

The protected header is the CBOR [RFC8949] map, in the core deterministic encoding (shortest integers, definite lengths, map keys ordered by their encoded bytes):

{
  1: -8,                          / alg: EdDSA, RFC 9053 /
  3: "text/plain; charset=utf-8", / content type of the payload /
  4: kid                          / bstr: key id as UTF-8 bytes /
}

alg is EdDSA [RFC9053]. kid is the same key id as the JSON signature's. The payload is the 64 ASCII hex characters of entry_sha256, not the entry. The signature is Ed25519 over the Sig_structure of [RFC9052] section 4.4, ["Signature1", protected, external_aad, payload], with empty external_aad. It is a second encoding of the same key over the same digest; the two signatures differ in their signed bytes only because the COSE form signs the Sig_structure and the JSON form signs <purpose>:<digest>.

When the service's signing key is not available, the route answers JSON with status NOT_SIGNED and the fixed sentence instead of bytes; it never serves an unsigned COSE object.

7. The Switch Pack

The switch pack is the service's own export of every entry bound to one handle, served only to that handle (a ticketed read: the request presents the handle, the service verifies it, and a request without one is refused). Its schema is gaip.switch-pack.v1. It holds:

The pack is an open, signed JSON document that any agent can read and check offline against the published keys. This document does not say what any other service does with it.

8. Relationship to SCITT

[RFC9943] describes an architecture in which Issuers make Signed Statements about Artifacts, a Transparency Service registers them in a verifiable data structure and returns Receipts, and a Transparent Statement is a Signed Statement augmented with its Receipt. In those terms:

The correspondence is descriptive. This document does not claim conformance to [RFC9943] or to any SCITT profile, and the receipt format here is not the SCITT Receipt structure; a future revision may describe a mapping. Related individual submissions describe action receipts for AI agents in SCITT or hash-chained forms ([I-D.noa-scitt-ai-agent-receipt], [I-D.sahu-agent-action-receipts], [I-D.mih-scitt-agent-action-capsule], [I-D.cowles-ward]); this document is set apart by keeping the record on the person's side, bound to a continuity handle and exportable as one signed pack. GAIP offers its act log as a running instance of this format, free of charge, without an account, for anyone to write to (once its operator's data protection assessment is complete) and for anyone to check. It makes no claim that any party trusts, certifies or has adopted it.

9. Security Considerations

The receipt shows that the service's key signed entry_sha256 at some time, and the chain and anchors show when, within the anchors' resolution. It does not show that the act happened, that the digests stand for what the agent says they do, or that any party did anything: an entry is the submission as recorded. A reader MUST treat amount_declared, counterparty and every agent-supplied digest as the agent's statement.

Digests are the agent's: a service cannot tell a digest of real content from a digest of anything else. Two signers on one digest (cosigner) show that two keys signed the same digest at the stated times; the service records the second signature as presented and does not check it. Whether that made an agreement is between the parties.

The service's private key MUST be held only in the service's runtime and left out of backups; a compromise or rotation is announced through the key set (a retired key keeps its kid, with when and why). Verifiers SHOULD check the key's status in the key set, not only its presence.

The chain is append-only; a tombstone changes an entry's content and never its chain record, so a reader that sees CONTENT_DELETED or ERASED_ON_REQUEST SHOULD treat the row as a placeholder whose link still verifies.

Submissions and reads are rate-limited per caller, and writes require a handle. A handle is a bearer secret: whoever holds it can write to and read the record it binds. Handles SHOULD be kept as any credential is.

10. Privacy Considerations

The service holds hashes and public page values only. It never receives the content of an act; a content field is refused. A person's identifier never reaches it: the continuity handle names an agent session, and the entry holds only a keyed hash of it (actor_handle_hash), under a label and a secret the service holds, so the hash cannot be recomputed by anyone else from a known handle. An opaque counterparty reference and product identifiers are kept as keyed hashes in the same way, and a ref or identifier for which no key is available is refused rather than stored in clear.

The record is served to the handle that bound it and to nobody else: the entries, the switch pack and the dispute export answer only a request that presents that handle (or, for the export, the cosigner's own key). One entry is public by its code as hashes and public page values; it names no handle in clear, but its actor_handle_hash is the same on every entry of that handle, so holders of several codes can link them.

The entry's content is kept for a fixed period, then replaced by digests and signatures; the handle's keyed hash is kept for that period and dropped with the content, and is removed earlier by an erasure tombstone on request. The service's operator publishes a data protection assessment of this processing before accepting outside entries and describes it on its privacy page.

A page read records what a public product page showed, never the page, and never a cart, checkout or account page.

11. IANA Considerations

This document has no IANA actions. A later revision may request registration of the media type parameters and the CBOR tags it uses, if any beyond those already registered.

12. References

12.1. Normative References

[RFC2119]
Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, , <https://www.rfc-editor.org/rfc/rfc2119>.
[RFC8032]
Josefsson, S. and I. Liusvaara, "Edwards-Curve Digital Signature Algorithm (EdDSA)", RFC 8032, DOI 10.17487/RFC8032, , <https://www.rfc-editor.org/rfc/rfc8032>.
[RFC8174]
Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, , <https://www.rfc-editor.org/rfc/rfc8174>.
[RFC8949]
Bormann, C. and P. Hoffman, "Concise Binary Object Representation (CBOR)", STD 94, RFC 8949, DOI 10.17487/RFC8949, , <https://www.rfc-editor.org/rfc/rfc8949>.
[RFC9052]
Schaad, J., "CBOR Object Signing and Encryption (COSE): Structures and Process", STD 96, RFC 9052, DOI 10.17487/RFC9052, , <https://www.rfc-editor.org/rfc/rfc9052>.
[RFC9053]
Schaad, J., "CBOR Object Signing and Encryption (COSE): Initial Algorithms", RFC 9053, DOI 10.17487/RFC9053, , <https://www.rfc-editor.org/rfc/rfc9053>.

12.2. Informative References

[GAIP-ANCHORS]
GAIP, "GAIP dated chain-head anchors", , <https://www.gaipagents.com/v1/free/observatory/anchors>.
[GAIP-FORMAT]
GAIP, "The act log: the format", , <https://www.gaipagents.com/acts/format>.
[GAIP-KEYS]
GAIP, "GAIP receipt signing keys (gaip.receipt-keys.v1)", , <https://www.gaipagents.com/.well-known/gaip-receipt-keys.json>.
[I-D.cowles-ward]
Cowles, A., "Write-once Append-only Receipt Digests (WARD): A Content-Free Hash-Chain Witnessing Protocol for Agentic AI Systems", Work in Progress, Internet-Draft, draft-cowles-ward-00, , <https://datatracker.ietf.org/doc/html/draft-cowles-ward-00>.
[I-D.mih-scitt-agent-action-capsule]
Mih, S., "An Agent Action Capsule Profile for SCITT", Work in Progress, Internet-Draft, draft-mih-scitt-agent-action-capsule-05, , <https://datatracker.ietf.org/doc/html/draft-mih-scitt-agent-action-capsule-05>.
[I-D.noa-scitt-ai-agent-receipt]
Toraman, T., "A SCITT Profile for AI-Agent Action Receipts", Work in Progress, Internet-Draft, draft-noa-scitt-ai-agent-receipt-01, , <https://datatracker.ietf.org/doc/html/draft-noa-scitt-ai-agent-receipt-01>.
[I-D.sahu-agent-action-receipts]
sahu, N., "Signed, Hash-Chained Action Receipts for AI Agents", Work in Progress, Internet-Draft, draft-sahu-agent-action-receipts-00, , <https://datatracker.ietf.org/doc/html/draft-sahu-agent-action-receipts-00>.
[RFC9943]
Birkholz, H., Delignat-Lavaud, A., Fournet, C., Deshpande, Y., and S. Lasker, "An Architecture for Trustworthy and Transparent Digital Supply Chains", RFC 9943, DOI 10.17487/RFC9943, , <https://www.rfc-editor.org/rfc/rfc9943>.

Appendix A. A worked example

The format page [GAIP-FORMAT] carries a synthetic, unsigned example entry and the diagnostic notation of the protected header. A signed sample made under a test-only key is published with the service's tests; it verifies under that key and under no other.

Acknowledgements

The problem this document names, that every rail keeps its records with a party and the person has none, was drawn from the published specifications of the 2026 agent payment and commerce rails and from the authors of the related individual submissions listed above. GAIP's Legal desk record of 10 October 2026 fixed the sentence and the words this format never uses.

Author's Address

Toby Graham
GAIP