Skip to content
Everruns Cloud is open in early access. Run agents without operating the platform.

Serve on celld (experimental)

Experimental. Like serve, everruns-serve-celld is a proof of concept with no compatibility promise.

celld runs a Cloudflare Workers application, Durable Objects included, on your own machines. Each Durable Object is a cell: one node owns it at a time, its SQLite database is replicated to other nodes and to an object-storage bucket, and another node takes it over when its owner dies. everruns-serve-celld (imported as serve_celld) uses a cell to keep a serve app durable.

celldserve
A cell, addressed by nameOne serve app instance: name one per conversation or per user
The container the cell supervisesThe serve binary, with its state on the container’s ephemeral disk
The cell’s SQLiteThe latest snapshot of serve’s state, and a journal of requests since
A new container (after a move, a node loss or an idle stop)Restored from the snapshot, then the journal is replayed
The cell’s alarmTakes the next snapshot once the app is idle, and recovers after a node restart

The agent never notices: the snapshot holds serve’s session catalog, the engine’s event log and the workspace, so a restored container resumes every session.

Replace serve::start with serve_celld::start in main:

#[tokio::main]
async fn main() -> serve::Result {
serve_celld::start(serve::App::builder().discover().build()).await
}

With no command the binary listens on port 8080 in start mode and adds three routes for the cell: GET /celld/state (boot id and busy), GET /celld/snapshot and PUT /celld/restore. serve boots on the first other request, so a restore can happen first. Keep state at one path for every container of an image (the default, /tmp/serve-celld, or SERVE_DATA_DIR), because the engine records the workspace root.

[sandbox] kind = "microvm" gives the agent a shell and file tools in a workspace inside the data directory, so snapshots carry it. The container is the boundary; for code you did not write, give the container class a runtime with its own kernel (runtime: "runsc" or "kata" in wrangler.jsonc).

The cell is a small JavaScript Durable Object that extends Container from @cloudflare/containers. Copy worker/ from the celld example. Requests reach a cell at /cells/<name>/v1/..., and the cell:

  1. asks the container for its boot id, and restores and replays when the container is new;
  2. journals a mutating request (a message, an approval, an answer, a cancel, an AG-UI run) before it forwards it;
  3. snapshots once the app is idle and drops the journal the snapshot covers.

POST /v1/sessions is not journaled, since serve picks the session id. It is answered only once a snapshot holds the new session. When another turn in the same cell keeps the app busy for 30 seconds, the answer carries x-celld-durable: pending and the snapshot follows when the turn ends.

  • A request the cell answered survives the loss of the container or of the node. Durability is per turn: a replayed turn calls the model and runs its tools again, so tools with side effects need their own idempotency.
  • Snapshots are stored whole in the cell and pass through the isolate heap (128 MB by default), which bounds the state one cell can hold.
  • Schedules run inside the container, so they run only while it is up.
  • The routes under /celld/ are the cell’s own; the Worker never forwards them.

The example runs without a container engine too: its external cell reaches a serve process by URL. Its end-to-end test kills the serve process mid-turn and then the celld node itself, and checks that the conversation comes back whole. See the example README.

A second, experimental shape runs no container at all: the engine is compiled to WebAssembly and runs inside the Durable Object, writing every event to the cell’s own SQLite. A turn advances one engine step at a time (one model call, or one batch of tool calls), and each step commits in one transaction, so losing a node costs at most the step that was running. It ships as an example, not a crate, and has one built-in tool. See the engine cell example.