Repository navigation
Concepts Tool Call Lifecycle
Every tool call an agent makes goes through the same steps: the client asks the signer whether the call may run, the
call runs (or doesn't), the client reports the result, and the signer records each step as a signed record. Records
belong to a run, and a run ends with a signed run.final. This page follows one call and one run from start to end.
sequenceDiagram
participant A as Agent's client
participant S as Signer
participant P as Approver
participant W as Witness
A->>S: register_run
S-->>A: run_id, run_token (run.registered)
A->>S: decide(tool, args)
S-->>A: allow, deny or ask (policy.decision)
opt ask
A->>S: approval_request, approval_wait
P->>S: approval_decide
A->>S: approval_consume
end
Note over A: the tool runs
A->>S: complete(decision_id, args_digest, result)
S-->>A: ok (tool.result)
A->>S: close_run
Note over A,S: grace window, gaps, reconciliation, run.final
S->>W: checkpoint + consistency proof
W-->>S: cosignature
register_run returns a run_id and a run token. The signer writes run.registered, which records the agent,
the isolation the signer measured for the caller, the fail modes that apply, the caller's identity, and (when
configured) the harness process and the parent run.
The run token is a capability for that one run: b64url(payload).b64url(HMAC-SHA256(run_token.key, ...)), with the
tenant, run id and caller identity in the payload. It is bound to the identity that registered the run, so in system
mode only that OS user can use it. It is an HMAC under its own key, not the evidence signing key, so it can never stand
in for a signature. A run that has gone final refuses every write. Sub-agents register with their parent's token,
which links the runs.
The client sends decide with the tool name, the arguments, how they were obtained (args_source: raw, parsed or
coerced), an optional class hint, the client's call id and attempt number, and a client counter.
The signer then:
- parses the arguments strictly (canonical JSON rules) and denies arguments that aren't valid (
TK-ARGS-INVALID); - classifies the tool from its own policy; a hint can only make the decision stricter, and a disagreement is a signed
class_mismatchgap; - evaluates the policy over the raw arguments (policy);
- applies the run's own state, which can only make the answer stricter: a call that breaks a live constraint the
person stated becomes ask
TK-CONSTRAINT(live constraints), and a held or quarantined run turns it into askTK-HOLDor denyTK-QUARANTINE(containment); - signs a
policy.decisionwith a freshdecision_id, every matching rule id, the policy hash, the regex engine and a commitment to the arguments (commitments).
It answers allow, deny or ask. A flag rule lets the call run and marks it only in the signed record. A deny or
ask is remembered per call id and attempt, so a retry with the same attempt gets the same answer.
For ask, the client asks for an approval, waits, and consumes it just before running the call. The approval is bound
to the exact arguments and used once. The whole flow, and who may answer: approvals.
After the tool runs, the client sends complete with the decision_id, the digest of the arguments it ran, and the
result or error. The signer checks the digest opens the decision's commitment (otherwise args_mismatch), redacts the
result, commits to it, and writes tool.result. If the call ran against a deny, or after an ask with no consumed
approval, the signer also writes capture.gap{executed_against_policy}.
Other events a client can record: observe (content the agent read, such as a page or a ticket), state_write
(digests of saved agent state, to detect tampering with it), model_event (model calls, when no gateway records
them) and constraint_add (what the person's request says not to do for the rest of the run: the Claude Code hook
sends it from the prompt).
close_run, or an idle timeout of one hour (paused while an approval is pending), writes run.closing. A short grace
window follows (5 seconds, longer when a gateway or an egress observer covers the run) in which only late records are
accepted. Then the signer writes:
- completeness gaps, such as decisions that never got a result;
- reconciliation findings, comparing what different capture layers saw (capture and completeness);
-
run.final, with the run's last record and hash, the capture coverage and the coverage matrix.
The signer signs a checkpoint of the whole log after each run.final, every 10 seconds while the log grows, and on
request (at most once a second). It sends each checkpoint to its witnesses with a consistency proof, and at most once an
hour anchors one in Rekor. A run's records are covered once a checkpoint includes them.
(Witnesses and monitoring.)
tracekit export --v2 --run RUN_ID -o run.tkb # one run as one bundle
tracekit verify run.tkb --trust trust.json # offline, against the keys and witnesses you pinned
tracekit last # the dev signer's latest run: export, pin and verify in one stepWhat the verifier checks: verification and assurance.
| Value | Set by | Why it matters |
|---|---|---|
seq, prev_hash (log chain) |
Signer | Orders every record in the log |
run_seq, run_prev_hash (run chain) |
Signer | Orders a run's records; a gap or reorder shows |
decision_id, approval_id, commitments, timestamps |
Signer | Bind results and approvals to decisions |
| Isolation, fail modes, gaps, tamper records | Signer only | No request field can carry them, so an agent can't claim them |
tool_call_id, attempt, client_seq, request_id
|
Client | Correlate calls and retries; a skipped client_seq becomes a signed gap |
Clients and services talk to the signer over a Unix socket, loopback TCP (Windows dev mode) or HTTPS, in JSON frames: one per line on a socket, one per request over HTTPS. The protocol is versioned: Tracekit 1.4 speaks version 19 and its signer serves clients from version 15 up, so older clients keep working. A client refuses a signer that can't serve its version, so upgrade the signer before its clients (deploy).
| Group | Calls |
|---|---|
| Runs and events |
register_run, decide, complete, observe, state_write, model_event, constraint_add, close_run, status, read, checkpoint_nudge
|
| Approvals |
approval_request, approval_wait, approval_decide, approval_decide_on_behalf, approval_get, approval_consume, approval_list, approval_abandon, approval_review, approval_revoke, approval_agents, passkey_register
|
| Services |
delegate_run, tailer_heartbeat, tailer_lost, observe_effect, decision_import
|
| Content and findings |
content_get, content_erase, why_finding, review_finding
|
| Operators |
incident_list, incident_show, incident_resolve, run_hold, run_quarantine, run_resume, response_pause, response_resume, suspend, unsuspend
|
What the newer versions added: 17 (Tracekit 1.3) the gateway's tool-result digests for
verified tool results; 18 kernel sensor effects on observe_effect; 19 review_finding
(signed findings of tracekit analyze --v2), constraint_add, OpenShell's imported events and the gateway's
amended fields.
State-changing calls carry a request_id. A retry with the same id and payload returns the original answer; the same id
with a different payload is a conflict.
Architecture · quickstart · the signer:
tracekit/signer/service.py, the writer:
tracekit/signer/pipeline.py, the RPC contract:
tracekit/signer/rpc_schema.py
Previous: Overview · All concepts · Next: The evidence format
Generated from docs/ at v1.4.1. Edit the docs in the repository, not here.
Get started
- Quickstart: the v2 signer
- Quickstarts
- Quickstart: Claude Agent SDK
- Quickstart: a custom agent
- Quickstart: LangChain and LangGraph
- Quickstart: MCP client
- Quickstart: OpenAI Agents SDK
- Coding agents other than Claude Code
- tracekit doctor
- Walkthrough: Tracekit end to end
How it works
- How Tracekit works
- Overview
- The life of a tool call
- The evidence format
- Verification and assurance
- Witnesses and monitoring
- Policy
- Approvals
- Detection and containment
- Capture and completeness
- Custody
- Provenance and tracekit why
- Deployment and isolation
- Storage, durability and performance
- Integrations
- Security model
- Architecture
- tracekit why: architecture
Integrations
- Integrations
- Adapters
- OpenTelemetry
- Remote clients
- Kubernetes agent-sandbox
- agentgateway
- agentsh and Tracekit
- Browser Use
- Claude Agent SDK
- Claude Code's sandbox
- Codex's sandbox
- CrewAI
- Docker MCP Gateway
- E2B
- Falco
- Gemini CLI's sandbox
- Google ADK
- Haystack
- LangChain v1
- LiteLLM
- LlamaIndex
- MCP client
- microsandbox
- Microsoft Agent Framework
- OpenAI Agents SDK
- NVIDIA OpenShell
- Pydantic AI
- sandbox-runtime's proxy on its own
- Strands Agents
- Tetragon
- Vercel AI SDK
- Wassette
Policy and approvals
- Policy reference
- Approvals
- Findings
- Reading the model's reasoning
- Incidents and containment
- Webhooks
Deploy and operate
- Deploying the signer
- The signer as a container
- The signer on Kubernetes
- The compose stack
- Identity
- Tool executor
- MCP gateway
- LLM gateway
- Egress observation
- Witnesses
- Log monitor
- Record-key issuer
- Signer observability
- Viewer
- tracekit why: causal investigation and replay
- Migrating
- Platforms
Verify the evidence
- Reading a v2 verify report
- Evidence format v2
- Auditor guide
- Sample evidence bundle
- Signing
- What Tracekit guarantees
Security and privacy
- Threat model: the v2 signer
- Threat model: the v1 laptop signer
- HTTP security checklist
- Threat-model review packet
- Privacy
- Known limits and trade-offs
- What's stable
Compliance
- Compliance mapping
- EU AI Act
- ISO/IEC 42001
- NIST AI RMF and the Generative AI Profile
- OWASP Top 10 lists for LLM and agentic applications
Evaluation
Demos
- Demos
- Agent approvers within bounds
- Completeness and tamper evidence
- Detection and containment
- Network coverage: an egress observer
- Guardrails: allow, deny, or ask a person
- MCP tools, gated and recorded
- Prompt injection held by provenance
- Why: which input caused the action
Help