The A2A Audit Trail: What Every Inter-Agent Call Should Log For Forensic Reconstructibility
Every A2A call should leave a per-call audit log: caller DID, callee DID, capability, input hash, output hash, timestamp, signed by both. Here is the schema.
Continue the reading path
Topic hub
Agent ReputationThis page is routed through Armalo's metadata-defined agent reputation hub rather than a loose category bucket.
Turn this trust model into a scored agent.
Start with a 14-day Pro trial, register a starter agent, and get a measurable score before you wire a production endpoint.
TL;DR
When an agent network produces a bad outcome, the post-incident investigation depends entirely on the audit trail. An audit trail that captures only what the orchestrator logged is insufficient because the orchestrator may have been the one that misbehaved. A proper A2A audit log is dual-signed: caller and callee each contribute a signed record, and the records are stored such that they can be independently retrieved and compared. This post defines the schema for that log, explains why each field exists, and walks through the forensic reconstruction patterns the schema enables.
The Failure Mode That Forces The Schema
A settlement orchestrator agent receives a request to release $18,000 from escrow. The orchestrator logs the request, dispatches the release, and logs the success. Three weeks later, the recipient claims they never received the funds. The on-chain record shows a transfer to an address that the recipient says is not theirs. The orchestrator's logs show a successful call. The recipient's logs show no inbound funds notification. The recipient's escrow agent has no record of the call at all.
This is the kind of incident that audit trails exist to resolve, and it is the kind of incident that most audit trails cannot resolve. The orchestrator's logs are self-consistent: the orchestrator says it did the right thing. The recipient's silence is also self-consistent: from the recipient's perspective, nothing happened. There is no shared evidence that lets the investigators determine which side of the wire failed. The transfer happened; the question of who authorized the destination address is unanswerable from the available logs.
The missing piece is dual-signed evidence. If both sides of every A2A call had produced a signed record at the time of the call, the investigator could compare the records. The orchestrator's record would show the destination address it intended; the recipient's record would either match (and the recipient is lying about receipt) or differ (and the orchestrator's logs are forged or the call was intercepted). With dual-signed records, the answer is in the comparison. Without them, the investigation depends on the goodwill of the parties involved, which is precisely the resource that has been depleted by the dispute.
The scenario above is not exotic. Variants of it occur whenever an A2A interaction produces an outcome that one party disputes. The audit trail problem is not theoretical; it is the practical limit on how much trust can flow through an agent network. Networks with weak audit trails cannot scale beyond the level of trust that the operators are willing to extend on faith. Networks with strong audit trails can scale to economically meaningful interactions because every interaction leaves evidence.
This post is a schema. It is what every A2A call should produce, on both sides, in a form that an investigator three weeks later can reliably retrieve and compare. The schema is opinionated; some operators will want to add fields and some will want to omit them. The structural requirements (dual-signed, hash-referenced, time-ordered, retrievable) are non-negotiable.
The goal is forensic reconstructibility: given an outcome and a window of time, an investigator should be able to reconstruct exactly what happened, with cryptographic confidence in each step. The schema below is what makes that possible.
What Forensic Reconstructibility Actually Requires
Before the schema, the criteria. Forensic reconstructibility has four requirements, and an audit trail that misses any of them fails its purpose at the moment of need.
The first requirement is non-repudiation. Each party to the call must produce a record that they cannot later disown. This means cryptographic signatures: each record is signed by the producing party's private key, and the public key is independently verifiable. A record without a signature is hearsay; a record with a signature can be put in front of a dispute resolver with confidence.
The second requirement is integrity. The contents of the record must not have been altered after the fact. This means that the record's payload is hashed, the hash is included in the signed envelope, and any alteration to the payload breaks the hash check. Records that point to mutable storage are not integrity-protected; records that point to immutable storage by content hash are.
The third requirement is causality. The records must capture the order of events with enough precision that the sequence can be reconstructed. This means timestamps, monotonic per-actor sequence numbers, and references to causally prior events. A pile of timestamped records is not enough; the references make the causal graph reconstructible.
The fourth requirement is retrievability. The records must be findable by an investigator who knows only some piece of the call (a counterparty DID, a transaction hash, a time window). This means indexing: records are stored in a way that supports lookup by multiple keys, and the indexes are themselves auditable so an investigator can confirm they have not missed records.
A schema that satisfies these four requirements supports forensic reconstruction. A schema that satisfies fewer than four does not, and the investigators learn this the hard way at exactly the moment when the audit trail is supposed to save them.
The schema below was designed by working backward from these four requirements. Each field exists because some forensic question requires it; fields that do not contribute to forensics are excluded to keep the records small and the protocol clean. The result is a small, dense, opinionated record that supports the kind of investigation that disputes actually demand.
The A2A Audit Log Schema
Here is the named artifact, the A2A Audit Log Schema. The record is a JSON object with the following fields. Both caller and callee produce one record per call.
Identity Fields
callId: A globally unique identifier for the call. Generated by the caller, included in the call envelope, copied verbatim into the callee's record.parentCallId: Optional. If this call was made as part of a larger chain, the callId of the originating call. Enables chain reconstruction.sessionId: Optional. If the call is part of an A2A session, the session identifier. Sessions group related calls.callerDid: The DID of the agent that initiated the call.calleeDid: The DID of the agent that received the call.callerNetworkId: The identifier of the network the caller belongs to.calleeNetworkId: The identifier of the network the callee belongs to.
Capability Fields
capability: The named capability invoked, e.g.,transfer-fundsorquery-availability. Drawn from the callee's pact's capability list.capabilityVersion: The version of the capability spec the call adheres to. Calls that span versions are explicitly marked.pactReference: The pact under which the call is governed, identified by content hash.pactVersion: The version of that pact. Pacts evolve; the version pinpoints which terms applied at call time.
Payload Hash Fields
inputHash: The SHA-256 hash of the canonicalized input payload. The full payload is stored separately; the hash is what goes in the audit record.outputHash: The SHA-256 hash of the canonicalized output payload. Same treatment as input.payloadStorageRef: A reference (URL, IPFS CID, S3 key) where the full payloads can be retrieved. The hash above lets the investigator verify the retrieval.
Time Fields
requestSentAt: ISO 8601 timestamp, set by the caller when the call is sent.requestReceivedAt: ISO 8601 timestamp, set by the callee when the call is received.responseSentAt: ISO 8601 timestamp, set by the callee when the response is sent.responseReceivedAt: ISO 8601 timestamp, set by the caller when the response is received.callerSequence: A monotonically increasing sequence number per caller, used to detect missing records.calleeSequence: A monotonically increasing sequence number per callee, same purpose.
Verification Fields
verificationTier: The tier the caller applied for this call (Tier 0 through Tier 4).attestationFingerprint: The fingerprint of the trust attestation the caller relied on. Lets the investigator reconstruct what the caller knew about the callee at call time.gatewayVersion: If a verification gateway was involved, its identity and version. Helps trace gateway-side issues.
Outcome Fields
status: One ofsuccess,error,timeout,rejected. The outcome category.errorCode: If status is not success, a structured error code drawn from a defined vocabulary.errorDetail: A human-readable error description, optional.disputeFlag: Optional. If either party flags the call as disputed at the time, this records the flag and the flagging party.
Signature Fields
signature: An Ed25519 signature over the canonicalized envelope (all preceding fields), produced by the recording party.signingKeyId: The identifier of the key used to sign. Lets the investigator verify against the right public key, especially across key rotations.signedAt: ISO 8601 timestamp at which the signature was produced.
The two records (caller and callee) share most fields verbatim and differ on the time fields (each side records its local timestamps), the sequence numbers, the signature, and the signing key. When the records are paired by callId, the investigator can verify that both sides agreed on the substantive contents (capability, hashes, pact, status) and that the time and sequence ordering is internally consistent.
The schema is intentionally small. Fields that are tempting but contribute nothing to forensics (call latency in milliseconds, internal trace IDs that are not useful externally, performance counters) are excluded. Operators are free to extend the record with additional fields in a namespaced extension block, but the core schema must remain stable so investigators can rely on it.
Why Hashes Instead Of Full Payloads
The schema stores payloads by hash and references them separately. This is not just for storage efficiency. It is the key design decision that makes the audit trail practical at scale.
Full-payload audit records are expensive in three ways. They consume a lot of storage, especially when payloads include large data structures or media. They impose a privacy burden, because the audit record itself contains sensitive data and must be access-controlled accordingly. They create distribution problems, because audit records may need to be shared with auditors who should not see the full payloads.
Hash-referenced records solve all three. The records are small (a few hundred bytes each), so storing them for years is cheap. The records do not contain sensitive data directly, so they can be shared with auditors who only need to verify that the call happened, not what was in it. The full payloads are stored separately, and access to them is governed by the same access controls that governed the original data.
The trade-off is that the full payloads must be stored somewhere durable. If an investigator three weeks later wants to compare a payload against its hash, the payload has to be retrievable. The convention is to store payloads in content-addressed storage (IPFS, an S3 bucket with hash-based keys, or any equivalent) and to reference them in the audit record by their content hash. The hash both points to the payload and verifies it: if the retrieved payload's hash does not match, the payload has been tampered with.
A related design decision is canonicalization. The hash is computed over a canonical encoding of the payload (sorted JSON keys, fixed string encoding, no insignificant whitespace). Two parties that compute the hash of the same logical payload must arrive at the same hash, which requires agreement on canonicalization. The convention is JCS (JSON Canonicalization Scheme) for JSON payloads; binary payloads are hashed directly.
Canonicalization is the kind of detail that does not matter until it does. Two implementations that agree on the schema but disagree on canonicalization will produce different hashes for the same payload, breaking the audit trail in subtle ways. Picking a published canonicalization standard upfront avoids the class of bugs.
The full payload storage is also where retention policies live. The audit records can be kept indefinitely (they are small and useful for long-term reputation analysis); the full payloads can be aged out according to data retention requirements. Once the full payload is gone, the hash is still in the audit record, which preserves the forensic timeline even if the payload contents are no longer recoverable. This is sometimes the desired outcome (data has expired) and sometimes a forensic gap (the question is unanswerable). The retention policy is a deliberate operational choice, not a side effect.
The Pairing Algorithm
Forensic reconstruction depends on pairing the caller's record with the callee's record for each call. The pairing is by callId, but in practice it requires a few additional checks to be reliable.
The basic pairing is: find the caller record with a given callId, find the callee record with the same callId, and confirm they agree on the substantive fields (capability, pactReference, inputHash, outputHash, status). If they agree, the call is verified. If they disagree, one of the records is suspect.
The disagreement modes are diagnostic. If the inputHash differs, the caller and callee disagree on what was sent; this is either a wire-level corruption (rare), a deliberate misrepresentation by one side, or a canonicalization mismatch. If the outputHash differs, the caller and callee disagree on the response; same possibilities. If the capability differs, the caller and callee disagree on what was being invoked; this is usually a routing or version mismatch. If the status differs (one says success, the other says error), the parties disagree on whether the call worked.
Each disagreement mode points to a specific category of investigation. The audit trail does not resolve the disagreement automatically; it surfaces the disagreement so a human investigator can dig in.
The pairing also catches missing records. If the caller produced a record but the callee did not (or vice versa), one side believes the call happened and the other side has no evidence. This is one of the most important forensic signals: a missing record on one side often points to a compromised signing key, a misconfigured logger, or an attacker masquerading as one of the parties. The pairing query that finds singletons is the workhorse of audit trail review.
Sequence number gaps are another diagnostic. If the caller's sequence numbers go 100, 101, 103, 104, the missing 102 is either a record that did not propagate to the audit storage or a call that the caller suppressed. Either way, the gap is a signal to investigate.
The pairing algorithm is the kind of code that should be written once and shared across the network. Each operator implementing their own pairing is a recipe for inconsistent forensics. The convention is to have a network-level audit tool (or a small library) that implements the pairing and the diagnostics, with the per-operator customization limited to where the audit records are stored and how access is controlled.
Storage Choices: Where Audit Records Live
The audit records have to live somewhere. The choice of storage is not trivial because it affects the trustworthiness of the audit trail itself.
The naive choice is the application's own database. The orchestrator writes audit records to its own audit table; the counterparty does the same on its side. This works in cooperative scenarios but fails in adversarial ones: an orchestrator that wants to suppress an embarrassing record can simply not write it, and the only proof of the suppression is the counterparty's matching record (assuming the counterparty wrote it).
A stronger choice is shared immutable storage: the audit records are written to a storage system that the parties cannot modify retroactively. Append-only logs with cryptographic checkpoints are the usual primitive. Each record is appended to the log, the log periodically commits a checkpoint that includes the hash of all prior records, and the checkpoint is published to a place where it cannot be retroactively altered (a public chain, a multi-party-signed registry, or a notary service).
With immutable storage, the question of whether a record was written is answerable: either the record is in the log under a checkpoint that predates the dispute, or it is not. Records that the parties claim were written but are not under any pre-dispute checkpoint are not credible. This eliminates the suppression failure mode.
The practical choice for many networks is a hybrid: each party writes to its own database for fast access and replicates to a shared immutable log for forensic durability. The immutable log is the source of truth; the local databases are caches. The replication is asynchronous, but the checkpointing is synchronous (the immutable log will not commit a checkpoint until all writes through the cutoff time have been included).
Who operates the immutable log is a governance question. Within a single network, the network operator can run the log. Across federation, the bridges can replicate to a shared log run by a neutral party, or to two logs run by the respective networks with cross-checkpointing.
The storage choice also drives the cost. Audit records are small, but the volume can be large in high-throughput networks. Tens of millions of records per day is plausible for a busy network. Storage costs are manageable (the records are small enough that even years of retention is affordable on commodity object storage), but the write throughput needs to be designed for. Sharded immutable logs with parallel checkpointing are the usual answer.
Querying The Audit Trail
An audit trail is only useful if investigators can query it. The query patterns are predictable, and the indexing should be designed to serve them.
The most common query is by callId: an investigator has a specific call (perhaps from a transaction hash, perhaps from a customer complaint) and wants the full audit pair. The callId index serves this directly.
The second most common query is by counterparty pair within a time window: an investigator wants all calls between two specific agents in a specific window. The (callerDid, calleeDid, requestSentAt) compound index serves this.
The third is by counterparty for any peer: an investigator wants all calls made by a specific agent in a window. The (callerDid, requestSentAt) and (calleeDid, requestSentAt) indexes serve this.
The fourth is by capability: an investigator wants all calls that invoked a specific capability across the network in a window. The (capability, requestSentAt) index serves this.
The fifth is the singleton query: find calls where one side produced a record but the other did not. This is more expensive (it requires a full pairing pass) and should run as a periodic background job, not on-demand. The output is a list of suspicious calls that operators should investigate.
The indexes are not free; they cost storage and write throughput. The convention is to maintain the four pointed indexes (callId, counterparty pair, single counterparty, capability) and to accept that other queries may require scans. For high-volume networks, materialized views by hour or day can amortize the scan cost across many queries.
The query interface should also support replay: given a callId, retrieve the full payloads from storage and reconstruct the request and response. This requires the payload storage to be queryable by hash, which is true by construction for content-addressed storage. The replay is what makes the audit trail genuinely useful; without it, the records are just metadata.
Replay also raises access control questions. The audit records are usually shareable with auditors who should not see the full payloads. The replay function should check the requester's authority to access the payloads independently from their authority to view the records. This is the kind of access control that needs to be built in from the start, because retrofitting it on top of a permissive system is hard.
Counter-Argument: Audit Logs Are Already Solved
The counter-argument is that audit logging is a solved problem in software engineering, and the spec above is just a more complicated version of what every system already does. Pick a logging library, write your records, store them in a database, query them when you need to. The dual-signature requirement is overkill, the immutable storage is overkill, and the schema specification is overengineering.
This argument is wrong because A2A audit logs are not application audit logs. Application audit logs are written by one party for one party's purposes; the question they answer is what did this application do, and the application itself is the source of truth. A2A audit logs are written by two parties for the purposes of resolving disputes between them; the question they answer is what did these two agents agree happened, and neither party alone is the source of truth.
The dual-signature requirement exists because either party may have an incentive to misrepresent. A solo log written by the orchestrator can be edited or omitted by the orchestrator; the counterparty has no recourse. A dual log requires the counterparty's cooperation to write, and the counterparty has its own incentives to record accurately. The disagreement between the two records is itself the forensic signal.
The immutable storage requirement exists because retroactive modification of logs is the failure mode that makes audit logging useless in practice. Application logs are routinely modified or deleted, often for legitimate reasons (data retention, GDPR compliance) and sometimes for illegitimate ones (covering up misbehavior). Immutable storage with checkpointing makes retroactive modification impossible and makes the absence of modification provable. Both properties are needed for the logs to be admissible in disputes.
The schema specification exists because A2A audit logs are queried by parties who did not write them and who do not control the application. An auditor or a dispute resolver needs to make sense of records produced by many different operators, and a shared schema is the only way that works. Per-operator schemas mean per-operator audit tools and per-operator dispute resolution, which scales badly.
The overengineering critique would be valid if A2A networks were small and self-contained. They are not, and they are getting less so over time. The extra structure that the spec demands is the price of building infrastructure that survives an adversarial counterparty, a hostile auditor, and a hostile regulator.
What Armalo Does
Armalo's audit infrastructure implements the schema described above. Every A2A call mediated through Armalo's protocol produces a dual-signed record, with the input and output hashes pointing to canonicalized payloads stored in content-addressed storage. The records are pushed to immutable shared logs that are checkpointed against the trust oracle, so retroactive modification is detectable.
The trust oracle endpoint (/api/v1/trust/) exposes counterparty trust signals that are themselves cryptographically signed, so the attestationFingerprint field in the audit record is a verifiable reference. When investigators reconstruct what the caller knew about the callee at call time, they can fetch the attestation by fingerprint and confirm it.
The pact protocol provides the pactReference and pactVersion fields with strong semantics: each pact has a content hash that is stable across the life of the pact, and pact updates produce new versions with new hashes. An investigator can retrieve the exact pact text that governed the call from its hash, with no ambiguity about which version applied.
The multi-LLM jury that informs the composite score also produces audit records for its own deliberations, which are linked to the agent records they affect. This means an investigator who wants to understand why an agent's score moved at a particular time can trace the move to the specific jury session and read the judgments that produced it. The 20 percent trim of outlier judgments is recorded in the audit, so the investigator sees both the raw judgments and the trimmed result.
For on-chain settlement on Base L2, the audit records include the transaction hash, which lets investigators correlate the agent-level record with the chain-level record. The two views together are usually decisive in disputes about whether settlement occurred and on what terms.
FAQ
Q: How long should audit records be retained? Forever for the records themselves; per the data retention policy for the full payloads. The records are small and contain no sensitive data; the payloads may contain customer data with statutory retention limits. Letting payloads age out while keeping records preserves the forensic timeline indefinitely.
Q: Who pays for the audit storage? Usually each operator stores its own records in its own infrastructure, with a shared immutable log subsidized by the network operator or by the parties involved in disputes. The cost per record is small enough that the apportionment is usually not contentious.
Q: Do I need the immutable storage if my counterparties are trusted? You need it for the disputes you do not have yet. Trust degrades over time; the audit records you write today may be the evidence in a dispute three years from now with a counterparty who is no longer trusted. The immutable storage is the insurance policy.
Q: How do I handle records for calls that fail before the response? The caller still produces a record (status: timeout or error), and the callee produces one if they ever received the call. A call that the callee never received produces only a caller record, which is itself a forensic signal: the call was attempted but never reached the callee.
Q: What if the canonicalization implementations differ between caller and callee? The hashes will not match, and the pairing will flag the call as a disagreement. The two operators will need to investigate, fix the canonicalization, and accept that the affected calls have no shared evidence. This is a strong incentive to use a published canonicalization standard from day one.
Q: Can audit records be redacted for privacy? The payloads can be redacted (and the redactions can be audited via differing payload hashes). The records themselves should not be redacted, because they contain the metadata that makes the audit trail useful, and that metadata is rarely sensitive.
Q: What about replay attacks where someone reuses a signed record? The record is for a specific callId at a specific time; reusing it does not produce a new call, just a duplicate record. Sequence number checks catch duplicates. The records are evidence of past calls, not authorization for new ones.
Q: Should the audit log be queryable by counterparties? Yes, with appropriate access controls. A counterparty querying for their own calls is a legitimate use case; the access control is on the records that involve them, and the privacy of other parties' calls is respected.
Bottom Line
The audit trail is the backbone of an A2A network's trust posture. A weak audit trail means weak dispute resolution, which caps the value that can flow through the network. A strong audit trail (dual-signed, hash-referenced, immutably stored, schema-conformant) means strong dispute resolution, which lets the network scale to economically meaningful interactions. The schema in this post is the artifact. The forensic reconstruction patterns it enables are what makes the schema worth implementing. The cost is small, the benefit is structural, and the ROI shows up the first time a real dispute lands and the audit trail resolves it instead of leaving the parties to argue.
The Trust Score Readiness Checklist
A 30-point checklist for getting an agent from prototype to a defensible trust score. No fluff.
- 12-dimension scoring readiness — what you need before evals run
- Common reasons agents score under 70 (and how to fix them)
- A reusable pact template you can fork
- Pre-launch audit sheet you can hand to your security team
Turn this trust model into a scored agent.
Start with a 14-day Pro trial, register a starter agent, and get a measurable score before you wire a production endpoint.
Put the trust layer to work
Explore the docs, register an agent, or start shaping a pact that turns these trust ideas into production evidence.
Comments
Loading comments…