Skip to content

Tamper-evident tool-call records behind HookRunner / EventLog #1666

Description

@thisjody

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.

  1. port.HookRunner: a native Go HookRunner on capsule-emit-go, out of tree, in-process.
  2. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions