Last month we wired cMCP’s audit chain into OpenTelemetry. It took about a day, it is opt in behind a single environment variable, and it exports digests rather than payloads. A gateway operator now watches policy decisions in the same dashboard they already watch everything else in, with no new tooling and no new place to look.
That is the boring half. The interesting half is what the gateway is recording underneath, because one blocked tool call produces three different records, deliberately, and each one exists for a different reader.
What one blocked call actually produces
An agent calls a tool through the gateway. Cedar evaluates the policy bundle, and the call is refused with an authority named who could approve it. Three things happen.
The audit chain gets the authoritative entry. It is hash linked, so each entry carries the digest of the one before it. Insert, delete or alter an entry and the chain stops verifying from that point forward. It records the decision, the rule that matched, the tool, the server identity, the latency, and digests of the request and response. Not the payloads. Digests.
Telemetry gets a mirror of that entry. The span carries a nineteen field allowlist, chosen field by field, so that adding a new field to an audit entry does not silently start exporting it. request_payload_hash and response_payload_hash are on that list. The payloads themselves are not, and cannot be, because the allowlist is the whole mechanism. Blocked decisions mark the span as an error, so a dashboard can alert on refusals without parsing attributes.
The TRACE Claim gets the portable version. This is the artifact you hand somebody who does not trust you. It is signed, it carries the policy bundle hash that was in force, and it can be checked by a verifier the counterparty already has, without calling us and without trusting the operator who ran the workload.
So: your on call engineer reads the span, your incident review reads the chain, and your auditor reads the claim. One event, three readers, three formats built for them.
Why that is the design rather than an accident
The instinct is to say this is duplication and should be collapsed into one record. It should not, and the reason is worth being precise about.
The audit chain is authoritative because it is tamper evident and local. The span is convenient because it lands in infrastructure you already run. The claim is portable because it is signed and versioned, which means somebody can verify it in two years with a tool built today.
Those three properties pull in different directions. A record optimised for tamper evidence is not optimised for a dashboard. A record optimised for a dashboard has no reason to be stable across versions. A record optimised for a stranger to verify has to hold its shape for years, which is exactly the constraint that makes it a bad place to put whatever field you thought of this morning.
The useful principle: the record that is easiest to enrich is the one with the least at stake. The span vocabulary can get richer whenever we like, because nothing outside the process depends on its shape. The signed claim cannot, because the entire point of evidence is that somebody else verifies it later with a tool they already have.
Once you see that, the three records stop looking like duplication and start looking like three different contracts with three different parties.
What this gives you today
If you run a gateway with this on, a blocked call is visible in your existing observability stack within a second, correlated with the trace your application already emits, without shipping a payload anywhere. That is the operational win and it costs one environment variable.
The evidence win is larger and slower. Because the claim binds the policy bundle hash, “the agent was refused under this exact policy” becomes something a counterparty can check rather than something you assert in a meeting. AARM v1.0 asks a policy engine for five decision types, and cMCP produces all five through an explicit crosswalk rather than string literals scattered through the evaluator, so an assessor can read the mapping in one file and check it against the bundle.
The honest limit
Two of the five decision types are classified rather than enforced. defer records a blocked call with its advice payload attached, but the gateway does not hold the request open pending an out of band decision, because it has no callback registry. step_up blocks and names an authority, with no in band re submission path yet. Both are in LIMITATIONS.md rather than a footnote. And a claim narrows step_up and defer to deny, because the v1.0 decision vocabulary predates AARM and widening it would make older verifiers reject claims they should accept. The audit chain and the span both keep the finer value, so the distinction survives where it matters operationally. Separately, pip install cmcp currently resolves a cryptography carrying two advisories reachable through certificate chain verification, pending an upstream release, and that is tracked in the open at https://github.com/agentrust-io/cmcp/issues/471.
Where this is going
The wider problem is that governance facts live in the wrong places. Policy decisions in a policy engine’s logs, approvals in a database, cost in a billing module, none of it correlated with the trace your application already emits.
So we put the contract in the open this week, at https://github.com/agentrust-io/agentrust-telemetry. It is backend neutral and deliberately not a tracing backend, a policy engine, an agent framework or a dashboard. You keep all of those. It defines six event families, including an evidence lifecycle that carries run completeness and TRACE finalization status, as JSON Schema with conformance fixtures and an independent runner, so you can check an implementation rather than take its word. It is alpha, and I am putting it out at that maturity on purpose, because the argument worth having is which facts belong in the contract at all, and that is much cheaper before there is an API to keep working.
Here is the question I do not have a settled answer to. If your telemetry and your evidence disagree about the same event, which one is authoritative? We say the audit chain is, and telemetry is a mirror. But the mirror is the more detailed object, and I can imagine an incident where the span is the only place a distinction was preserved.
If you run policy enforcement anywhere in your stack, go and diff the two records for one blocked call. I would genuinely like to know whether anyone else’s line up.
Top comments (0)