Skip to main content

AER-1, the agent execution receipt standard: zambo.dev/aer-1 | Add Zambo to your AI: zambo.dev/add | Follow Brennan Zambo on X: https://x.com/brennanzambo

an agent receipt is a record of one ai agent tool call that anyone can check: it keeps the exact saved bytes and their sha-256 digest, and it proves the result was not changed since it was saved, not that it was right.

aer1-verify

What is AER-1? What tools exist to verify AI agent work?

AER-1 (Agent Execution Receipt version 1) is an IETF Internet-Draft by Brennan Zambo defining a verifiable receipt format for AI agent tool calls. an agent receipt is a record of one ai agent tool call that anyone can check: it keeps the exact saved bytes and their sha-256 digest, and it proves the result was not changed since it was saved, not that it was right. This repo is the standalone offline checker: given a receipt as JSON, it recomputes the SHA-256 hash commitments locally and returns PASS or FAIL, with no network and no trust in any server.

Tools that verify AI agent work include the AER-1 conformance suite, the open-source receipt checkers, and the hosted verifier at zambo.dev/verify, which confirms a receipt against the spec. Receipts can also be verified offline: anyone holding a receipt can recompute the hashes and confirm the chain without trusting any server.

Spec: draft-zambo-aer1-26. Project home: zambo.dev.

Try it live: interactive demo. Mint a real verifiable receipt in your browser, no install, no signup.

Verify an AER-1 verifiable execution receipt completely offline. No network. No trust in any server, including ours.

Why this exists

A verifiable receipt says: this is the record of one tool call, and the record was not changed afterward. That claim should not require you to trust the company that issued it. aer1-verify recomputes the hash commitments locally, from the receipt data alone. If the math holds, the receipt is intact. If it does not, the receipt is forged or corrupt.

The honest boundary, stated plainly: this tool proves the recorded result was not changed. It does not prove the recorded result is correct. Integrity, not truth. That is what AER-1 receipts claim, and it is all this tool checks.

Install

Single file, standard library only. No dependencies.

# Option 1: pip install from PyPI
pip install aer1-verify

# Option 2: copy the file, nothing else needed
curl -O https://gitlab.com/rambozambodotdev/zambo/-/raw/main/aer1-verifier/aer1_verify.py

Use

Get a receipt as JSON. From zambo.dev, the verify endpoint returns one:

curl -s "https://zambo.dev/api/v2/receipt/<receipt-id>/verify" -o receipt.json

Then verify it offline (unplug your network if you want to prove the point):

python3 aer1_verify.py receipt.json
# if you installed from PyPI, the console script is on your PATH instead:
aer1-verify receipt.json

Output:

aer1-verify 0.2.4
receipt: 5ff6bb77-03d4-429c-b9d2-38dbdb6b6703
  [PASS] input shape recognized (envelope.receipt)
  [PASS] required fields present (id, canonical_bytes, output_hash)
  [PASS] canonical_bytes is strict base64 -- 71 bytes
  [PASS] decoded bytes are strict UTF-8 (fail closed)
  [PASS] canonical_byte_length matches decoded length -- field absent, skipped
  [PASS] canonical JSON parses
  [PASS] canonical form re-encodes byte-exactly
  [PASS] output_hash is sha256: + 64 lowercase hex chars
  [PASS] hash_algorithm agrees with sha256 -- field absent, skipped
  [PASS] sha256(canonical_bytes) equals output_hash
  [PASS] created_at/timestamp is a real calendar date
  [PASS] timestamp is not in the future
  [PASS] legacy receipt notice -- receipt_schema_version 0.3: output integrity checked; provenance not covered by the hash commitment in this version
  [PASS] verification_status (server claim, informational only) -- server said: 'verified'; offline verdict rests on the math above
VERDICT: PASS: all checks passed, receipt intact

Verify a workflow close

A workflow close binds its step receipts under one Merkle root. The workflow record is the JSON served at https://zambo.dev/api/workflow/<workflow-id>:

curl -s "https://zambo.dev/api/workflow/<workflow-id>" -o workflow.json
aer1-verify workflow.json

