The Durable Object, the sandbox daemon, and their deploy assets leave the rhema workspace for a repository of their own. The DO was already a deployment artifact (publish = false, worker feature wasm-only), and rhema-sandbox-server's only consumer was the image beside it — both are product, not framework. The harness and the seam stay behind: rhema-cloud-agent and the sandbox family join the rhema repository's publish list, and this tree consumes them from crates.io at 0.2.6, the same arrangement the claw keeps its framework crates under. The sandboxd image build carries the fix its extraction needed: the old Dockerfile still staged the claw's workspace (claw and tools manifests, integrations globs, the vendored crossterm) and missed rhema-provider-registry's manifest entirely. The manifest stage is now this workspace's two members, fetched from the registry. Not yet buildable: the rhema 0.2.6 batch (rhema-sandbox, -local, -http, rhema-cloud-agent) must publish first — then one cargo check generates Cargo.lock and CI goes green.
Running the coding agent on Cloudflare
One Durable Object per session: WebSocket chat, streaming model
events, bash in a Cloudflare Sandbox container, checkpoints in the
DO's SQLite storage. A restart — of the DO, the container, or the
worker — resumes the session; a command that died with the container
comes back as an "interrupted" tool result, never silently re-run.
Prerequisites
- A Cloudflare account with Workers Paid: Containers require it, and the bill includes container runtime (see the pricing caveat below).
- Node (for wrangler and worker-build), Rust with
wasm32-unknown-unknown(rustup target add wasm32-unknown-unknown).
Deploy
# from the repository root
npx worker-build --release # builds crates/rhema-cloudflare-code into build/worker/
npx wrangler deploy -c deploy/cloudflare/wrangler.toml
First deploy creates the CodingAgent and SandboxShim Durable
Object classes and the container binding.
Secrets
Two wrangler secrets, never config files, never DO storage:
npx wrangler secret put ANTHROPIC_API_KEY -c deploy/cloudflare/wrangler.toml
npx wrangler secret put SANDBOX_TOKEN -c deploy/cloudflare/wrangler.toml
ANTHROPIC_API_KEY authenticates the model calls. SANDBOX_TOKEN is
any long random string; the agent DO sends it to the sandbox shim on
every call, and the shim refuses everything else. Generate one with
openssl rand -hex 32.
Driving a session
Every session lives at /sessions/<sessionId>/...:
# Set up the session once: model, approval policy, seed, skills.
curl -X POST https://<worker>/sessions/demo/config \
-H 'content-type: application/json' \
-d '{
"model": "claude-sonnet-4-5",
"approval": "ask_client",
"seed_git": "https://github.com/acme/widgets",
"skills": [{ "name": "deploy",
"description": "Triggers on deploy tasks.",
"body": "---\ndescription: Triggers on deploy tasks.\n---\n..." }]
}'
# Chat over a WebSocket: send {"type":"chat","text":"fix the build"},
# receive streaming events; answer approval requests with
# {"type":"approve","id":"...","approve":true}. Stop with {"type":"stop"}.
curl https://<worker>/sessions/demo/session # the checkpointed record
Approval policies: deny_all (default), allow_list with
{"allow_list": ["bash"]}-style tool names, allow_all, and
ask_client — requests go over the socket, an unanswered ask denies
after 120 s.
Limits and pricing
- Subrequests: one model fetch plus one or two sandbox calls per step. A step costs 2–3 subrequests against the plan's per-invocation budget (1000 on paid plans), so a 16-step turn spends well under a tenth of it. If turns ever grow past that, the alarm-driven step queue (one invocation per step) is the v2 answer, not limit-raising.
- CPU: the DO's work is orchestration; the model and the container do the heavy lifting off-thread. Wall-clock, not CPU, is the binding constraint, and workerd allows long-lived WebSocket requests.
- Container pricing: Containers bill for runtime, not
invocations — a container that idles for its inactivity window
still costs minutes. The shim keeps one warm for 15 minutes after
the last command (
INACTIVITY_TIMEOUT_MS); turn that down for cost, up for latency. - Workspace persistence: files survive idle sleeps. A full container eviction loses them; the next seeded session re-clones. Treat the sandbox as a checkout, not a store — push early.
What is where
| File | What it is |
|---|---|
wrangler.toml |
the Worker: DO bindings, SQLite migration, container, vars |
entry.mjs |
the deployable entry: re-exports the classes, routes /sessions/<id>/... |
sandbox-shim.mjs |
the JS DO wrapping ctx.container in the JSON surface the agent DO speaks |
../../crates/rhema-cloudflare-code/ |
the Rust agent: WS protocol, turn loop, checkpoints, fetch transport |