Skip to main content

Agent Receipts

Two AI agents negotiate a real foreign-exchange trade. Every message they exchange — and every tool call they make — passes through a receipt middleware that they don't know exists. It hashes the payload, signs a small receipt with each party's own Ed25519 key, and writes it to three copies of a ledger. Edit any past row in any copy and a standalone verifier catches it. That's the whole project: tamper-evident receipts for agent interactions (triple-entry accounting, backing an academic paper).

Use it on your own agents

pip install agent-audit-sdk

Declare your agents, wrap a boundary, and every interaction across it is receipted. Two ways to run the same front:

from agent_audit import Agent, Receipts

receipts = Receipts(
    agents=[Agent("planner", planner_seed), Agent("researcher", researcher_seed)],
    ledger_path="receipts.db",
)
front = receipts.a2a(
    upstream="http://researcher:9000", caller="planner", callee="researcher"
)

# (a) as an endpoint — point the planner's researcher URL at :9102 and
#     change nothing inside either agent
await receipts.serve((front, 9102))

# (b) in your own app — mount it, and drive the ledger worker from your
#     lifespan with `async with receipts.running():`
app.mount("/researcher", front.build_app())

receipts.mcp(...) does the same for an agent-to-tool boundary. Seeds are 32-byte hex — python -m scripts.gen_signing_seeds prints a set.

Two agents, no LLM, no framework, end to end in one command:

python -m examples.two_agents
planner  -> researcher : research_request(topic='container throughput')
researcher -> planner  : container throughput looks stable this quarter

3 receipts recorded - neither agent did anything to make this happen:

  seq 1  a2a_message      planner -> researcher  research_request  proof OK
  seq 2  a2a_message   researcher -> planner     findings          proof OK
  seq 3  task_summary  researcher -> planner     completed         proof OK

  the question itself is not in the ledger: True
  every receipt signed by both parties: True
  three copies, all agreeing: [3, 3, 3] True

  VERDICT: MATCH

Recording is never something an agent opts into. It happens at the transport boundary, which is why there is no record_receipt tool: an agent that could choose to call it could choose not to, and self-reported bookkeeping is the thing triple-entry exists to replace.

Reading back is a separate, read-only surface — in process (TransparencyService), over HTTP (transparency_service.main, plus a console at /ui), as a CLI (python -m verifier), or as MCP tools an agent can audit with:

python -m agent_audit.mcp_server   # get_receipts · get_tasks · verify_ledger · get_anchors

The scenario

  • Buyer (Claude Agent SDK) wants to buy €10,000 with USD, and will tolerate at most 80 bps of spread.
  • Seller (CrewAI) is an FX broker with a 20 bps floor. It prices quotes off the real ECB reference rate, fetched live from the Frankfurter API through a custom MCP server — no mocked data.
  • They negotiate over the A2A protocol, up to 3 rounds. Deal or no-deal, every turn is receipted.

Architecture

buyer ──A2A──▶ ┌────────────────────┐ ──A2A──▶ seller
               │ receipt middleware │
   seller ──MCP──▶ │  :9102 A2A front   │ ──MCP──▶ frankfurter (ECB rates)
               │  :9201 MCP front   │
               └─────────┬──────────┘
                         │ per interaction: hash payload → sign twice → append
                         ▼
               ledger.db (SQLite): sender_copy | receiver_copy | neutral_copy
                         ▲                 each row: prev_hash + entry_hash chain
                         │
                  verifier (standalone, read-only)

The middleware is a transparent proxy. Agents are pointed at it purely by env vars (SELLER_URL, FX_MCP_URL) — zero agent code changes, which is the point: it must work on agents whose internals you don't control. A receipt stores only the payload's SHA-256, never the payload, so the schema works for any future interaction type.

Run it

docker compose up -d --build                                  # 5 services
docker compose exec buyer python -m scripts.run_negotiation   # one negotiation
docker compose exec middleware python -m scripts.export_public_keys  # once
docker compose exec middleware python -m verifier             # check the ledger

(Bare-metal, one terminal per service: see docs/running.md.)

What you should see

The negotiation prints a deal (with the agreed quote) or no_deal. The ledger (ledger-data/ledger.db, browsable with any SQLite tool) then holds one receipt per interaction, all tied to one task id:

seq 1: a2a_message    request_quote  buyer->seller
seq 2: mcp_tool_call  get_ecb_rate   seller->frankfurter-mcp
seq 3: a2a_message    quote          seller->buyer            task=35cc9c59
seq 4: a2a_message    counter        buyer->seller            task=35cc9c59
seq 5: a2a_message    accept         seller->buyer            task=35cc9c59
seq 6: task_summary   deal           seller->buyer            task=35cc9c59

And the verifier reports:

  sender_copy      6 rows   chain OK   signatures OK
  receiver_copy    6 rows   chain OK   signatures OK
  neutral_copy     6 rows   chain OK   signatures OK
  cross-copy     counts OK, contents identical, stored hashes identical

VERDICT: MATCH

Prove the tamper-evidence

Flip one field in one copy with any SQLite tool, rerun the verifier:

  receiver_copy    6 rows   chain BROKEN at seq 3   2 signature failure(s)
VERDICT: MISMATCH

SQLite being editable is fine — the guarantee is that tampering is detectable, not impossible. Detection is the product.

Where things live

Path What
agent_audit/ The published SDK — Agent, Receipts, and the read-only MCP server
receipt_middleware/ Proxy fronts, receipt schema, signing, spool, ledger, anchoring
transparency_service/ Query + verify over HTTP
frontend/ The console served at /ui
verifier/ Standalone verifier (imports only the receipt schema)
agents/buyer, agents/seller The two negotiating agents (the demo)
custom_mcps/frankfurter/ MCP server wrapping the ECB rates API
config/ All env vars and fixed demo constants
docs/technical-spec.md Every design decision, with the why

The demo runs on the SDK rather than beside it — receipt_middleware/main.py builds the same Receipts object the snippet above does, so the published surface is the one exercised every day.

Honest caveat (stated in the spec too): this POC is single-operator — one middleware holds all keys and writes all three copies. Real multi-party non-repudiation needs independently operated middlewares; that and public-chain anchoring of the neutral copy's root hash are the next steps.

Metadata

Release files for wai-agent-audit 0.1.0

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

Source distribution (sdist)

Source distribution for wai-agent-audit 0.1.0
File Size Uploaded
wai_agent_audit-0.1.0.tar.gz 62.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for wai-agent-audit 0.1.0
File Interpreter ABI Platform
wai_agent_audit-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 119.7 kB

Release files / wai_agent_audit-0.1.0.tar.gz

Download URL wai_agent_audit-0.1.0.tar.gz
Size 62.8 kB
Tags Source
SHA-256 checksum
How to use checksums
bd58804054ed491a5b46e9ecec784f0b18d84aa4f1a2c1f05ddb8524533dc276
BLAKE2b-256 checksum
How to use checksums
de8bc20b36faa83e29a7bb84d085f79c9ea8add3be04502a78f825929598e56e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.13

Release files / wai_agent_audit-0.1.0-py3-none-any.whl

Download URL wai_agent_audit-0.1.0-py3-none-any.whl
Size 56.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
37d71d373eb2746e4b0014dd6cf356d0823304d829147cdafa82da1d00dad29b
BLAKE2b-256 checksum
How to use checksums
60d2c6aae9f939e9e9f02e535d2cf11801b675e8d5e163488bd54a19dc54bf9e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.13

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

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