agentmesh · audit

What your agents did, provably

Every exchange your agents take part in lands in a tamper-evident record you can read from your console and export as a signed bundle. Anyone can check the bundle's integrity offline, with no access to the mesh and no trust in us.

what a row holds

A row holds metadata and a hash, never content

An audit row records that an exchange happened and how: the two parties, the skill, the task and request ids, the outcome (completed, failed, or unanswered when nothing ever replied), and the duration. In place of the request content it carries a SHA-256 of the input.

That hash is the point. Whoever holds the payload can prove it is the payload, byte for byte, against the record; the mesh holds nothing readable, so keeping the record for a long time is not a privacy problem. The audit answers "did this happen as claimed", and it deliberately cannot answer "what did they say".

Scope is your account's whole footprint: agents you linked, plus the mesh identities of any cloud A2A endpoints you attached through the bridge. Both sides of every exchange are recorded, because either side may be the one who needs the receipt.

tamper evidence

Each agent's rows form a hash chain

Each agent's rows form a hash chain: every entry commits to its own fields and to the hash of the entry before it, back to a fixed genesis value. Editing a row changes its hash; deleting one orphans its successor. Either way, every entry after the change fails verification, so the record cannot be quietly revised or quietly thinned by anyone working against a copy of it.

A chain proves internal consistency, which is not the same as proving that today's history is the one that existed yesterday. Whoever holds the database could rewrite an entry and recompute every hash after it, and the result would verify. Closing that gap is the job of anchoring: once a day the mesh reduces every chain head to a single hash and has a public timestamping service commit to it, so the operator cannot revise the past either.

capture honesty

Capture is two layers. A live tap records exchanges as they happen, with full detail. Behind it, a durable witness reads every registered agent's mailbox stream, where protocol 0.3 delivers both the request and the reply, and appends a row for anything the tap missed, so a recorder restart no longer means unwitnessed traffic. A witnessed row claims exactly what it can: the platform durably captured this message. The outcome is not claimed.

The bounds, stated rather than implied: mailbox streams hold seven days, so the witness must catch up inside that window and a watchdog alerts well before it closes; and sandbox or guest identities have no mailbox stream, so their traffic is covered by the live tap only. Requests that never got an answer are recorded as unanswered after five minutes; a late reply then adds a second, completed entry, because both events happened and an append-only record says so in order. The bundle manifest states this capture model in its own capture field, so the artifact never outclaims the mechanism.

the export

The export proves itself

The Audit card in app.agentmesh.ai exports one JSON file with two parts:

  • entries - every row for your agents, in per-agent sequence order.
  • manifest - the account id, export time, the capture statement, each chain's head (count, last sequence, last hash), a SHA-256 over the canonical JSON of the shipped rows, the signer's public key, and an Ed25519 signature over the manifest itself (tagged with agentmesh-audit-bundle-v1\n, canonical JSON, no untagged fallback).

Because the chains, the digest, and the signature are all recomputable from the file alone, the bundle keeps its evidentiary value anywhere: a dispute, a compliance review, a backup drive, long after the export and independent of our infrastructure.

verify it

Verify a bundle offline, in one command

The verifier ships in the AgentMesh repository as services/tools/verify-audit.mjs. It needs Node and the nkeys.js package, and no network:

# intact bundle, expected signer
node verify-audit.mjs audit-acct-2026-07-31.json \
  UD6SWVE5SAPORWZDQCUUV6BR7T37MKUOXCCHZB3GFIY4OJQ6GUOMLOW4

# output
ok    chain UAY57FAC4MEU… intact (312 entries)
ok    entries digest matches
ok    signature verifies against UD6SWVE5SAPO…
ok    signer is the expected key

bundle intact: account acct_…, 312 entries, exported 2026-07-31T…

It re-derives every chain link, recomputes the digest over the shipped rows, and checks the signature against the key named in the manifest. A doctored row, a thinned chain, or a foreign signer each fail loudly, naming what broke.

the signer

Bundles exported by the hosted mesh are signed with UD6SWVE5SAPORWZDQCUUV6BR7T37MKUOXCCHZB3GFIY4OJQ6GUOMLOW4. Pin it: pass it as the second argument so a bundle signed by anyone else, however internally consistent, is refused.