meir dick

Meir Dick · November 2026

Cloudflare as an agent runtime

I set out to prove that Cloudflare can run real agents on the user's own account. The agent's machine sleeps when idle, costs nothing while asleep, wakes in seconds, keeps its state, and never holds a credential.

It works. I built it three times, for Amp, OpenClaw and OpenCode, and shipped a Laravel package for Workers AI. The containers were the easy part. The hard part was the control plane around them, and that's what I think Cloudflare should own.

Below: what I built, what it measured, what I learned, and an RFC for Cloudflare's engineers.

≈12 snew Amp thread ready from a project snapshot, against 36–45 s on E2B and Vercel
2.5 sOpenCode workspace restored from a snapshot, same session
0 / 200messages lost across 200 OpenClaw sleep/wake cycles
$7–14a month for an always-reachable OpenClaw assistant

What I built

All of it runs on Workers, Durable Objects and Containers, in the user's own Cloudflare account. Each install is one command.

cf-orbs

live

Amp's coding agent on your own Cloudflare account

A self-hosted Amp orb provider. Each Amp thread gets its own container, which snapshots to zero when Amp pauses it and restores on the next message. Install takes 4 min 41 s from an empty folder.

New thread ready≈12 sE2B and Vercel: 36–45 s
First tool call, warm0.3–0.5 sAmp-hosted: 0.85 s
npm install7.7 sAmp-hosted: 15.2 s

Gaps I bridged

  • A snapshot index in a Durable Object, because there's no API to list snapshots.
  • A 60 s alarm that keeps busy orbs alive, plus a watchdog for maximum awake hours.
  • A readiness race that falls back to the image when a snapshot has expired.

ContainersDurable ObjectsSnapshotsAmp orb contract

oc-workers

live

OpenClaw that sleeps when idle and never loses a message

A persistent OpenClaw assistant. It sleeps when idle and wakes on a message, a timer or the UI. It replaces moltworker, which I ran from the start and never got reliable.

Sleep/wake cycles2000 updates lost, 0 repeated
Random kills50SQLite loss p95 1.32 s
Cost, light use$7–14/mosleeps between messages

Gaps I bridged

  • SQLite streamed to R2 every second (Litestream) through a gate, so the container holds no R2 key.
  • Webhooks acknowledged at the edge while asleep, then replayed once the container is up.
  • Epochs and generations so two containers never write the same state, and a cron that catches lost alarms.

ContainersDurable ObjectsR2Sandbox SDKWorkers AI

cf-opencode

in progress

Remote OpenCode sessions that keep going when the laptop closes

The local OpenCode client attaches to a workspace in a container on your account. Close the laptop and the turn finishes on the server, then the workspace snapshots to zero. Connect again and the same session is there.

Boot8.5 sfrom the image
Snapshot to zero5.5 sthen no compute bill
Restore2.5 ssame session after resume

Gaps I bridged

  • A proxy fix for getTcpPort: an http target and no public Host header.
  • Model keys swapped in at egress, so they never reach the disk or a snapshot.
  • A browser session opened by the Worker; only its WebSocket URL enters the container.

ContainersDurable ObjectsEgress interceptionBrowser Run

laravel-cf-workersai

live

Workers AI and AI Gateway for the Laravel AI SDK

A Workers AI provider for Laravel's AI SDK: text, embeddings, reranking, images, speech, transcription, Clef decisions and Clairic extraction, called directly or through AI Gateway. Its sibling, prism-workers-ai, does the same for Prism.

laravel/ai versions5tested from v0.9 to v1.3
prism-workers-aiPackagistfeatured on Laravel News

Gaps I bridged

  • Direct and AI Gateway routes behind one config, including gateway authentication.
  • A 4096-token default, because Cloudflare's own default of 256 cuts answers short.
  • Clef answers mapped onto Laravel's classification questions, with probabilities.

Workers AIAI GatewayClefClairic

What I learned

  1. Cloudflare works as an agent runtime. A container, a Durable Object as its controller and disk snapshots give you agent machines that scale to zero, in the user's own account.
  2. The control plane is the product. Lifecycle, an idempotent op journal, a snapshot index, keep-alive, fencing and recovery were most of the work every time.
  3. Snapshots save the disk, not memory. So every harness has to survive a restart. Design for resume, not for staying up.
  4. Keeping secrets out of the box is Cloudflare's strongest card. Egress interception and Worker gates beat putting keys in the container's environment.
  5. Without a control plane you pick a failure. Keep the box on and pay 24/7, or let it sleep and lose state. Moltworker shows both.

The model: cell, fleet, adapter

