CellB

How it works

CellB has one runtime core that does not know where it runs, plus an adapter for each platform. The same core runs a cell in a tab, in a Node process, or as one node of a celld fleet.

One core, ports for what differs

The core reaches the platform only through a small set of ports. Each platform supplies its own implementation:

PortWhat it isImplementations
OpenCellSqlA synchronous SQLite database per cellnode:sqlite, SQLite WASM on OPFS, SQLite WASM in memory
CellStorageDurability, backup, ownership and R2 objectscelldBucket, filesApiLtxLog, filesApiChangeLog
LeasePer-cell ownership with an epoch and a "lost" signalcelldBucket(…).ownership
ObjectStoreA bucket with conditional writesS3, celld dev's SQLite store, memory, OPFS

The SQL port is synchronous on purpose. ctx.storage.sql.exec() returns rows at once, as on Cloudflare, and both drivers are synchronous underneath.

The path of a request

Request ─► runtime.fetch ─► Worker's fetch(req, env, ctx)
                                 │ env.COUNTER.getByName("a")
                                 ▼
                          stub ─┬─ local: the object's host ─► instance
                                ├─ another shard (HTTP over a MessagePort)
                                └─ the owner node (cells-http or celld's tunnel)

Inside a cell

  • Implicit transaction. The first SQL statement opens a transaction, and it commits when the current turn of the event loop ends.
  • Input gate. Events are queued, so a read-modify-write across storage awaits is safe.
  • Output gate. A reply leaves only after the commit it depends on is durable.
  • Reset. If a commit fails or the lease is lost, the object breaks, and the next call builds a fresh instance.

Ownership depends on the deployment

DeploymentUnit ownedMechanism
One processnothing to sharenone needed
A browser origin, shardeda shardone Web Lock per shard, held by one shard worker
A fleet bucket (celld, CellB, browsers)a cellcelld's own.json per cell, a node lease per node, epochs and self-fencing

Durability

With an LTX log (celldBucket or filesApiLtxLog) every acknowledged write is restorable: the RPO is 0. The first commit of a generation is a snapshot, and later commits carry only the changed pages. In a browser, SQLite WASM reports those pages through a hook on its database file. A periodic backup alone is cheaper, but it can lose up to one interval of writes.

Cells on LTX files in a browser

With bucket: { ltx, node }, a browser shard keeps each cell exactly as celld keeps it: a snapshot and one LTX file per commit, in an OPFS directory. SQLite never holds the database. It reads pages through a VFS from a store worker, with an LRU page cache and read-ahead, so a cell's memory is its page cache, whatever its size. Each commit becomes one LTX file, written under a temporary name and moved into place. The store compacts a cell into a new snapshot when its files grow, and holds a Web Lock per cell, so only one tab or worker writes a cell at a time.

Loaded Workers get a realm of their own

A Worker Loader binding runs each loaded Worker in its own isolate: a worker thread in Node, a Dedicated Worker in a browser. Plain values in its env are copied, and bindings become stubs back to the parent. This is how a host runs plugin or agent-written code apart from its own globals.

Read more