IETF Internet-Draft for AI Compliance Receipts

The canonical wire-format spec for signed AI agent compliance receipts.

Asqav implements draft-marques-asqav-compliance-receipts, an IETF Internet-Draft published under the Independent Submission stream. The draft standardises the wire format for signed AI agent action receipts: each receipt commits to an action, a decision, and an anchor binding, with hash-chained integrity per agent.

The profile maps the receipt format onto nine regulatory regimes, namely the EU AI Act, DORA, NYDFS Part 500, NIST AI RMF, Colorado AI Act, Texas TRAIGA, CIRCIA, the HIPAA Security Rule, and SEC 17a-4. Read the canonical draft on IETF Datatracker: https://datatracker.ietf.org/doc/draft-marques-asqav-compliance-receipts/.

At a glance

Normative clauses (the MUST/SHOULD set)

The 17 pinned clauses below summarise the binding requirements of the draft. Each one is a plain-English gloss. The canonical text lives on Datatracker.

1. Anchor entry shape

Every receipt carries a top-level anchors[] array whose entries are {type, value} objects. The type field acts as the discriminator and selects the schema for value.

2. Anchor type vocabulary

The type field MUST be either rfc3161 or opentimestamps. Any other value is a hard rejection at verification time.

3. IEEE-754 float prohibition

Digest-covered fields MUST NOT contain IEEE-754 floats. Numeric values that need decimal precision are encoded as strings to keep the JCS canonicalisation byte-stable across language runtimes.

4. Observation decisions exclude protectmcp:decision

A receipt with decision = observation MUST NOT pair with type = protectmcp:decision. Observation is a non-enforcement vocabulary and pairing it with a decision-type receipt is a false-attestation hazard.

5. Reason required for deny and rate_limit

Receipts whose decision is deny or rate_limit MUST carry a reason string. A bare denial without a reason is rejected.

6. Clock-skew rejection at 300 seconds

The issued_at timestamp MUST be within a 300-second forward-only window of the verifier's clock. Receipts dated in the future beyond that bound are rejected. Backward skew is tolerated to accommodate replay.

7. issuer_id byte-equals signature.kid

The issuer_id field on the receipt MUST be byte-equal to the kid field inside the signature envelope. A mismatch is a hard rejection.

8. payload_digest object form

The payload_digest field is an object with two members, algorithm and value. A bare digest string is not accepted on the wire.

9. previousReceiptHash digest scope

The previousReceiptHash field commits to the JCS-canonical bytes of the prior receipt's signed payload, and only those bytes. Headers and the signature envelope are out of scope for the chain.

10. Hash-chain genesis

The first receipt emitted by an agent uses an all-zero 32-byte SHA-256 as its previousReceiptHash. Verifiers detect chain start by exact byte-match against the zero vector.

11. Anchor presence

An anchor MUST be present on every signed receipt, with type set to either rfc3161 or opentimestamps. A receipt without an anchor is incomplete and is rejected.

12. OpenTimestamps 7-day upgrade deadline

When the chosen anchor is opentimestamps, the calendar-pending proof MUST be upgraded to a Bitcoin attestation within seven days. Beyond that window the proof is treated as incomplete.

13. counterparty_binding wire vocabulary, end-to-end

The counterparty_binding object lands in the cloud signing relay, the Python SDK, the TypeScript SDK, and the conformance vectors as one atomic vocabulary. The same token shape applies on all four surfaces.

14. LEI form

Legal Entity Identifiers carried on receipts are 20 characters and pass ISO 7064 mod 97-10 checksum validation. Malformed LEIs are rejected at envelope parse time.

15. base64 and base64url tolerance on envelope_hash

Verifiers MUST accept both base64 and base64url encodings on the envelope_hash field. The signer is free to pick either. The verifier normalises before comparison.

16. chain_emission_blocked SHOULD reason code

When chain emission is intentionally suppressed (for example, during key rotation or maintenance windows), the receipt SHOULD carry the reason code chain_emission_blocked. This keeps the chain gap auditable.

17. action_type format

The action_type string follows the namespace:verb shape, with no spaces. The advertised vocabulary lives at /.well-known/governance.json on the issuer.

Reference implementations

Four open-source implementations ship the spec today. They share conformance vectors and stay in lockstep.

Implementation What it covers
Asqav cloud Signing relay, audit pack, multi-region anchors. Reference verifier.
Python SDK PyPI: pip install asqav. Source at github.com/jagmarques/asqav-sdk.
TypeScript SDK npm: npm install @asqav/sdk. Byte-identical receipts with the Python SDK.
Claude Code hook Enforcement gateway for agent tool calls. Run asqav hook pretool and asqav hook posttool from the SDK.

Conformance vectors

Conformance test vectors covering counterparty_binding, anchor entry shapes, IEEE-754 float gating, and reason-required deny/rate_limit live in tests/conformance/test_ietf_vectors.py of the asqav repo. Re-running them against any verifier produces a binary PASS or FAIL for spec conformance.

Co-implementors and signatories

Open to co-implementors. If you ship a verifier or signer against this draft and want to be listed, open an issue on github.com/jagmarques/asqav-sdk.