Repository navigation
Case Study OSS Agents
On 2026-10-12 we integrated Tracekit into two real open-source agent projects. We followed the docs a customer would read, and used a real model (gpt-5.6-luna) on fixed sets of tasks. We ran each task set without Tracekit, then with it, and compared the signed records with each project's own logs.
This page covers what the integration took, what the signed record caught, what it cost, and how to check the evidence yourself.
| OpenAI customer-service agents demo | CrewAI | |
|---|---|---|
| Project |
openai-cs-agents-demo at bd7bfca: a FastAPI airline support app on the OpenAI Agents SDK 0.23, with a triage agent, five specialists and guardrails |
CrewAI 1.15.27: sequential and hierarchical crews, with manager delegation |
| Tracekit path |
tracekit.instrument() (the OpenAI Agents adapter and model-call capture) |
no adapter: CrewAI's global tool hooks, following the custom quickstart, plus tracekit.instrument() for model calls |
| Tasks | 10 conversations | 9 crews (33 tool calls) |
| Integration | 4 lines, plus about 12 to give each conversation its own run | about 60 lines of hook glue |
| Overhead | +1.6% wall time | 73.8 s → 81.9 s for the 9 crews (own call ids); 85.9 s with the model's ids, including 4 approval waits of about 3 s |
| Result | every tool call and model response recorded; 0 false reconcile findings; 0 gaps; bundle VERIFIED offline |
33/33 calls decided and reconciled; deny, ask and reject held; Python and TypeScript verifiers agree on all 25 bundles |
Integration.
-
tracekit.instrument()at startup records every tool call through the OpenAI Agents adapter. It also records every model response through the model SDK's capture. - A chat server has many conversations in one process, so each conversation gets its own run, kept open across
requests. Each turn enters it with
with conversation.current():.
What the signed record caught. These were bugs in Tracekit, found by its own records, and fixed before this page was written:
-
Lost streamed responses. On the first pass, verify reported 12 of 12 honest tool calls as
fabricated: calls with no model response asking for them. The OpenAI Agents SDK streams through a call path that our model-call capture missed. The records were right to complain: no recorded response asked for those calls. After the fix, a rerun shows 0 fabricated calls. -
One run per server.
instrument()opened one run per process, so a long-running server mixed every conversation into one run. The signer then closed it after an hour idle. Fixed: work follows the current run, and docs say to use a run per conversation. -
Approvals with a typed context. An
askfailed when the app's run context was a pydantic model rather than a dict, which is how real apps write it. Fixed.
What it can't show. Two tools here don't name their target:
-
cancel_flight()takes the flight from the conversation's context; -
issue_compensation(reason)has no amount.
A signed decision records the arguments it was given, so for these tools it has nothing to show or for a rule to match. Design consequential tools so that their arguments name what they act on.
Result. After the fixes, the rerun had a run per conversation: 10 conversation runs, all finalised, even across a signer restart. It had 0 reconcile findings, 0 gaps and +1.6% wall time, and the exported bundle verified offline.
Integration.
- CrewAI 1.15 has global tool hooks:
before_tool_callcan block a call by returningFalse, andafter_tool_callsees the result. The gate from the custom quickstart goes in them:-
before_tool_callcallsdecideon the tool and its input. On anask, it callsapproval_requestandapproval_wait, and the crew waits in the hook. It then callsapproval_consume, and returnsFalseunless the call may run. -
after_tool_callrecords the outcome withcomplete, withstatus="error"when CrewAI reports a tool error.
-
-
tracekit.instrument()records the model calls, including the manager agent's.
What the signed record caught.
-
A denied tool ran while the signer was down. CrewAI swallows any exception a hook raises, and then runs the tool.
The Python client's
decideraisedSignerUnavailablewhen the signer couldn't be reached. So with the signer stopped between two calls,close_accountran, although the policy denies it.- The record showed it: the run held a decision for the call before, and nothing for
close_account. The bundle verified onlyTO HEAD 1 (open). - The rule that follows: a gate in a framework hook must block on any exception. The glue catches everything in
before_tool_calland returnsFalse, so an outage blocks the call (the run's fail mode isclosed).
- The record showed it: the run held a decision for the call before, and nothing for
- No call ids in the hooks. CrewAI's hook context has the tool name and input but not the model's tool-call id. With invented ids, the signer couldn't pair a single decision with the model response that asked for it: 33 calls, 66 false reconcile findings. Passing the model's id into the hook (a small wrapper around CrewAI's executor) paired 33 out of 33, with zero findings.
-
Policy and provenance held.
- A deny rule kept
close_accountfrom running, and the model was told the rule ids. - An
askonrefund_orderwaited for an approval given withtracekit approvals approve. The refund then ran exactly once. - A rejected
delete_customernever ran. -
send_emailafter the agent read an untrusted ticket was held, with the ticket recorded as the source of the address.
- A deny rule kept
-
Mandatory gateway. With
gateway_mandatory: true, 4 crews produced zero reconcile records.
Export a run and verify it with a trust config built from sources you trust, not from the bundle:
tracekit export --v2 --config signer.yaml --run RUN_ID -o run.tkb
tracekit signer trust --config signer.yaml -o trust.json
tracekit verify run.tkb --trust trust.json
npx --package ./cygnuxlabs-tracekit-<version>.tgz tracekit verify run.tkb --trust trust.json # the TypeScript verifier must agree-
Integrity: VERIFIED.says that the run's records are the ones the signer signed, complete from registration torun.final. - The
tiersandcoveragelines say what was observed. Calls captured in the agent's own process are tier T1; calls a gateway or executor saw are tier T2. - Any gap or reconcile record names what the signer saw but wasn't told, or was told and didn't see.
Verdicts explains each line, and known limits lists what a verified bundle doesn't prove.
The custom agent example runs the same gate with a scripted model: no API key, no network.
python examples/v2/custom/agent.py --scriptedIt starts a dev signer and runs three calls:
-
lsis allowed; -
sudo rm -rf /is denied, and the agent goes on; - an unparsable command waits for an approval, which
--scriptedgives from a second process.
It then exports the run and prints the tracekit verify report.
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