Opening this per CONTRIBUTING to agree on scope before any code; I'd plan to implement whichever shape you pick.
Think of the harness as a workshop and the event log as the shop's own logbook. This stations a notary at the bench: one sealed slip when a tool is handed over, one when it comes back, stapled together by the first slip's own fingerprint.
docs/agent-identity-model.md names the gap: "The audit trail is the harness's word. The event log records what happened, but it is self-attested. An operator, auditor, or downstream service cannot verify that a given action traces back through a specific subagent, agent, and user."
What this would add. Each tool call becomes a signed, content-addressed record. At PreToolUse: a planned record binding the tool name, CallID, and a digest of Input. At PostToolUse: an outcome record with a digest of the result and confirmed/failed from IsError (both already on the PostToolUse Input payload), carrying the planned record's content address as its parent; CallID is only the lookup key that finds which planned record to point at, the link itself is the content address, verified by recomputation. A third party with the issuer's public key verifies tamper-evidence, chain order, and non-repudiation of each record after the fact, without the harness's log. It is not independence from the harness — a key the harness holds is still the harness signing — which is why who holds the key is #478's question and this composes with it rather than competing. Nor is it the "traces back through a specific subagent, agent, and user" half of your gap: SessionID and user identity do not enter the record as fields (anything inside a tool payload is committed only within the payload digest); attribution is the issuer's job, not the record's.
The format is an individual IETF Internet-Draft, work in progress and not WG-adopted, profiling SCITT signed statements: draft-mih-scitt-agent-action-capsule-04 (https://datatracker.ietf.org/doc/draft-mih-scitt-agent-action-capsule/). The Go producer is action-state-group/capsule-emit-go (Apache-2.0; depends on veraison/go-cose and fxamacker/cbor; not yet tagged as a release, which I'd fix before proposing anything in-tree). The Python producer, capsule-emit, ships the same planned→outcome shape for other frameworks and carries the verifier; the emit→verify round-trip test lives in the out-of-tree repo's CI, so nothing here ever puts Python in your test matrix.
Two shapes, both on ports you already ship.
port.HookRunner: a native Go HookRunner on capsule-emit-go, out of tree, in-process.
port.EventLog / port.ToolCallRecorder: seal at the durable append rather than in the request path. I'd want your read on which process that lives in — if the durable append is at the relay per AGENTS.md, this is a relay-side implementation, not a loop-side port — and whether the log carries the pre-call event; if it only sees completed calls, shape 2 emits one record per call rather than a planned/outcome pair.
Non-goals and limits. Records only; never blocks or mutates (that stays with the guardrail checker and permission policy). Only digests are sealed — data minimization, not confidentiality; low-entropy inputs like paths or short commands would need salted commitments, which the producer supports. Fail-open by default: if the recorder is unavailable the call proceeds, and a gap shows as a planned CallID with no outcome record — veto that default if you'd rather. Individual records are verifiable; that nothing was omitted is not, and that is what registration with a transparency service buys, deferred here.
Questions. Which port, and which process for shape 2? Out of tree is my proposal, with a link from the extension-points docs if you keep a list of community implementations; in-tree only if you'd want it, wherever you'd put it.
Disclosure: I co-maintain capsule-emit and am on a paid engagement with Action State Group.
Opening this per CONTRIBUTING to agree on scope before any code; I'd plan to implement whichever shape you pick.
Think of the harness as a workshop and the event log as the shop's own logbook. This stations a notary at the bench: one sealed slip when a tool is handed over, one when it comes back, stapled together by the first slip's own fingerprint.
docs/agent-identity-model.md names the gap: "The audit trail is the harness's word. The event log records what happened, but it is self-attested. An operator, auditor, or downstream service cannot verify that a given action traces back through a specific subagent, agent, and user."
What this would add. Each tool call becomes a signed, content-addressed record. At PreToolUse: a planned record binding the tool name,
CallID, and a digest ofInput. At PostToolUse: an outcome record with a digest of the result and confirmed/failed fromIsError(both already on the PostToolUseInputpayload), carrying the planned record's content address as its parent;CallIDis only the lookup key that finds which planned record to point at, the link itself is the content address, verified by recomputation. A third party with the issuer's public key verifies tamper-evidence, chain order, and non-repudiation of each record after the fact, without the harness's log. It is not independence from the harness — a key the harness holds is still the harness signing — which is why who holds the key is #478's question and this composes with it rather than competing. Nor is it the "traces back through a specific subagent, agent, and user" half of your gap:SessionIDand user identity do not enter the record as fields (anything inside a tool payload is committed only within the payload digest); attribution is the issuer's job, not the record's.The format is an individual IETF Internet-Draft, work in progress and not WG-adopted, profiling SCITT signed statements: draft-mih-scitt-agent-action-capsule-04 (https://datatracker.ietf.org/doc/draft-mih-scitt-agent-action-capsule/). The Go producer is action-state-group/capsule-emit-go (Apache-2.0; depends on veraison/go-cose and fxamacker/cbor; not yet tagged as a release, which I'd fix before proposing anything in-tree). The Python producer, capsule-emit, ships the same planned→outcome shape for other frameworks and carries the verifier; the emit→verify round-trip test lives in the out-of-tree repo's CI, so nothing here ever puts Python in your test matrix.
Two shapes, both on ports you already ship.
port.HookRunner: a native GoHookRunneron capsule-emit-go, out of tree, in-process.port.EventLog/port.ToolCallRecorder: seal at the durable append rather than in the request path. I'd want your read on which process that lives in — if the durable append is at the relay per AGENTS.md, this is a relay-side implementation, not a loop-side port — and whether the log carries the pre-call event; if it only sees completed calls, shape 2 emits one record per call rather than a planned/outcome pair.Non-goals and limits. Records only; never blocks or mutates (that stays with the guardrail checker and permission policy). Only digests are sealed — data minimization, not confidentiality; low-entropy inputs like paths or short commands would need salted commitments, which the producer supports. Fail-open by default: if the recorder is unavailable the call proceeds, and a gap shows as a planned
CallIDwith no outcome record — veto that default if you'd rather. Individual records are verifiable; that nothing was omitted is not, and that is what registration with a transparency service buys, deferred here.Questions. Which port, and which process for shape 2? Out of tree is my proposal, with a link from the extension-points docs if you keep a list of community implementations; in-tree only if you'd want it, wherever you'd put it.
Disclosure: I co-maintain
capsule-emitand am on a paid engagement with Action State Group.