Anthropic splits an agent into a harness (the loop), a session (the log) and a sandbox (the machine). Something still has to see every session, start and stop sandboxes, snapshot them and bring them back. Cloudflare gives you an excellent controller for one sandbox. The layer that sees the fleet, every team builds again.

  1. Front doors
    Amp orb provider, OpenCode client, chat webhooks, Codex remote
    harness vendors
    exists
  2. Harness adapter, one per harness
    boot, ready, attach, quiesce, export state, resume hook, activity signal
    ecosystem, on a standard interface
    partial
  3. Fleet plane
    session registry, reconcile loop, op journal, quotas, rollout, events, list and inspect
    Cloudflare library
    missing
  4. Cell: one Durable Object plus one container
    lease and fencing, state machine, egress policy, snapshots
    Cloudflare
    exists
  5. Platform mechanisms
    list objects and snapshots, managed snapshot lifecycle, eviction notice, reliable timers, inbound TCP and SSH, stable credential injection
    Cloudflare platform
    missing

Dashed layers are missing. The adapter layer is partial: Cloudflare's experimental ContainerHarness runs Claude Code and Codex headless, with no interactive attach and no fleet view.

RFC

Fleet primitives for agent sessions on Cloudflare

AuthorMeir Dick
StatusDraft for discussion
DateNovember 10, 2026
AudienceContainers, Durable Objects and Agents SDK engineers

Summary

A Durable Object plus a container is an excellent cell for one agent session. To run a fleet of sessions behind a real harness (Amp, OpenCode, OpenClaw, Codex, Claude Code), every team rebuilds the same layer: a registry, a reconcile loop, an op journal, a snapshot index, keep-alive, and recovery.

I built that layer three times on Cloudflare, and it was most of the work. This RFC proposes:

  1. A short list of platform mechanisms only Cloudflare can provide.
  2. An optional Fleet capability in the Agents SDK.
  3. A standard harness adapter interface, extending ContainerHarness.

It doesn't ask for a generic lifecycle or for Cloudflare to own sleep policy. That matches your stance of helpers, not base classes.

What already exists, and works well

  • ctx.container with durable_object scheduling: choosing the image and size at runtime, fast starts, and the Durable Object as controller.
  • Filesystem snapshots (beta).
  • Interception of outbound requests: the basis for keeping credentials out of the container.
  • The Agents SDK Lifecycle: a durable job queue for one Durable Object, with ownership of its alarm.
  • ContainerHarness (experimental): Claude Code and Codex, sessions, operation IDs, event replay, snapshot on idle, ContainerEgress.

ContainerHarness is very close to the cell-plus-adapter layer. This RFC is about what sits around it.

Problem

A Durable Object controls one container. Nothing controls the fleet, and some of what a fleet needs only the platform can provide. Here is what each of my projects had to build:

GapMy workaroundWhere
No API to list or delete snapshotsA Registry Durable Object that indexes every snapshot handle and project mappingcf-orbs
No list of sessions or containersThe same registry, written to on every state changecf-orbs, oc-workers
Alarms are best-effort (a failed setAlarm, a reset Durable Object)A cron ensure() every 5 minutes that re-arms alarms and restarts dead sessionsoc-workers
Spawn and resume outlast a request; a Durable Object can reset mid-waitAn op journal per session: idempotent, with resumable phasescf-orbs, oc-workers
Work inside the container isn't counted as activity; the inactivity timeout maxes out at 6 hA 60 s alarm that refreshes the timeout, plus a watchdog for maximum awake hourscf-orbs
No warning before evictionA crash-only design: Litestream WAL streaming to R2, credential write-through, and a resume pathoc-workers
A missing or expired snapshot only fails later, through monitor()Racing the readiness probe against the failure, then falling back to the imagecf-orbs
start() returns before the container is ready; exec doesn't inherit the environment, has no timeout, and cancelling it doesn't kill the processA wrapper (ContainerPort) and a bootstrap scriptcf-orbs
getTcpPort().fetch refuses https and passes the public Host header throughRewriting the target to http://container and dropping Hostcf-opencode
No inbound TCP or SSH into containersA shell and SSH over a Worker WebSocket (in progress)cf-opencode
Credential injection is example code; the CA needs root at runtime; the limit is 128 interceptsRebuilt egress handlers and Worker gatescf-opencode, oc-workers
No documented API to enable containers on a Durable Object namespaceFound the upload metadata by reading wrangler's sourcecf-opencode

Goals and non-goals

Goals: run N long-lived sessions of a third-party harness, with sleep and wake, resume, visibility and recovery, and no custom infrastructure code. Keep policy (idle timers, image rollout, quotas) in user code.

