Every lab that has shipped autonomous agents at scale has built the same loop privately: external event → context → model → tools → audit. Stripe’s Minions. Anthropic Managed Agents. Internal Codex pipelines. Different triggers, same shape — and almost always someone else’s servers, someone else’s audit story, someone else’s training data.
ClawQL Streams is that loop as a platform: any event source, significance-gated Agent sessions, the full ClawQL tool surface embedded in the session cell, WORM durability via SQLite → LTX → bucket, and a runtime ladder from Cloudflare Workers through celld to ClawQL’s owned Rust runtime (clawql-cellrt) with optional AMD SEV-SNP + QR air-gap audit.
This pairs with Anything to MCP. MCP to Anything. (the short ClawQL map), Seven Surfaces, One Catalog, What Convergence Week Actually Proved, When Rubrics Become Rewards, the API spend that never compounds, and Correctness by Construction (Erlang / Jane Street / NASA → Effect + Rust + TLA+ for cellrt / TEE).
The pattern everyone rebuilds
| Stripe Minions | Anthropic Managed | ClawQL Streams | |
|---|---|---|---|
| Trigger | Slack reaction | Cron / API | WebSocket · NATS · webhook · cron · poll · gRPC · SSE · (planned) QR |
| Tools | Internal Toolshed | Built-in + custom | clawql-core + Protocol Fabric in-process |
| Audit | Internal | Vendor-managed | LTX → your bucket (RPO=0); optional QR air-gap |
| Sovereignty | None | Anthropic servers | celld / Cloudflare / K8s HPA / cellrt |
| Training data | Vendor or none | Vendor | Operator-owned RTP/OBT on session close |
The logical pattern is not novel. Owning the pattern — with protocol surfaces, virtual keys, and an audit trail you can reconstruct with sqlite3 — is.
Streams: event in, agent out
Streams extends the existing schedule idea from time triggers to event triggers. A subscription binds a source, a significance filter, a prompt, an ATR tool allow-list, budgets, and (optionally) RTP consent for training export. When an event clears the filter, an Agent session cell runs. No human starts the chat.
Three delivery modes (same event, three consumers)
| Mode | What happens | Best for |
|---|---|---|
| Reactive | Event → storage.put() → LTX → bucket | Always-on WORM / compliance |
| Ambient | Event → NATS buffer → attached to next MCP tool response | Agent awareness without spawning a session |
| Autonomous | Event → significance filter → Agent session cell | Act without human initiation |
Everything still lands in the durable buffer. Autonomy is opt-in per subscription via the significance filter (threshold, pattern, rate, composite, always, never).
The embedded stack (v0.2)
The important architectural choice in Streams v0.2: do not HTTP-call a separate ClawQL server for every tool. Embed the tool surface in the session cell. The only external HTTP call from an Agent cell should be the model call.
Agent session cell (Durable Object / celld / cellrt)
├─ clawql-streams — lifecycle, significance, RTP flush
├─ clawql-core — search · execute · memory_* · audit · cache (in-process)
├─ mcp-api-adapter — Protocol Fabric surfaces in the same bundle
└─ fetch() only to:
└─ clawql-inference — PAL routing, virtual keys, model HTTP
That kills tool-round-trip latency, removes a live MCP HTTP dependency from the hot path, and makes the cell self-contained. Bundle size stays under celld’s 64MB code limit via esbuild tree-shaking and runtime provider loading (CLAWQL_PROVIDER) — not by shipping every provider spec into the DO.
Virtual keys, not long-lived credentials
Event clears significance filter
→ POST /v1/keys (scope = cell id, budget, TTL)
→ spawn Agent cell with virtualKeyId
→ each model call: X-Virtual-Key
→ each tool call: in-process + ATR allow-list
→ setAlarm(expiresAt) // not setInterval
→ alarm: expire key · capture spend · flush RTP/OBT · deleteAll()
A destroyed cell’s key is already expired. Compromise of one key is one session, not the fleet.
WORM is the SQLite write
In the celld path there is no separate “audit sidecar.” Every storage.put() is acknowledged only after LTX replication to the bucket (RPO=0). The bucket is the forensic record. An auditor reconstructs a cell with sqlite3 and S3 tools — no live system access required.
Session close wraps accumulated RTP turns in an OpenBenchTrace outer envelope (arm, spend caps, clawql_version, Merkle root) and exports to R2 / Postgres / Hugging Face per subscription consent. That is the same OBT→RTP composition Convergence Week proved in CI — now on production event traffic. See What Convergence Week Actually Proved.
Runtime ladder: Cloudflare → celld → cellrt
Same Durable Object shape. Three deployment realities.
Cloudflare Workers (hosted)
Same DO classes, wrangler deploy. Fine for low resident-cell counts. Economics flip quickly once you keep many subscriptions resident (outbound WebSockets keep cells warm).
celld (self-hosted Durable Objects)
celld (Deno / Ryan Dahl, Apache 2.0) is a ~58MB binary: V8 + SQLite + LTX + Tokio, speaking the Cloudflare Workers/DO JavaScript API, with an S3-compatible bucket as coordinator. No membership protocol. No control plane. One atomic bucket write grants cell ownership. Peer discovery is “point another node at the same bucket.”
Why use it instead of inventing DO on Node worker_threads: millions of simulated fault schedules, differential conformance against workerd, live fleet fault injection. Rebuilding that coverage is not a weekend project.
Cost shape (order of magnitude from the Streams spec): ~1k resident cells on Cloudflare is thousands of dollars/month; the same order of work on a small celld fleet is tens of dollars. That asymmetry is why self-hosted DO matters for Streams.
Constraints that rewrite agent code (loud failures, no silent gaps):
| Do not use in DO code | Use instead |
|---|---|
setInterval | ctx.storage.setAlarm() — durable across restarts |
node:child_process / claude -p | fetch() to clawql-inference |
node:fs, KV, R2 bindings, Cache API | DO SQLite (+ LTX to bucket) |
Raw TCP / cloudflare:sockets | fetch() / WebSocket |
deriveKey / DigestStream | digest + randomUUID |
Outbound WebSockets do not survive cell migration. Store connection intent + cursor in SQLite; reconnect from alarm(). Agent DO names like agent:{subscriptionId}:{eventId} give replay idempotency — NATS redelivery hits the same cell name instead of spawning a duplicate session.
Alpha honesty: celld is alpha, not hostile-multi-tenant-safe, peer protocol is HMAC over plain HTTP (private network / WireGuard required), security fixes track latest release. Regulated customers who need patch SLAs today stay on Kubernetes HPA against NATS consumer lag until celld is production-stable. Streams’ Helm scalingBackend is explicitly kubernetes | celld | cloudflare.
clawql-cellrt (owned production runtime)
celld is the right bootstrap. It is not ClawQL’s long-term security story.
clawql-cellrt is a Rust binary purpose-built for ClawQL cells: same bucket/LTX/lease coordination pattern, but with embedded PAL + virtual keys, HashiCorp Vault dynamic secrets, eBPF monitoring (Linux), Cosign self-attestation on boot, OTel/Prometheus/Langfuse, and — the structural difference — Wasmtime + WIT capability grants for clawql-core compiled as a WASM component.
clawql-cellrt
├─ Fleet coordinator (S3 leases, LTX, peer HMAC)
├─ Per-cell Tokio task
│ ├─ Wasmtime → clawql-core.wasm (Effect-TS via ComponentizeJS)
│ ├─ Explicit WIT imports only (no undeclared capability exists)
│ ├─ HTTP / WebSocket / durable alarms
│ └─ SQLite + LTX WORM
├─ Security (eBPF · Cosign · capability enforcer · virtual keys)
├─ Inference (PAL · budgets · call store)
├─ Vault (AppRole · lease inject into WASM env)
└─ Observability (OTel · Prometheus · Langfuse RTP forwarder)
The teaching artifact is the capability world, not a longer feature list. Tool code cannot “be careful” about filesystem or raw sockets — those imports are simply not linked:
// clawql-core.wit (logical)
world clawql-core {
import wasi:http/outgoing-handler@0.2.0 // fetch only — no raw TCP
import clawql:storage/cell-storage // cell SQLite, not node:fs
import clawql:crypto/digest
import clawql:crypto/hmac
import clawql:crypto/random
import clawql:crypto/aes-gcm
import clawql:inference/call // virtual-key + budget enforced
import clawql:audit/worm-append // WORM only via host
export clawql:tools/call-tool
export clawql:tools/list-tools
export clawql:mcp/handle-request
export clawql:mcp/handle-websocket
}
Bootstrap path ships first: cells call local clawql-mcp over HTTP while @clawql/wasm-polyfills and @clawql/effect-wasm land. Mode auto-detects whether clawql-core.wasm is present. Clients do not care which path executed the tool.
Virtual keys in Rust make lifecycle structural: one-way state transitions (Active → exhausted / expired / revoked), Drop on the cell handle expires the key even on panic. Same product rule as the TypeScript DO path — harder to violate by accident.
TEE + QR: remove the network from the trust chain
Even with SEV-SNP attestation and LTX WORM, a paranoid model still has a hole:
Hardware → binary measurement → WORM entries → network → verifier
The first three links can be cryptographic. The fourth is an operator, a cloud, or a transit path that can filter or delay events.
clawql-tee air-gap audit transport replaces that hop with a physical unidirectional channel: QR frames rendered inside the TEE boundary (HDMI, serial, e-ink, or thermal printer), scanned by a camera outside it. No return channel. The screen cannot be instructed by the network stack the auditor distrusts.
What multiplexes in the sequence:
- WORM audit chunks (CBOR + zstd, Merkle-chained, HMAC’d)
- SEV-SNP attestation report + VCEK material (verifier hits AMD KDS — the TEE never needs to)
- Session binding (cell id, virtual key, event hash, RTP/OBT hash)
Frame discipline matters: sequential indexes, payload hashes, prev_frame_hash, running Merkle root, footer with final root + Ed25519 session signature bound to the Cosign-verified cellrt identity. Miss a frame → gap error → rescan. Replay another session’s frames → HMAC/session binding fails.
Normal sessions (~200 audit entries) scan in seconds at ~500ms/frame. Heavy exports print to thermal and scan offline — a physical artifact a regulator can leave with.
This is the same optical frame family as Protocol Fabric’s QR surface (Seven Surfaces, One Catalog): Streams/TEE for audit egress; Core CLAWQL_QR_SOURCES and adapter --qr-* for tool/catalog I/O. One encoding story, two jobs.
The zero-trust chain (complete)
1. Cosign + SEV-SNP — binary + hardware
2. WIT WASM sandbox — capabilities structurally absent
3. Attestation-gated Vault — secrets after measurement verifies
4. LTX WORM (RPO=0) — record before ack
5. QR air-gap transport — verifier path the operator cannot edit
6. GPU CC (when present) — weights/inputs off the host
That is what “zero trust” means when it is not a slide: verification makes trust unnecessary.
How the pieces compose
Event source
→ Streams gateway (significance)
→ Agent cell on Cloudflare | celld | cellrt
├─ tools in-process (core + adapter)
├─ model via virtual key → clawql-inference
├─ WORM via SQLite → LTX → bucket
└─ RTP/OBT flush → training / Langfuse
→ (regulated) QR audit sequence → clawql-tee-verifier
→ flywheel: scored traces → adapters → tier-map.json
OpenBench Convergence Week proved the eval side of OBT/RTP on a frugal model. Streams is how the same envelope emits from production events. Rubrics / GRPO are how those traces become a cheaper Frugal route. Ontology CQE is what structured memory_recall inside those sessions can decide exactly.
What to run when
| Need | Path |
|---|---|
| Hosted, low cell count | Cloudflare Workers + Streams DO code |
| Self-hosted DO economics, accept alpha | celld fleet + private peer net |
| Patch SLAs / air-gapped K8s today | Streams scalingBackend: kubernetes + NATS HPA |
| Owned security + WASM capabilities | clawql-cellrt (HTTP bootstrap → WASM) |
| Regulator must not trust your network | cellrt TEE + QR air-gap export |
What is still draft
Streams v0.2, celld integration, cellrt, and tee-airgap are spec’d — shipping in stages. Bundle-size CI, celld production timeline, Effect→WASM scheduler shims, and display-path TCB limits (compromised display driver) are called out in the specs as open risks, not hand-waved.
The product claim that does not wait on any of that: the session pattern is clear. Event-triggered agents with in-process tools, scoped virtual keys, bucket-backed WORM, and an optional physical audit channel are how you stop rebuilding Minions in every org.
Further reading
- Streams / celld / cellrt / TEE specs (draft) — docs.clawql.com/streams · QR stream · TEE air-gap audit
- celld · denoland/celld
- Seven Surfaces, One Catalog — Protocol Fabric + QR
- What Convergence Week Actually Proved — OBT wraps RTP
- When Rubrics Become Rewards — traces → adapters
- Memory Finds. Ontology Decides. — CQE inside those sessions
- The API Spend That Never Compounds · Twelve Layers of LLM Cost
- Correctness by Construction — Erlang, OxCaml, Ada/SPARK → Effect + Rust + TLA+
- Why We Migrated to Effect-TS — typed errors and Layer DI on the TypeScript side
- The Model Believed in Itself — encouragement vs adversarial pressure on long-horizon agents
