TruthLayer
Network-native, SDK-free evidentiary telemetry for autonomous agent systems
White paper — July 2026
Abstract
Autonomous AI agents now spend money, call tools, execute code, and transact with other agents — largely outside the observability perimeter their operators, auditors, and insurers can trust. Existing telemetry is self-reported: the agent (or the SDK embedded in it) writes its own history, and nothing prevents that history from being edited, elided, or fabricated after the fact.
TruthLayer is a cryptographically sealed, jurisdiction-aware, insurer-underwritable ledger of every action an AI agent takes. It captures agent activity at the network layer — LLM calls, MCP tool invocations, sandboxed code execution, x402 micropayments, and artifact access — without requiring any SDK inside the agent, binds every event into a per-agent hash chain signed by keys the agent never holds, seals each day under a Merkle root anchored to the Bitcoin blockchain via OpenTimestamps, and makes the whole record independently verifiable by anyone holding only public artifacts — no TruthLayer endpoint required.
The platform is composed entirely on Cloudflare primitives (Workers, Durable Objects with SQLite, R2, Workflows, Workers AI, AI Gateway) and runs at the edge across 330+ points of presence. This paper describes the threat model, the evidentiary design, the detection and enforcement layer, the verification story, and the measured behavior of the production system, including results the platform has not yet achieved.
1. The problem: agent activity is unauditable by construction
Three converging trends make autonomous-agent risk uninsurable today:
- Agents act with real-world authority. They hold credentials, spend budgets via micropayment protocols (x402), execute arbitrary code in sandboxes, and call third-party tools over MCP. The blast radius of a compromised or misaligned agent is financial and legal, not just reputational.
- Telemetry is self-reported. Agent frameworks log their own actions. A prompt-injected, compromised, or simply buggy agent controls its own audit trail. For a claims adjuster, a regulator, or a court, “the agent’s logs say X” is testimony from the defendant.
- No neutral observation point exists. Instrumenting the agent means trusting the agent. Instrumenting the model provider covers only LLM calls. What is needed is a chokepoint the agent’s traffic already flows through, operated by neither the agent nor its operator.
TruthLayer’s answer is to make the network itself the witness. When agent traffic transits Cloudflare’s edge — AI Gateway for LLM calls, Monetization Gateway for x402 payments, the Sandbox SDK for code execution, MCP-over-Workers for tool calls, Logpush for egress — each hop emits a webhook into TruthLayer’s ingest pipeline. The agent needs no SDK, cannot suppress the record, and never holds any key that could forge or unmake it.
An evidentiary record is only as strong as its weakest tampering opportunity. TruthLayer’s design goal is that every class of after-the-fact revision is either cryptographically impossible or cryptographically evident — including revision by TruthLayer itself.
2. System architecture
TruthLayer is a set of Cloudflare Workers and Durable Objects with two storage tiers and no servers:
Agent traffic ──► Cloudflare edge products ──► ingest-worker (HMAC-verified webhooks)
│ one Durable Object call
▼
AgentDO (per-agent SQLite: hash chain,
│ admission, quota, signing subkey)
certification / │
registry writes ▼
TenantDO (per-tenant: root keys, agents,
policy, credentials)
│
seal-workflow (nightly Merkle seal + OpenTimestamps anchor)
anomaly-workflow (hourly/nightly behavioral scan, Workers AI embeddings)
kill-switch worker (throttle / kill / reactivate, AI Gateway spend limits)
backup-workflow (nightly encrypted DO exports)
│
▼
R2: private payloads (8 sharded buckets),
public registry / seals / OTS proofs,
Iceberg cold tier (reference DDL, deferred)
Read side: audit-api (tenant-scoped, external auditors, REST + Cap'n Web)
admin-api + dashboard (cross-tenant operator console, Cloudflare Access)
tools/ledger-verify (standalone offline verifier, zero Cloudflare deps)
Cardinality. One TenantDO per customer; one AgentDO per agent, addressed
deterministically as {tenant_id}:{agent_id}. The Durable Object model gives each agent’s ledger
a single-writer execution context: chain construction never needs distributed consensus because
Cloudflare guarantees one live instance per object, with SQLite storage (up to 10 GB per object)
colocated with the compute that mutates it.
Why the edge. Ingest sources are globally distributed webhooks. Running admission, chain append, and signing in a Durable Object placed near the traffic — rather than a regional database — is what makes a sub-100ms evidentiary write path possible (§8).
3. The evidentiary core
3.1 The event and the chain
Every observed action becomes a LedgerEvent: category (llm_call, tool_call,
sandbox_exec, x402_payment, egress_call, artifact_access, auth_event, gap_marker,
generic), source, timestamps, jurisdiction, integer micro-USD cost, a reference to the full
payload in R2, and the chain fields.
The full payload (prompt text, tool arguments, receipts) is canonicalized under RFC 8785 (JCS) — so hashing is byte-deterministic regardless of key order or whitespace — hashed with SHA-256, gzip-compressed, and written to a private R2 bucket. Only the hash enters the chain:
chain_this_hash = SHA-256( chain_prev_hash ‖ payload_hash ‖ event_id ‖ event_ts ‖ category )
chain_index is strictly monotonic per agent (enforced by a SQLite UNIQUE constraint and the
DO’s concurrency gate). Deleting, reordering, or editing any historical event breaks every
subsequent hash. The payload itself can be lost (R2 is fire-and-forget off the response path; a
failed write is durably recorded and alerted, never silent) without weakening the chain — the
hash commitment was made before the response was sent.
Gaps are signed, not silent. When an event is refused — over quota, agent killed, agent
rate-limited — TruthLayer appends a signed gap_marker carrying the reason and the refused
event’s identity. An auditor can therefore distinguish “nothing happened” from “something was
refused and here is the cryptographic record of the refusal.” There is no code path that drops an
authenticated event without leaving a chained trace.
3.2 The two-level signing hierarchy
Every event, daily seal, and anomaly record carries an Ed25519 signature. Key custody follows a two-level hierarchy modeled on DNSSEC’s KSK/ZSK split:
- The tenant root key is generated inside the tenant’s own
TenantDO, wrapped at rest under a platform KEK (AES-GCM), and never crosses an RPC boundary. It can be rotated at any time; every generation ever used remains published (marked retired) so old signatures stay verifiable forever within the retention window. - Per-agent signing subkeys are generated inside each
AgentDO— the private half never leaves the object in either direction. TheTenantDOcertifies the public half with its active root key, producing a short-lived certificate (48-hour validity, alarm-driven renewal with a 12-hour margin) that is published append-only in the tenant’s public registry. Events are signed locally with the subkey; verification chains through the certificate to the root.
This hierarchy is what makes the hot path fast (§8) and bounds compromise: a leaked subkey can forge signatures for one agent, briefly — never for the tenant, and never beyond the validity window a verifier will accept. Subkey private halves are themselves KEK-wrapped at rest, under a key deliberately independent of the tenant-key KEK.
Every signed artifact records key_id — which exact key generation (root or subkey) produced its
signature — so rotation never orphans history.
3.3 Daily seals and the Bitcoin anchor
A hash chain alone proves internal consistency, not when the chain existed. Each UTC day, per agent, the seal workflow:
- Builds a Merkle tree (RFC 6962-style, with domain-separated leaf/node hashing to close the
second-preimage forgery class) over the day’s
chain_this_hashvalues. - Signs
{tenant_id, agent_id, date, merkle_root, chain_head_hash, chain_head_index, event_count}(JCS-canonicalized) and publishes the seal to a public R2 path. - Submits the Merkle root to multiple OpenTimestamps calendars concurrently (real protocol, per-calendar retry with exponential backoff); the resulting proof commits the root into the Bitcoin blockchain.
The result: for any past day, an independent party can confirm that this exact set of events, in this exact order, existed no later than the Bitcoin block the proof lands in — a timestamp guarantee that does not depend on trusting TruthLayer, Cloudflare, or the tenant. Sealing is idempotent by construction (Ed25519 is deterministic), so re-runs cannot create conflicting seals.
3.4 Independent verification
tools/ledger-verify is a standalone Node CLI with zero Cloudflare dependencies that
verifies an agent-day using only public artifacts: the tenant registry
(public/tenants/{id}.json — all root key generations plus all subkey certificates), the
published seal, and the OTS proof. It recomputes the seal hash, resolves the seal’s key_id
through the certificate chain (verifying the certificate’s own signature against the root
generation it names, and that the seal’s signing time falls inside the subkey’s validity
window), verifies the Ed25519 signature, and confirms the anchor proof exists. Full Bitcoin
block-inclusion verification is delegated to the standard ots verify tool rather than
reimplemented.
The same verifier accepts a self-contained evidence bundle — a single JSON export of every artifact needed for a date range — so an auditor or insurer can archive the evidence and verify it years later, offline, against nothing but mathematics. Exports fail closed: a day without a published seal cannot be packaged as evidence, because an unsealed day has no Merkle root or anchor to verify against.
4. Ingest: authenticated, idempotent, one round trip
Six webhook routes cover the observation surface (/ingest/ai-gateway, monetization-gateway,
sandbox, mcp, egress, generic). Each is authenticated by a per-source HMAC-SHA256
signature over the raw request body, verified in constant time before any parsing. Timestamps
more than five minutes in the future are rejected outright (the chain, not the client clock,
orders the ledger). The egress route consumes Logpush NDJSON batches with all-or-nothing
validation.
A verified event then makes exactly one Durable Object call — AgentDO.ingest() — which
performs, atomically within the single-writer object:
- Idempotency: exact-match dedup on
(source, source_event_id)with 24-hour retention. Replays return200 {duplicate:true}without touching the chain. - Admission: kill-switch state. A killed agent’s events become
agent_killedgap markers; a throttled agent gets a real requests-per-minute budget with atomic slot reservation. - Quota: the agent’s own hourly spend cap. Over-cap events become
quota_exceededgap markers, and deliberately stay retryable (the dedup key is only recorded once quota clears). - Append + sign: chain construction and a local subkey signature (§3.2).
Every response — success or gap — carries the agent’s current agent_status. This is the
kill-switch’s distribution channel (§6): the tenant’s integration is already reading these
responses on every event, so a killed status reaches it on the very next call with no
additional infrastructure and no TruthLayer credential inside the tenant’s estate.
5. Behavioral anomaly detection
Detection runs as a scheduled Cloudflare Workflow (hourly, plus a deeper nightly pass) computing eight features per agent over a 7-day window:
| Feature | Signal |
|---|---|
spend_velocity_z | z-score of the last hour’s spend vs. the trailing week |
tool_category_kl | KL divergence of the 24h event-category mix vs. baseline |
mcp_counterparty_jaccard | set drift of MCP servers contacted |
sandbox_exit_failure_rate | non-zero-exit fraction of sandbox processes |
x402_counterparty_hhi | payment-concentration (Herfindahl) shift |
capability_envelope_violation_count | tool calls outside the agent’s declared allowlist |
prompt_embedding_drift_cos | nightly: cosine drift of prompt centroids (Workers AI, bge-large) |
completion_embedding_drift_cos | nightly: same for completions |
Features feed a weighted logistic score with fixed thresholds — T1 > 0.60 (record), T2 > 0.80 (throttle to 10 rpm + alert), T3 > 0.95 (kill + page). Weights are tenant-overridable as data (never tenant-authored code), with defaults tuned empirically — the highest weights sit on capability-envelope violations and MCP counterparty drift, and minimum-sample guards were added after early tuning drove the false-positive rate to 60% on sparse windows.
Two honesty mechanisms are structural:
- Anomalies are themselves signed ledger artifacts. The detection fields are signed at record time; later human review annotates but never re-signs, so the original machine judgment is permanently distinguishable from the human verdict.
- A precision guardrail freezes automation, not detection. Nightly, the system computes its own precision over the last 200 human-reviewed T2+ anomalies. Below 0.70, automated T2/T3 actions freeze — anomalies still record their true threshold, alerts still fire, but the kill-switch is not invoked until precision recovers. The system never pretends it detected something milder than it did.
6. Enforcement: the kill-switch
TruthLayer is a webhook receiver, not an inline proxy — it deliberately holds no credentials for the tenant’s own infrastructure. Enforcement therefore uses two mechanisms that don’t require any:
- Ingest-boundary enforcement. Throttle/kill state lives in the
AgentDOand gates admission on every subsequent event (§4). Theagent_statusfield in every ingest response is the propagation channel: the tenant’s integration self-enforces (stops its sandbox, halts its agent loop) within one event of the action. Every throttle, kill, and reactivation is itself recorded as a signed, payload-backed ledger event — the enforcement history is part of the evidence. - AI Gateway spend limits (defense in depth). If a tenant supplies a scoped AI Gateway
credential, a kill also applies a real $0 per-agent spend-limit rule at Cloudflare’s AI
Gateway — a hard stop on LLM spend even if the tenant’s integration ignores
agent_status. Reactivation deliberately does not silently undo this (there is no “remove limit” API, only replace), and the operator console surfaces that asymmetry rather than hiding it.
Actions originate from the anomaly workflow (automated, subject to the precision guardrail) or from a human operator via the dashboard, where every action requires a stated reason and is attributed — server-side, from the operator’s verified identity, never client-supplied — in the signed event itself.
7. Access surfaces and trust boundaries
Three read/control surfaces exist, each with a deliberately different trust model:
audit-api(external auditors): per-tenant opaque credentials exchanged for short-lived JWTs signed by the tenant’s ownTenantDO. Every route resolves the tenant exclusively from the verified token — there is no code path where a caller-supplied tenant id is trusted, so the surface cannot be stretched into cross-tenant access. Provides point-in-time ledger reads (“the ledger as of time T” is a plain immutable-log query, not a destructive restore), seal retrieval, anomaly review, and evidence-bundle export, over both REST and Cap’n Web RPC.admin-api+ dashboard (platform operators): a separate, cross-tenant console gated by Cloudflare Access with independent JWT verification at the origin (signature, issuer, and audience — per Cloudflare’s own guidance that the edge gate alone is insufficient). Covers tenant/agent lifecycle, ledger browsing, the anomaly queue, kill-switch actions, billing, and evidence export. Built as a new surface precisely because audit-api’s single-tenant trust boundary was the wrong one to weaken.ledger-verify(everyone else): requires no access at all — the point of the public artifact design is that verification is permissionless.
Secrets discipline is uniform: every private key, API token, and JWT secret at rest is wrapped under a platform KEK; plaintext secrets are returned at most once at creation; nothing sensitive crosses an RPC boundary that doesn’t need to. Nightly encrypted exports of every Durable Object’s full state provide disaster recovery without a plaintext copy ever landing in R2.
8. Performance and hardening: measured, not projected
TruthLayer’s engineering discipline is that results are logged honestly, including misses. The hardening program (chaos suite of ten fault-injection scenarios, per-IP flood controls, encrypted backups, payload sharding) culminated in sustained live load testing of the deployed system with real HMAC-signed traffic against a pool of 500 real tenant/agent pairs.
The original architecture missed its target. The initial ingest path made four sequential cross-Durable-Object round trips per event (tenant admission → agent admission → append → a nested signing call back to the tenant object, since only the tenant held signing keys). Measured ceiling: ~4,700 events/sec sustained, p99 ≈ 1.8s against a 10,000/sec, sub-500ms target. Server CPU time was 1–4ms per request; the gap was pure RPC wait. Storage sharding (payloads spread across 8 R2 buckets by consistent tenant hash) fixed a real background-write reliability ceiling but, as predicted and confirmed, did not move request latency.
The single-hop redesign. The subkey hierarchy (§3.2) exists precisely to fix this: with
signing local to the AgentDO and tenant policy cached (its ingest-relevant fields are immutable
after creation), the entire ingest decision — idempotency, admission, quota, append, signature —
collapses into one Durable Object call with zero mid-request RPCs, while the auditor-facing trust
anchor (the public registry) is unchanged.
Measured results after the redesign (same methodology, 30-minute sustained run from a single generator machine): 6,042 req/s at 99.99% success, with p50 at moderate rates down from ~180ms to 25–59ms — confirming the RPC chain was gone from the request path, but leaving open whether the residual gap to 10k/s was the platform or the single-machine test rig (one source IP, one TLS origin — not what real 10k/s inflow looks like).
Distributed edge load generation answered that question. A Workers-based generator fleet — Durable Object lanes placed across all nine placement regions, each HMAC-signing and firing real ingest requests from its own colo, with measured egress from 16+ points of presence worldwide — removed the client from the equation:
| 4-hop architecture | Single-hop, single-machine rig | Single-hop, edge fleet | |
|---|---|---|---|
| Sustained throughput | 4,728 req/s | 6,042 req/s | 14,638 req/s |
| Run | 30 min, 10.9M req | 30 min, 10.9M req | 10 min, 9.51M req |
| Success rate | 99.82% | 99.99% | 99.98% |
| p50 / p99 | ~180ms / 1.8s | 202ms / 3.4s | 143ms / 1.8s |
| 10k/s target | not met | not met | met — exceeded by 46% |
The earlier “ceilings” were the client rig, not the platform. Per-stage instrumentation (permanent, sampled structured timing markers deployed before the run, cross-checked against per-invocation CPU accounting) attributes the budget at saturation precisely: the entire in-object ingest decision — idempotency, admission, quota, chain append, Ed25519 signature, SQLite commit — costs ~1–2ms of CPU per event, flat from 4k to 14.6k req/s, with ~1ms more in the stateless worker. Essentially all remaining request latency is geographic round-trip time from worldwide callers to where each agent’s Durable Object happens to live, plus tail queueing. No shared resource curves upward with load: the design behaves as the coordination-free, shard-per-agent system it is, and its throughput limit scales with agent count rather than any central constraint.
The still-unmet half of the target is honest and specific: p99 latency (1.8s at 14.6k/s with worldwide traffic) exceeds the sub-500ms bar, and the instrumentation shows this is a data placement property — Durable Objects migrate toward their callers over time, and this test pool’s objects all lived in one region while its traffic suddenly came from everywhere — not a compute, signing, or serialization cost.
Two semantic changes shipped with the redesign, both documented rather than silent: idempotency dedup narrowed from tenant-wide to per-agent (retried deliveries always land on the same agent object, so replay protection is preserved), and the hourly spend quota moved from a tenant-wide aggregate to each agent’s own declared cap — finer-grained, and enforceable without cross-object coordination.
9. Product tiers and business model
TenantRecord.tier defines three commercial tiers, billed via Stripe:
- Diagnostic — the observability tier: full ingest, chain, detection, and dashboards, with a 30-day hot retention default.
- Evidentiary — adds the full evidentiary posture: daily seals, Bitcoin anchoring, 7-year cold retention (2,555 days), and evidence-bundle export.
- Insurer-attested — adds the attestation artifact an underwriter consumes: a signed JSON
evidence bundle verifiable end-to-end by
ledger-verify(human-readable report templates are deliberately post-v1).
On top of subscriptions, TruthLayer takes 1% of x402 payment volume flowing through observed agents — inherited from the Monetization Gateway’s own accounting, never separately re-counted from Stripe (a spec-level non-negotiable: the ledger, not a second bookkeeping system, is the source of truth; $10,000 of observed monthly x402 volume reconciles to a $100 invoice item, with idempotency delegated to Stripe’s own guarantees). The insurance thesis is the long-term business: an insurer cannot underwrite what cannot be audited, and TruthLayer’s artifact set — signed events, sealed days, anchored roots, signed anomaly and enforcement history, permissionless verification — is designed to be the underwriting substrate for autonomous-agent policies.
10. Threat model summary
| Adversary | Mitigation |
|---|---|
| Agent forges/suppresses its own history | Capture at the network layer; agent holds no keys, needs no SDK, cannot bypass the chokepoint it already transits |
| Tenant edits history after an incident | Hash chain + published daily seals + Bitcoin anchor: revision is evident to any verifier |
| TruthLayer edits history | Same: seals and OTS proofs are public and independently verifiable; TruthLayer cannot rewrite a Bitcoin block |
Compromised AgentDO / leaked subkey | Blast radius = one agent × ≤48h validity window; certificate chain and validity check are enforced by the offline verifier |
| Forged ingest traffic | Per-source HMAC over the raw body, constant-time verification, replay dedup, future-timestamp rejection |
| Silent refusal / selective dropping | Signed gap_markers — refusal itself is chained evidence |
| Detector goes bad (silent degradation) | Self-computed precision guardrail freezes automated enforcement below 0.70 precision while continuing to record honestly |
| Runaway spend | Per-agent hourly caps at admission; AI Gateway $0 spend rules as an independent second layer |
| Flood/abuse of public endpoints | Per-IP flood ceilings ahead of any crypto work; HMAC rejects unauthenticated volume cheaply |
| Operator abuse | Cloudflare Access + origin JWT verification; every mutating action attributed server-side and signed into the ledger |
11. Current status and known limitations
The platform is deployed and live: all eleven workers, both Durable Object classes, the sharded R2 topology, the Access-gated operator console, and the billing pipeline. The build was executed phase-by-phase with per-phase definitions of done, each verified by real tests (unit, integration, property-based, chaos, and browser end-to-end) before the next phase began.
Honest open items, tracked rather than hidden:
- Tail latency under globally distributed load: throughput is proven (14,638 events/sec sustained at 99.98% success — the 10k/s target met with 46% headroom), but p99 latency at that rate (1.8s) exceeds the sub-500ms bar. Instrumentation attributes this to Durable Object data placement relative to suddenly-worldwide callers, not compute; improving it is a placement question, tracked as such.
- Cold tier: the Iceberg table design exists as reference DDL; the hot→cold data-movement pipeline (and therefore cold-tier point-in-time queries) is deferred.
- Payload readback: event payloads are hash-committed and stored, but no surface yet decompresses them for display; metadata/hash/reference is the v1 contract.
- Precision guardrail bootstrap: the guardrail needs 200 human-reviewed anomalies before it can compute precision; until review volume accumulates it is structurally inert.
- Anchoring granularity: daily seals bound ordering within a day by chain, but existence proofs are day-granular; per-event anchoring is a cost/benefit decision deliberately not taken.
- Insurer-facing report templates: the attestation artifact is machine-verifiable JSON; human-readable reports are post-v1.
12. Conclusion
The premise of TruthLayer is that trust in autonomous agents will not come from better self-reporting — it will come from evidence that survives hostile scrutiny. By observing agents at a network layer they cannot avoid, committing every action to a signed hash chain under keys the agent never touches, sealing each day under a Merkle root anchored in Bitcoin, and making verification permissionless and offline, TruthLayer turns “what did this agent do?” from a question about logs into a question about mathematics.
The system is built, deployed, measured, and — where it falls short of its own targets — says so in writing. That discipline is not incidental: a platform whose product is evidence must hold its own engineering record to the same standard.
TruthLayer runs on Cloudflare Workers, Durable Objects (SQLite), R2, Workflows, Workers AI, AI
Gateway, and Cloudflare Access. Verification requires none of these: ledger-verify is a
standalone tool with no Cloudflare dependencies, and all verification artifacts are public.