Non-goals: bigger instances or region pinning, which are already well-known requests; owning the meaning of a harness (what a turn is, what's safe to replay); snapshotting memory.

Proposal

Tier 1: platform mechanisms only Cloudflare can provide

  1. Enumeration. List the Durable Object instances in a namespace, with container state. List and delete snapshots, from ctx.container.snapshots and the account API.
  2. Managed snapshot lifecycle. Refresh a snapshot without restoring it (today a snapshot expires 30 days after its last restore). Fail fast when start() is given a missing snapshot. Publish snapshot pricing.
  3. Eviction notice. A hook such as onContainerStopping({ reason, deadline }), with a grace period long enough to quiesce and snapshot.
  4. Timers that are always delivered. Guaranteed alarm delivery, or documented semantics plus a periodic tick per object that the platform runs, so a reconcile loop doesn't need cron.
  5. Liveness and readiness. Activity inside the container counts toward keep-alive. start() can wait for a readiness probe. exec supports env, timeout, and killing the process on cancel.
  6. Ingress. TCP and WebSocket passthrough to a container port, with Host preserved and https supported, and an SSH pattern for terminal harnesses.
  7. Stable credential injection. Promote ContainerEgress to a declarative platform feature (for each host, which header gets which secret). Trust the CA automatically, raise the 128-rule limit, and let bindings (browser, AI, R2) reach the container through the same path.

Tier 2: Fleet, an optional Agents SDK capability

The pattern is the Kubernetes Agent Sandbox controller, a reconcile loop that keeps each session at its desired state, built on Durable Objects:

interface Fleet {
  create(spec: SessionSpec): Promise<SessionRef> // idempotent by id
  get(id: string): Promise<SessionStatus>
  list(q?: { state?: State; label?: string; cursor?: string }): Promise<Page<SessionStatus>>
  setDesired(id: string, d: { state: "awake" | "asleep" | "deleted"; image?: string }): Promise<OpId>
  snapshot(id: string): Promise<OpId>
  fork(id: string, from?: SnapshotId): Promise<SessionRef>
  events(q?: { since?: Cursor }): AsyncIterable<FleetEvent>
}

interface SessionSpec {
  id: string; adapter: string; image: string; instance: string
  idle: { sleepAfter: Duration; maxAwake?: Duration }
  egress: EgressPolicy; labels?: Record<string, string>
}
  • Reconcile loop: each session stores its desired and observed state, and a level-triggered loop drives one toward the other. Alarms only speed it up.
  • Registry: sharded by tenant or project, written to by each cell on every transition.
  • Op journal: every operation has an ID, is idempotent, and has phases that resume after a reset.
  • Fencing: an epoch or lease per session, so two containers never write the same state.
  • Quotas, rollout (canary or pinned image per session), and events for visibility.

Tier 3: harness adapter interface

Extend containerAgent() and agents/harness/container/runtime to cover interactive harnesses, not only headless prompt submission:

interface HarnessAdapter {
  image: ImageRef
  boot(ctx): Promise<void>                    // entrypoint and environment
  ready(ctx): Promise<boolean>                // readiness probe
  attach?(ctx): AttachInfo                    // URL and protocol for a native client
  quiesce(ctx): Promise<void>                 // flush state before a snapshot
  state: { log?: LogExport; paths: string[] } // what has to survive, and where it goes
  resume(ctx): Promise<void>                  // runs after a restore
  activity(ctx): Promise<boolean>             // whether it's busy
  egressHosts: string[]                       // where credentials get injected
}

The adapters belong to the harness vendors and the community. Cloudflare would publish the interface and a few reference adapters, as it already does for Claude Code and Codex.

Alternatives considered

  • Keep everything in user code (today). It works, as these builds show, but every team reinvents it and many get it wrong. Moltworker is the public example: no supervision, backups that expire, and billing 24/7.
  • Harness in the Durable Object only (Pi, Codex compiled to WASM). Good for new agents, but most adoption today is existing CLIs and servers that live inside a container.
  • A hosted sandbox vendor (E2B, Modal). They ship fleet lifecycle, but give up running in the user's own account, scale to zero next to a Durable Object, and credential injection at the edge, which are Cloudflare's advantages.

Open questions for Cloudflare

  1. Is enumerating Durable Objects and snapshots on the roadmap, or ruled out by design?
  2. What are the intended delivery semantics for alarms on objects that control a container?
  3. Is an eviction notice possible with the current scheduler?
  4. Would you accept Fleet and the adapter interface as a contribution to cloudflare/agents, or should they live outside it?
  5. Will ContainerHarness grow interactive attach, or stay headless?

Ask

A 30-minute call with the Containers and Agents SDK owners. I can walk through the code and the measurement harness, and I'm happy to build the first version of Tier 2 and Tier 3 upstream.