Repository navigation
Quickstarts Custom
Every tool call is decided by the v2 signer before it runs and recorded after, as a signed event. This page adds it to the agent you have in three steps. It uses the dev signer, which runs as your own user (see what dev assurance means).
pip install tracekit-aiWithout a framework for tracekit.instrument() to wire, your agent asks the signer itself, once per tool call:
from tracekit.sdk.client import Client, result_field
with Client().run(agent="my-agent") as run: # one run; starts the dev signer if none answers
for call_id, tool, args in my_agent_tool_calls():
d = run.decide(call_id, tool, args) # before it runs: allow, deny or ask
if d["decision"] == "allow" and (d.get("unavailable") or run.approval_consume(call_id, tool, args)["ok"]):
result = run_tool(tool, args) # your tool, with exactly those arguments
# its outcome, bound to the decision: the result itself for a tool the policy calls untrusted (provenance)
run.complete(call_id, result=result_field(d.get("send_result"), result))
else:
result = "refused: " + (", ".join(d["rule_ids"]) or d.get("reason", "")) # the model gets the refusalPass the model's raw arguments string as args when you have it: the signer records which form it saw. decide
doesn't raise when the signer can't be reached: the run's fail mode answers, with "unavailable": True (an open fail
mode lets the call run unrecorded, with a warning). Since 1.2, decide returns the run's fail-mode decision with
unavailable: True instead of raising SignerUnavailable: a loop that caught that exception to block the call now
gets {"decision": "deny", ...} and blocks by the decision. approval_consume doesn't raise either: a signer that
can't be reached or refuses answers {"ok": False, ...}, and the call doesn't run. complete never raises: the call
already ran, so a result the signer can't take is a warning, and the signer records the call as decided and not
completed. To record
the OpenAI, Anthropic or Google Gen AI calls made inside the with block into its run as well, add from tracekit import autotrace; autotrace.instrument(None) before it.
Block a call by what your hook returns, never by raising: some frameworks run the tool, or turn the error into an empty
answer, when a hook raises. Everything Tracekit refuses on its own (a model call under a closed fail mode, autotrace's
refusals) raises tracekit.Refused, a PermissionError: let it through your own error handling.
| Framework | What it does with an exception | With Tracekit |
|---|---|---|
| CrewAI | runs the tool when a before_tool_call hook raises |
instrument() wires hooks that block by returning False (CrewAI) |
| LangGraph / LangChain |
ToolNode's handle_tool_errors turns a tool's exception into a message |
the middleware returns its refusal as a tool message, and pauses an ask whatever handle_tool_errors says |
| mcp-agent |
generate_str logs an exception from the model call at debug level and returns ""
|
a fail-closed model refusal reads as an empty answer: treat "" as a failure, or run with fail_modes: {model: open}
|
| PydanticAI | validates a call's arguments before any hook, and asks the model again when they fail | the call is never decided, and reads as reconcile.hook_missing under autotrace; its structured output comes as output tools (final_result, final_result_<Type> per output type): name each in the policy's structured_output:; the Tracekit capability decides every call after validation, and output functions too (Pydantic AI) |
| Your own loop | whatever your except does |
re-raise tracekit.Refused, or return the refusal to the model |
tracekit lastIt exports the most recent finished run, pins the dev signer's key, verifies the bundle and says where it is (the v2 quickstart).
An ask waits for a person; this is the loop with that step:
from tracekit.sdk.client import Client
with Client().run(agent="my-agent") as run:
d, aid = run.decide(call_id, "Bash", args), None # before the call runs: allow, deny or ask (an outage:
# the fail mode, "unavailable": True)
if d["decision"] == "ask":
try:
aid = run.call("approval_request", tool_call_id=call_id)["approval_id"]
run.call("approval_wait", approval_id=aid, timeout_ms=300000)
except Exception: # an outage or a refusal while asking: block the call
d = {**d, "decision": "deny"}
if d["decision"] != "deny" and (d.get("unavailable") or
run.approval_consume(call_id, "Bash", args, approval_id_hint=aid)["ok"]):
... # run the call, with exactly those arguments
run.complete(call_id) # its outcome, bound to the decisionAn offline run of all this (a mock model, no API key), with an approval: python examples/v2/custom/agent.py --scripted
from a checkout.
[PASS] manifest — every file is listed with its SHA-256 and nothing else is in the bundle
[PASS] checkpoint — tracekit.local/... at tree size 12, signed by its pinned log key
[PASS] witness quorum — 0 pinned cosignature(s), 0 required
[PASS] signatures — 11 record(s), keys valid at their position
[PASS] run chain — run '...' of tenant 'default', contiguous from run_seq 0
...
Integrity: VERIFIED.
Assurance: dev; records ed25519; checkpoint Ed25519 only (...); no witness cosignature; approvals: self; ...
Integrity: VERIFIED means every record was signed by the log key in trust.json, the run is one unbroken chain, and a
checkpoint signed by that key includes it: nothing was edited, dropped or reordered after signing. Change one byte of
the bundle and it reads Integrity: FAILED, naming the check (tracekit demo --server shows this). Assurance: dev
says no pinned witness cosigned the checkpoint and the approval was a self-approval. The v2
quickstart explains every line.
- System mode runs the signer as its own OS user, so the agent can't reach its keys, store or policy, and approvals come from another account: system mode.
-
Witnesses cosign the signer's checkpoints from another host, so a rolled-back or forked log shows:
witnesses. Pin them in the trust config and the report reads
Assurance: witnessed. -
Your own policy: the dev signer uses the coding pack plus two demo rules (
tracekit/policy2/packs/dev.yaml); a configured signer takespolicy:insigner.yaml.
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