ADR 0032: AGT emits TRACE v0.1 Trust Records¶
- Status: accepted
- Date: 2026-06-16
Context¶
AGT records every tool call via a Merkle-chained audit log (MerkleAuditChain, AuditEntry). Entries are signed with HMAC-SHA256 and chained by hash. The chain is tamper-evident within a deployment but not portable: verifying it requires the shared HMAC secret, which means only the operator can verify. A regulator, auditor, or downstream principal cannot check the evidence without trusting the operator's key management.
TRACE v0.1 (agentrust-io/trace-spec) is an EAT-profile (RFC 9711) signed claim that any holder of the public key can verify offline. It defines eleven required fields covering agent identity, runtime measurement, policy binding, data classification, build provenance, and tool transcript. It is the portable evidence format that makes AGT's governance decisions inspectable outside the deployment boundary.
AGT already has the raw data for most TRACE fields: the Merkle chain tip covers tool_transcript.hash, SessionState carries the monotonic data_class, PolicyInterceptor evaluates and records Cedar policy decisions. The gap is the TRACE envelope, the EdDSA signature over the payload, and a handful of fields (model, runtime, build provenance) that must be injected from configuration.
Phase 2 of TRACE (hardware attestation: TEE-measured policy bundle, TEE-bound key in cnf.jwk, SCITT receipt in transparency) is explicitly out of scope for AGT. cMCP and other runtimes that embed AGT handle TEE attestation at their own boundary. When cMCP emits a TRACE record, it supersedes AGT's software-only claim for that session. AGT's Phase 1 record is the baseline evidence for deployments that do not run inside a TEE.
Subject identity: TRACE v0.2 accepts both spiffe:// and did: URIs in subject (agentrust-io/trace-spec#35, merged in trace-spec v0.2.0). AGT uses did: identities and emits them directly in subject. No parallel SPIFFE identity is required.
Decision¶
AGT emits one TRACE v0.1 Trust Record per session, at session close, via a new TRACEAuditSink driven by GovernedCallable.close_session().
Scope of AGT's TRACE record (Phase 1, Level 0 software-only):
eat_profile: constant"tag:agentrust.io,2026:trace-v0.1"iat: Unix epoch seconds taken from the lastAuditEntrytimestampsubject:agent_didpassed togovern()(must be a DID or SPIFFE URI; a bare identifier like"*"emits a warning and skips the record)model,runtime,build_provenance: injected fromTraceConfig;runtime.platformis"software-only",runtime.measurementis SHA-256 of the Merkle root hashpolicy.bundle_hash: SHA-256 of the Cedar policy bytes, captured atGovernedCallableconstruction timepolicy.enforcement_mode:"enforce"(Phase 1; advisory mode is a future extension)data_class: taken fromTraceConfig.data_class(defaults to"internal")appraisal.status:"affirming"for Phase 1transparency: empty string for Phase 1tool_transcript.hash: SHA-256 of the sort-keys canonical JSON of theAuditEntrylist for the sessiontool_transcript.call_count: total number of entries in the chain
Wire format:
The record is a signed JSON object (not a compact JWT). agentrust-trace's sign_record() embeds the Ed25519 signature as signature (base64url, no padding) and the public key as cnf.jwk directly in the JSON dict. TRACEAuditSink calls TrustRecord.model_validate() on the signed dict before writing to catch schema violations at emit time. The output is written to a UTF-8 JSON file at TraceConfig.output_path.
Key management:
Key loading is delegated entirely to agentrust_trace.load_signing_key(). AGT does not manage key material directly. The agentrust-trace package documents the expected environment variable for key injection; AGT does not re-expose it.
Config surface:
TraceConfig is a dataclass with: output_path, model_provider, model_id, model_version, build_provenance_slsa_level, build_provenance_digest, appraisal_verifier, data_class. Pass it as trace=TraceConfig(...) to govern() or as GovernanceConfig.trace. The feature is default-off: omitting trace= leaves the existing audit sinks unchanged.
GovernedCallable.close_session() triggers emission and returns the path of the written file, or None if the audit log is empty or the agent ID is not a DID/SPIFFE URI.
Implementation:
agentmesh/governance/trace_sink.py:TraceConfig,session_to_trust_record(),TRACEAuditSinkagentmesh/governance/govern.py:GovernanceConfig.trace,GovernedCallable.close_session()- Runtime dependency:
agentrust-trace>=0.2.0(imported insideemit()to keep the import optional at load time)
What does not change:
AuditEntry, MerkleAuditChain, PolicyInterceptor, and SessionState are unchanged. TRACEAuditSink is an adapter over the existing chain. No existing sink is affected. The HMAC-chained audit log continues to be written in parallel.
Consequences¶
- AGT sessions produce a signed, portable evidence record verifiable by any holder of the public key -- no shared secret, no operator trust required for verification.
- Deployments that do not pass
trace=TraceConfig(...)are unaffected. The feature is additive and default-off. tool_transcript.hashand the Merkle chain tip are independently derivable from the sameAuditEntrylist, making the TRACE record and the audit log mutually verifiable without a shared secret.- Phase 2 (hardware attestation) requires no AGT changes. When cMCP or another TEE runtime emits a Level 2 TRACE record over the same session, it carries a TEE-measured
policy.bundle_hashand a TEE-boundcnf.jwkthat supersede AGT's software-only fields. The two records are linked by the sharedsubjectandtool_transcript.hash. did:identities are emitted directly insubject-- no SPIFFE SVID required. TRACE v0.2 (agentrust-io/trace-spec#35) acceptsdid:natively.- CBOR-COSE wire format is deferred to a future ADR if constrained-device deployments require it.
References¶
- ADR-0017 (Merkle chain for audit tamper-evidence) -- the chain this sink reads.
- ADR-0019 (OTel BatchSpanProcessor pattern for event sink) -- sink protocol.
- ADR-0025 (structural typing for sink and source protocols) -- the Protocol this sink implements.
- ADR-0009 (RFC 9334 RATS architecture alignment) -- the attestation framing TRACE extends.
- agentrust-io/trace-spec v0.2.0 -- the claim schema and conformance tests.
- agentrust-trace v0.2.0 (PyPI) --
TrustRecord,sign_record,load_signing_key. - agentrust-io/cmcp#124 -- Phase 2 TEE enforcement; the runtime that will supersede this record for TEE deployments.