Security and isolation
CellB is a library: the embedder decides what code it runs and where. This page says what CellB isolates, what it leaves shared, and what each setting exposes, so that the boundary is a choice and not an assumption. Terms are those of CONTEXT.md.
The project’s own Worker is trusted code
The project’s Worker and its Durable Objects run in the host’s realm, not in an isolate:
- In Node they share the host’s process:
process,process.env, the module cache, and every global. Code that writes toglobalThisaffects the whole runtime. - In a browser they share the worker that hosts the project, and so the origin: its OPFS, IndexedDB, cookies through the router, and the network.
Run as a project only code you would run in the host itself.
A loaded Worker is isolated from the project, not from the platform
Code handed over at run time through a Worker Loader binding (Dynamic Workers) runs in a realm of its own:
| Host | Realm of a loaded Worker |
|---|---|
| Node | a worker thread, always |
Browser, with isolateScript | a Dedicated Worker of its own |
Browser, without isolateScript | none: the project’s realm |
In its own realm a loaded Worker does not see the project’s globals or env. Its env holds what
its WorkerCode declares: plain values are copied, and bindings become stubs whose every call goes
back to the parent runtime. A loaded Worker therefore reaches the project’s storage, other objects
and services only through what it was given.
What that realm does not take away:
- The platform’s own APIs. A Dedicated Worker runs on the page’s origin, with the origin’s storage APIs and network; a worker thread runs in the host’s Node process.
- The network, beyond
fetch.globalOutbound: nullreplaces the globalfetch()with one that throws; a Fetcher asglobalOutboundreceives its fetches. Neither removes the platform’s other network APIs, such as theWebSocketglobal. - CPU, memory and storage. CellB enforces no limits per Worker or per object. Calls into a loaded Worker have no timeout, and one that never returns holds its caller.
Running untrusted code needs more than CellB gives: an opaque-origin iframe or a separate origin in a browser (out of CellB’s scope, gaps.md), a separate process or container on a server.
What an origin’s pages share
- Cookies passed through
installProjectRouterare readable by the page and are allPath=/. Browsers dropCookieandSet-Cookiefrom the requests and responses that script builds, so the router stores the Worker’s cookies with the Cookie Store API; cookies written by script cannot beHttpOnly(browser-host.md). - Cells in OPFS belong to the origin. Any page or worker of the origin can open the same storage.
What a fleet bucket’s credentials grant
- A page’s S3 credentials are visible to its user. A browser node signs each request itself, so its key is in the page, with a same-origin proxy too. Give it a key limited to the bucket and prefix (browser-host.md).
- Bucket access is fleet access. The fleet’s peer secret is an object in the bucket
(
fleet/peer-auth.json); CellB reads it from there to authenticate celld’s peer requests. A key that can read the fleet’s prefix can read that secret. - Peer requests are authenticated (Node):
startCelldPeerServerchecks each one with the fleet’s HMAC-SHA-256 secret, within ±30 s, against a nonce replay cache, for this node (celld-compatibility.md).
What breaks
- A public endpoint mounted on
cellsHttpHandlerlets clients address any object directly withx-cellb*headers. Mountruntime.fetch(orCellRouter.ingress, which strips those headers) on public ingress, andcellsHttpHandleronly on internal links. - A loaded Worker on a browser site without
isolateScriptis not isolated at all: it shares the project’s realm and module registry. PassisolateScript(the package ships@statewalker/cellb/isolate-worker) whenever loaded code is not your own.