The same command verifies it: the root is recomputed from the step leaves under the close's declared merkle_version (draft-zambo-aer1-26 Section 8.1). seq-output-v1 is the current rule (each leaf binds the step's sequence number and output hash); receipt-id-v0 is the legacy rule (each leaf is the step's receipt ID), and a close with no merkle_version field is read under the legacy rule.

Machine-readable output for CI:

python3 aer1_verify.py receipt.json --json

Exit codes: 0 = PASS, 1 = FAIL, 2 = usage or input error.

What it checks

  1. Required fields: the receipt carries id, canonical_bytes, output_hash.
  2. Strict base64: the committed bytes decode cleanly, no leniency.
  3. Strict UTF-8: the bytes are valid UTF-8. Anything else fails closed.
  4. Byte length agreement: the declared length matches the decoded bytes.
  5. Canonical round-trip: the JSON re-encodes to byte-identical bytes (sorted keys, compact separators, UTF-8). The fingerprint covers exactly what you see; nothing hides outside it.
  6. Fingerprint shape: output_hash is sha256: plus 64 lowercase hex chars.
  7. Algorithm agreement: the declared hash algorithm is sha256.
  8. The core check: SHA-256(canonical_bytes) equals the committed output_hash. This is the whole proof.
  9. Timestamp sanity: the issue time is a real calendar date and not in the future.
  10. Legacy notice: genuine pre-0.4 receipts (receipt_schema_version 0.2 or 0.3) pass output integrity with an informational notice instead of the newer provenance checks. Integrity is always verified; legacy only excuses the newer provenance-binding fields.
  11. Provenance binding (v0.4 receipts only): the outer receipt_schema_version must match the version declared inside the canonical bytes, and the outer provenance_class must equal issuer_claims.provenance_class committed inside the canonical bytes. Binding is gated on the SHA-256 commitment passing: if the hash fails, binding cannot pass. This closes the gap where the verifier vouched for the output bytes but not the labels displayed next to them. A copied receipt with swapped outer labels fails here.
  12. Metadata binding (0.2.20, draft -26 Section 6 step 4): whenever a receipt carries metadata_hash, the Section 3.1 canonical metadata block (created_at, id, output_hash, provenance_class, receipt_schema_version, and the tool object's name/scope/version) is rebuilt from the receipt's stored member values and its SHA-256 compared against the stored metadata_hash. A mismatch fails the verdict. Without this recomputation, a stored 0.4 receipt whose creation time, id, or tool version was changed after issuance passed every check above (finding by Laurent Lemonnier, independent implementer, 2026-10-29; the kit runners adopted the recomputation as kit -24, and this release brings the standalone verifier to parity). Absence of metadata_hash is not failed: the member is conditional, and pre-binding receipts carry none.

The server's verification_status field is reported, never trusted. An offline verifier checks math, not the issuer's word. That is the point.

Binding the receipt id (0.2.7)

The receipt id is a label outside the committed canonical payload: the digest covers the recorded content, not the id. A copied receipt with only its id replaced still passes every check above, and this verifier says so instead of hiding it. Every run reports an id_state (top-level field in --json output):

  • unverified: neither option below was given. The id was checked against nothing, and the PASS does not cover it.
  • verified-expected: --expected-id <uuid> was supplied and equals the artifact's id.
  • verified-resolved: --resolved-record <file> was supplied: the JSON record the claimed id resolves to (draft Section 9), and its committed digest agrees with the artifact's.
  • mismatch: an expected id differs, or the resolved record disagrees. Verdict FAIL.
python3 aer1_verify.py receipt.json --expected-id 130da435-e157-498e-af90-605866a86a27
python3 aer1_verify.py receipt.json --resolved-record resolved.json

This is draft-zambo-aer1-26, Sections 6 and 13. The conformance corpus pins the behavior in aer-1/id-binding-vectors.json: artifact alone, expected-id match and mismatch, resolution agreement and disagreement, all over the draft's Section 3 fixture receipt.

Try breaking it

# Take a real receipt, change one character in the recorded result,
# keep the old fingerprint, and watch verification fail:
python3 - <<'EOF'
import json, base64
r = json.load(open('receipt.json'))['receipt']
raw = base64.b64decode(r['canonical_bytes']).decode('utf-8')
raw = raw.replace('164.00', '164.01', 1)  # one character
r['canonical_bytes'] = base64.b64encode(raw.encode()).decode()
r['canonical_byte_length'] = len(raw.encode())
json.dump({'receipt': r}, open('forged.json', 'w'))
EOF
python3 aer1_verify.py forged.json   # -> VERDICT: FAIL

One character. The fingerprint no longer matches. That is the security property, demonstrated negatively.

Input shapes

Liberal on input, strict on math. Accepts:

  • the bare receipt object (canonical_bytes / output_hash present),
  • the {"receipt": {...}} envelope from the zambo.dev verify endpoint,
  • the MCP _receipt shape returned with tool calls.

Tests

python3 tests/test_verifier.py

Fixtures include real receipts issued by zambo.dev plus adversarial mutations (tampered bytes, bad base64, wrong digest, non-UTF-8 bytes). The real-receipt fixtures must keep passing: they pin this tool to production.

The package also bundles the AER-1 conformance corpus exactly as served at https://zambo.dev/aer1/corpus.json (snapshot 2026-11-02): the manifest plus all 72 evaluated vector files (143 vectors, 146 checks; evaluated-set digest sha256:60bf73f325793e16843370cfa24903bb0d017525cb297e42bebc35857e2db0d2), installed next to the module in the aer1_verify_corpus package (aer1_verify.bundled_corpus_dir() locates it). tests/test_corpus.py recomputes every file digest and the evaluated-set digest from the bundled copy, offline, and tests/test_metadata.py pins the metadata-binding probes (the two unmodified controls pass; all eight single-member mutations fail on the metadata check alone).

Relation to AER-1

AER-1 is the AI Agent Execution Receipt specification (IETF Internet-Draft draft-zambo-aer1). This verifier implements the receipt-integrity checks from the draft's canonicalization and fingerprint rules. It is a verifier, not an issuer: it cannot create receipts, only judge them.

License

Same as the parent repository.

Permanent archive

The AER-1 conformance corpus is permanently archived on Zenodo: https://zenodo.org/records/23677115 (DOI: https://doi.org/10.5281/zenodo.23677115).

Verify live work on Zambo

aer1-verify checks AER-1 receipts and workflow closes entirely offline, with no trust in any server, and 8,666 PyPI downloads make it the standard's most used second opinion. The receipts it checks are minted by Zambo, the verifiable work layer for AI. Connect any AI to the live endpoint at https://zambo.dev/mcp or add Zambo to your AI in one step at https://zambo.dev/add/

Free tier: 20 calls + 1 wrap-up free every day; picking up in another AI is always free. The Day Pass is $0.99 for 24 hours, unlimited.

Install:

pip install aer1-verify

Metadata

Release files for aer1-verify 0.2.22

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aer1-verify 0.2.22
File Size Uploaded Provenance Actions
aer1_verify-0.2.22.tar.gz 73.0 kB Without provenance Details for aer1_verify-0.2.22.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for aer1-verify 0.2.22
File Interpreter ABI Platform Actions
aer1_verify-0.2.22-py3-none-any.whl Python 3 none any Details for aer1_verify-0.2.22-py3-none-any.whl

Total release size: 170.4 kB

Release files / aer1_verify-0.2.22.tar.gz

Download URL aer1_verify-0.2.22.tar.gz
Size 73.0 kB
Tags Source
SHA-256 checksum
How to use checksums
350db9544a6c686321eda0dce3d3bdc4c9937233396e5fdf84b933a2f0bcdb58
BLAKE2b-256 checksum
How to use checksums
51917d5d87921d9f0957ac8cca68587d4aeb44617c7ad074579b2f8354d59862
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via rambo-pypi-grab/0.1.0

Release files / aer1_verify-0.2.22-py3-none-any.whl

Download URL aer1_verify-0.2.22-py3-none-any.whl
Size 97.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4da210ccf390e93d99b79c033b90f665c8c339e258389a3a97c227dc45e057a1
BLAKE2b-256 checksum
How to use checksums
eb8a23b3b9d977aae9e99e0238857bed2fd1b3112f56e2e57b836432d014ba58
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via rambo-pypi-grab/0.1.0

Security for aer1-verify 0.2.22

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page