Repository navigation
Monitor
Experimental: this works and is tested, but may change and gets less hardening than the stable core.
Witnesses check that a log only grows. They don't read its records, so they can't see a run finalised twice, a record
after a run.final, a second registration of a run id, or a key retired that nobody announced. A monitor reads
every record and checks those rules, then publishes a signed report. A verifier that pins the monitor uses a fresh,
conflict-free report to raise witnessed to witnessed+monitored.
The signer serves its logs read-only on the metrics port. The record log's entries are every tenant's records (run
ids, tool names, commitments), so the signer serves them only with serve_records: true; checkpoints, hash tiles and
the salted registry logs are always served. Keep that port private to the monitor (metrics: {listen: ..., serve_records: true} in signer.yaml; allow_remote: true to serve a monitor on another host):
tracekit signer vkey --config signer.yaml > log.vkey
tracekit monitor --log http://signer.internal:9464 --log-key "$(cat log.vkey)" --state /var/lib/tracekit-monitor
# with Rekor anchors: the same trusted_root the signer uses, and its publishing key (data_dir/rekor.pub)
tracekit monitor ... --rekor trusted_root.json --publishing-key "$(cat rekor.pub)"The monitor polls every 60 s (--every S, or --once: exit 0 when clean, 1 with conflicts, 2 when the poll could
not finish). Its key is DIR/monitor.key, created on first use (--key FILE to bring one); DIR/monitor.vkey is
the key a verifier pins. Run it somewhere the signer's operator can't change: a monitor they control vouches for
nothing.
--allow KID (repeatable) names a record key the log may declare in a later signer.epoch or retire with
key.retire. Keys of the log's first record are always allowed. Anything else is a conflict.
Each poll reads the record log's checkpoint, checks its signature, and checks it against the last one seen. A smaller
tree is a rollback. A tree that does not extend the last one (another root at its size) is a fork. Both are conflicts and keep both
signed notes as evidence. The monitor then fetches the new records, rebuilds the tree and checks it matches the
checkpoint. Each tenant's registry log in /logs/v0 is read the same way. Then, over every record seen so far:
| Rule | Conflict |
|---|---|
| log chain | a seq or prev_hash that does not continue the log; a record whose hash is not its event's |
| run chain | a run_seq or run_prev_hash that does not continue its run |
| one run.registered per run | a second run.registered for a run id |
| one run.final per run, nothing after it | a second run.final; any record after a run's run.final
|
| run.final head | a run.final whose head is not the run's last record |
| key announcements | a signer.epoch key or key.retire for a kid not passed with --allow
|
| registry leaves | a leaf that points to no record of that type and hash, or two leaves for one record in a registry, or two run hashes for one run; a run.registered or run.final still without a leaf one poll after it was seen |
rekor anchors (with --rekor) |
an anchor the signer serves that doesn't verify or isn't of the log; an entry under the publishing key, in the Rekor shards the anchors are in, that matches no anchor one poll after it was seen |
Conflicts stay in the report once found. State (DIR/state.json, the trees in DIR/tiles/) is saved after each poll,
so a restarted monitor carries on from where it stopped.
A Rekor entry the signer wrote but never got a reply for (a timeout after the write) matches no stored anchor, so it
shows up as a conflict too. Check the signer's witness_failed gaps for that Rekor outage before treating it as
misbehaviour.
DIR/report.json, rewritten each poll:
{"report": {"format": "tracekit.monitor.report.v1", "origin": "tracekit.example.org/log/1", "checked_size": 1042,
"checked_root": "<base64>", "time": "2026-10-10T12:00:00Z", "conflicts": [{"rule": "...", "detail": "..."}],
"rules_checked": ["checkpoint consistency", "..."]},
"sig": "<base64 Ed25519 over 'tracekit monitor report v1\n' + JCS(report)>"}Serve it as a static file (for example python3 -m http.server --directory DIR) so auditors can fetch it.
Pin the monitor in the verifier's trust config. Reports come from the verifier's side, never from a bundle:
{"logs": ["..."], "algs": ["ed25519"], "witnesses": [...],
"monitors": [{"vkey": "tracekit-monitor+1a2b3c4d+AQ...", "class": "customer", "max_age_s": 3600}]}tracekit verify run.tkb --trust trust.json --monitor-report report.jsonA report counts when a pinned monitor signed it, it is no older than max_age_s, it is for the bundle's origin, it
was checked to at least the checkpoint's size, it lists no conflicts, and its monitor is not classed operator. A counting report raises witnessed to
witnessed+monitored. It never raises dev or local. If a pinned monitor reports conflicts for the origin, or a
different root at the checkpoint's size, monitor fails and the conflicts are listed. A stale, unpinned or
not-yet-covering report is a warning and is ignored.
On the metrics port, read-only, in the C2SP tlog-tiles layout:
| Path | |
|---|---|
/checkpoint |
the record log's latest note, with cosignatures |
/tile/<L>/<N>[.p/<W>] |
hash tiles |
/tile/entries/<N>[.p/<W>] |
records, as JSON lines, one per line (a record can be longer than C2SP's uint16 length prefix) |
/registry/<id>/checkpoint, /registry/<id>/tile/...
|
each tenant's registry log (origin <origin>/registry/<id>); entry bundles are C2SP's |
/anchors |
the stored Rekor anchors, as JSON lines |
/logs/v0 |
the logs list (record log and registries) |
The records hold tenant names, run ids, tool names and keyed digests of arguments, never the arguments themselves (privacy.md). Expose the port only to the monitor.
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