# celld: self-hosted, distributed Durable Objects

> celld runs Workers and Durable Objects on your own machines, with one SQLite database per cell and S3-compatible storage.

Author: [Flavio Copes](https://flaviocopes.com/about/) | Published: 2026-08-07 | Topics: [Cloudflare](https://flaviocopes.com/tags/cloudflare/) | Canonical: https://flaviocopes.com/celld/

Ryan Dahl just launched [celld](https://celld.dev).

It is an open source implementation of [Cloudflare Workers](https://flaviocopes.com/cloudflare-workers/) and [Durable Objects](https://flaviocopes.com/cloudflare-durable-objects/) that runs on your own machines.

You write a Worker. You bind a Durable Object. You use the same JavaScript APIs and a familiar `wrangler.jsonc` file.

But you do not deploy the application to Cloudflare.

You run `celld` on your own VMs. Every Durable Object gets its own SQLite database. The databases are replicated to an S3-compatible bucket you control.

The bucket also coordinates the nodes.

There is no separate database cluster, placement service, membership system, or consensus service to operate.

This is a very interesting architecture.

I use Cloudflare Workers, D1, Durable Objects, Queues, R2, and many other Cloudflare services across my projects. I like the platform.

But celld takes one of its best ideas and turns it into infrastructure we can run anywhere.

The project was [announced on August 5, 2026](https://x.com/rough__sea/status/2085001943693549887). Version 0.1.0 is available under the Apache 2.0 license in the [denoland/celld repository](https://github.com/denoland/celld).

It is also an alpha.

I would not move an important production system to it today. But I would absolutely build an experiment with it.

Let’s see why.

## First, what is a Durable Object?

A normal serverless function is stateless.

It receives a request, does some work, and returns a response. The next request might run on another machine.

This is great until the requests need to coordinate.

Imagine a chat room with 500 connected people. Messages must have one clear order. Connections need shared state. Two users might update the same room at the same time.

You can solve this with a database, locks, a message broker, and a [WebSocket](https://flaviocopes.com/websockets/) service.

My [free Real-time Web Applications course](https://flaviocopes.com/courses/real-time-web-applications/) covers the foundations behind these long-lived connections.

Or you can send every request for that room to one Durable Object.

A **Durable Object** is a small, named server with private persistent storage.

You might create one object per:

- chat room
- user
- document
- game
- project
- device
- AI agent

All requests for one object reach the same logical place.

The object handles one event at a time. It can keep hot state in memory, store data in SQLite, hold WebSocket connections, and schedule alarms.

The important idea is not the class or the API.

The important idea is this:

> Give every independent piece of state one owner.

This removes a lot of coordination from the application.

Cloudflare provides the machines, routing, storage, failover, global placement, and operations behind its Durable Objects product.

celld keeps the programming model. It replaces the managed platform with V8, SQLite, LTX, an S3-compatible bucket, and a small fleet of your own machines.

## What is a cell?

In celld, a **cell** is a Durable Object.

Each cell has:

- a stable name
- one current owner node
- one JavaScript isolate
- one private SQLite database
- one ownership epoch
- a replicated copy of its durable state in object storage

The name is the address.

If I create one cell for an Events Logger project called `flaviocopes.com`, every event for that project goes to the same cell.

Another project, `waitinglists.dev`, gets another cell and another database.

The two projects do not share a table. They do not compete for one database lock. A bug that damages one database does not damage the other one.

The application is sharded from the start.

This is different from the common multi-tenant design where every customer shares one large database and every query includes a `tenant_id`.

With celld, the boundary is physical.

One cell, one SQLite file.

## The complete architecture

The shortest description of celld is:

> V8 + SQLite + LTX + S3 + Tokio

Let’s expand that.

### V8 runs the Worker code

Each celld node embeds [V8](https://flaviocopes.com/v8/), the JavaScript engine used by Chrome, Node.js, Deno, and Cloudflare Workers.

`celld deploy` reads a supported [Wrangler](https://flaviocopes.com/cloudflare-wrangler/) project and uses esbuild to create the Worker bundle.

The same node runs stateless Worker requests and stateful cells.

The runtime implements a useful part of the Cloudflare Workers API, including `fetch`, service bindings, JavaScript RPC, Durable Objects, alarms, static assets, streams, WebSockets, and part of the Node.js compatibility layer.

This does not mean every Cloudflare application runs unchanged. I will come back to that boundary later.

### SQLite stores each cell

Every cell has a separate SQLite database.

If SQLite is new to you, my [free SQLite course](https://flaviocopes.com/courses/sqlite/) covers how the database works and how to use its tools.

This gives the object transactions, indexes, SQL queries, and a familiar file format.

More importantly, it keeps the unit of state small.

A database for one chat room or one project is easier to move than a database for the whole application.

The cell has one writer at a time. celld uses an ownership epoch to fence old writers.

If a node loses ownership, it cannot keep writing into the current state. Its old writes go to an old epoch path that is no longer authoritative.

### LTX replicates SQLite changes

[LTX](https://github.com/superfly/ltx) is the transaction file format used by Litestream.

celld includes a Rust implementation. It turns SQLite changes into segments that can be stored and restored through object storage.

The local SQLite file makes warm reads and execution fast. The LTX copy in the bucket makes the state durable and movable.

celld does not acknowledge a durable write before its data reaches the bucket.

This is what the project means by **RPO=0**: killing a node should not lose a write for which the caller already received success.

I explain RPO, RTO, and recovery planning in my [free Backup and Restore course](https://flaviocopes.com/courses/backup-and-restore/).

An unacknowledged request can still fail. RPO=0 does not mean zero downtime. It means the durability boundary is explicit.

### S3 is the source of truth

All nodes in a fleet point at the same S3-compatible bucket.

The bucket stores:

- deployments
- SQLite and LTX state
- cell ownership records
- node leases
- peer authentication material

The bucket is both storage and coordinator.

Object storage compare-and-swap gives one node ownership of a cell. When another node takes over, the ownership epoch changes.

Notice what happened here.

celld did not make distributed coordination disappear. It placed that responsibility on the conditional-write and consistency guarantees of the object store.

There is no Raft cluster or separate consensus service inside celld. The object storage provider still runs a distributed system underneath.

That is a good trade when S3 is already the most durable part of the infrastructure.

### Tokio runs the node

celld is written in Rust and uses Tokio for asynchronous work.

The node handles HTTP, peer requests, WebSockets, SQLite work, replication, leases, restoration, and lifecycle management.

The first release ships as a small static executable and as a Docker image.

## What happens during a request?

Let’s follow a request to a cell.

```mermaid
flowchart LR
  A["Client request"] --> B["Any celld node"]
  B --> C["Stateless Worker"]
  C --> D["Resolve the cell name"]
  D --> E{"Who owns the cell?"}
  E -->|"This node"| F["Run the cell in V8"]
  E -->|"Another node"| G["Signed peer request"]
  G --> F
  E -->|"Nobody"| H["Claim ownership with compare-and-swap"]
  H --> I["Restore SQLite from LTX"]
  I --> F
  F --> J["Write local SQLite"]
  J --> K["Replicate LTX to the bucket"]
  K --> L["Return the acknowledged response"]
```

Traffic can arrive at any node.

The Worker chooses the cell by name. celld checks the current owner.

If the cell already lives on this node, the request stays local.

If another node owns it, celld forwards the request through its authenticated peer protocol.

If no node owns it, one node claims it with a conditional bucket write, restores its SQLite state, and starts the isolate.

The code runs in V8.

If the request changes durable state, celld writes SQLite locally and replicates the committed change to the bucket before releasing the response.

The result is a useful split:

- warm work is local
- durable writes pay for one object storage round trip
- inactive state lives in cheap object storage
- machines can be replaced

## Why celld does not need a control plane

Most distributed systems start with a list of machines.

The machines need to know who joined, who left, who is healthy, where a shard lives, and who should move it after a failure.

This often creates a control plane.

celld uses the bucket instead.

A node starts with the bucket credentials and an address peers can reach. It writes a lease. Other nodes discover it through the bucket.

There is no join command and no fixed membership file.

Cell ownership is also a record in the bucket.

If a node disappears, its lease expires. Another node can claim the cell, restore the database, and continue.

This makes nodes replaceable.

But the design has a consequence: the bucket is not only a backup.

It is the root of authority for the fleet.

Anyone with those credentials can control deployments, ownership, state, and peer authentication. The [security documentation](https://celld.dev/docs/security) recommends credentials scoped to one fleet bucket.

## Hibernation changes the cost model

A cell does not need to stay in memory forever.

When a cell is idle, celld can hibernate it. Its durable state remains in the bucket while its JavaScript isolate and local working state disappear from memory.

The next request wakes it again.

This works well for applications with many mostly idle entities.

Imagine one cell for every customer project.

Maybe 100,000 projects exist, but only 300 receive traffic right now. The active cells use RAM. The other 99,700 live as objects in the bucket.

This is the same economic idea that makes Durable Objects attractive.

You pay for the active working set, not the complete population.

celld publishes a few early measurements on its homepage:

- about 4 MB of RAM per resident trivial cell
- about 1,000 resident cells on one 8 GB node
- around 4 ms to wake a hibernated cell on the local benchmark machine
- around 90 ms for a durable region-local write
- around 20 seconds for failover after killing a node

Those numbers come from different test conditions. The site says its speed and density measurements used an Apple M-series laptop, while durability used a regional VM fleet and a nearby bucket.

The [testing documentation](https://celld.dev/docs/testing) gives more useful context.

One test ran ten nodes with 4 vCPUs and 8 GB each. The fleet held 10,000 resident cells and 20,000 WebSocket connections. After two nodes stopped, all cell data was available again after about 11 seconds at the tail, with spare capacity in the remaining nodes.

This is promising.

It is not a promise that my application will produce the same numbers.

## The cost claim needs context

The celld homepage compares its self-hosted cost with long-lived Cloudflare Durable Objects.

Its model uses $48 per month for an 8 GB VM holding 1,000 resident cells. It estimates roughly $49 per month for 1,000 resident cells, compared with about $4,150 for continuously active 128 MB Durable Objects on the Workers Paid plan.

That is where the “orders of magnitude cheaper” claim comes from.

But the comparison describes a specific workload: many objects resident all month.

It does not mean celld is always cheaper.

For a small application, Cloudflare can cost less. The free plan can cover tiny workloads, and a managed platform removes a lot of work.

A self-hosted bill also includes more than VMs:

- load balancers and public ingress
- object storage requests and data transfer
- spare capacity for node failures
- monitoring and logs
- backups and recovery tests
- security updates
- operator time

The celld cost chart says application traffic and writes add more compute and bucket usage. It also adds capacity in complete nodes, so the first cell still needs a machine.

I would not choose celld because of one chart.

I would choose it when the architecture fits and I want control of the failure domain, storage, placement, and cost curve.

## A tiny celld application

The programming model is familiar if you used Durable Objects.

This Worker sends every request to a counter named `room-42`:

```js
export class Counter {
  constructor(state) {
    this.state = state
  }

  async fetch() {
    let count = (await this.state.storage.get('count')) ?? 0
    count++
    await this.state.storage.put('count', count)

    return new Response(JSON.stringify({ count }))
  }
}

export default {
  async fetch(request, env) {
    const id = env.COUNTER.idFromName('room-42')
    return env.COUNTER.get(id).fetch(request)
  },
}
```

The binding and migration live in `wrangler.jsonc`:

```json
{
  "name": "counter",
  "main": "index.js",
  "compatibility_date": "2026-08-05",
  "durable_objects": {
    "bindings": [
      {
        "name": "COUNTER",
        "class_name": "Counter"
      }
    ]
  },
  "migrations": [
    {
      "tag": "v1",
      "new_sqlite_classes": ["Counter"]
    }
  ]
}
```

This is normal Durable Objects code.

You deploy it to a bucket instead of a Cloudflare account:

```bash
celld deploy . \
  --bucket s3://flavio-celld-lab
```

Then start one node against the same bucket:

```bash
celld \
  --bucket s3://flavio-celld-lab \
  --listen 0.0.0.0:8080 \
  --advertise cell-a.internal:8080
```

The standard AWS credential chain provides access to S3. You can also pass an endpoint and region for [R2](https://flaviocopes.com/cloudflare-r2/) or another compatible object store.

Worker projects need esbuild on the `PATH`. Asset-only deployments do not.

## What runs unchanged?

The launch says Workers and Durable Objects code can run unchanged.

The important words are **supported APIs**.

According to the current [Cloudflare compatibility page](https://celld.dev/docs/cloudflare-compat), celld supports:

- module Workers and `fetch`
- Durable Objects with SQLite storage
- alarms
- inbound hibernatable WebSockets
- outbound WebSocket clients
- service bindings
- most JavaScript RPC behavior
- static assets, `_headers`, and `_redirects`
- common Web Platform APIs
- part of Web Crypto
- part of the Node.js API

The project plans to add [D1](https://flaviocopes.com/cloudflare-d1/), Workflows, and perhaps [Queues](https://flaviocopes.com/cloudflare-queues/).

It does not plan to reproduce the whole Cloudflare platform.

[KV](https://flaviocopes.com/cloudflare-kv/), R2 bindings, [Cache API](https://flaviocopes.com/cloudflare-workers-cache/), Workers AI, Vectorize, Hyperdrive, Browser Rendering, Email, custom domains, and TLS termination are outside the current scope.

Cron handlers are not supported. A cell can use durable alarms instead.

celld accepts `wrangler.json` and `wrangler.jsonc`, but not `wrangler.toml`. Unsupported configuration keys stop the deployment.

This loud failure is good.

A compatibility layer that quietly ignores a binding is dangerous.

I would still run a real test suite before moving any Worker. A project can depend on a Node.js module or small runtime behavior without making that dependency obvious in its configuration.

## This is not Cloudflare on your own server

celld implements a focused runtime and state model.

It does not give you Cloudflare’s network.

There is no managed global ingress, anycast routing, DDoS protection, TLS, account system, multi-tenant scheduler, or worldwide placement layer.

One celld fleet runs one application deployment.

You put your own [reverse proxy](https://flaviocopes.com/reverse-proxy/) or load balancer in front. You terminate TLS there. You decide which regions exist and how traffic reaches them.

The nodes communicate over a signed peer protocol. Requests use HMAC authentication, body signatures, clock limits, and replay protection.

But peer traffic is plain HTTP.

The [limitations page](https://celld.dev/docs/limitations) says to keep peer addresses on a private network or an encrypted overlay such as WireGuard or Tailscale. My [free Tailscale course](https://flaviocopes.com/courses/tailscale/) explains how that private network works.

Do not expose the peer port to the public internet.

The project is also not safe for hostile multi-tenant workloads yet.

This is an alpha boundary, not a small footnote.

## The architecture I find most interesting

The most interesting part is not self-hosting a Cloudflare API.

It is using object storage as the foundation of a stateful system.

We usually put a database in the center:

```text
application nodes -> shared database
```

celld turns that into:

```text
requests -> named cells -> private SQLite databases -> object storage
```

The unit of movement is not a row or a table.

It is a small database attached to one logical actor.

This architecture gives us three useful properties.

### Contention stays local

One busy project does not lock the database of another project.

Only requests for the same cell need serialization.

### Failure has a smaller blast radius

A damaged cell affects one object database, not one shared database used by every customer.

The machines and bucket are still shared failure domains. Cell isolation does not eliminate those.

### State moves with the computation

The node that owns a cell also runs its code and opens its SQLite database.

Warm requests do not cross a database network boundary.

When ownership moves, the database moves too.

This is a clean model for chat, collaboration, games, devices, workflows, and agents.

## Cells are a good home for AI agents

My list of cell examples included an AI agent. That one deserves more space.

I run coding agents every day, so I have seen what this workload looks like up close.

An agent is a long-lived, stateful thing. It has a conversation history, working memory, tool results, and a stream of progress events. It might spawn sub-agents that need all the same things.

Map that onto a cell and the fit is obvious.

Each agent gets a name, so every request about it reaches the same place. Its history and memory live in its own SQLite database. It handles one event at a time, so two tool results cannot corrupt its state. It can set an alarm to resume work later. While it waits for a slow model response, or for me, it can hibernate.

The database-per-agent part matters more than it looks.

Agents write constantly. Every step and every tool call produces events worth storing. Put a thousand agents on one shared database and that log stream becomes the scaling problem. Give each agent its own database and the writes never meet.

Isolation is the other half. Running many agents usually means giving each one a container or a VM, so they cannot read each other's state. A cell draws a comparable boundary around a few megabytes of RAM, and it wakes in milliseconds instead of the seconds a container needs.

The dashboard watching an agent can hold a hibernatable WebSocket to its cell. Progress streams in live, and the connection survives while the agent sleeps between steps.

Before celld, building an agent platform this way meant committing the whole product to one provider. Now the same architecture runs on machines you choose.

The cell model can also fit some of my projects.

## I could rebuild Events Logger around cells

[Events Logger](https://flaviocopes.com/i-launched-events-logger/) is the first project I would try.

Today it is a self-hosted [Astro](https://flaviocopes.com/astro/), [HTMX](https://flaviocopes.com/why-i-use-htmx/), and [Alpine.js](https://flaviocopes.com/why-i-use-alpinejs/) application. Every app sends events to one HTTP API. Events go into one local SQLite database. HTMX polls for new events in the dashboard.

The current architecture is small and works well.

A celld version would change the storage boundary.

I would create one cell per Events Logger project:

```text
events:flaviocopes.com
events:waitinglists.dev
events:prototyped.dev
```

Each cell would keep its own events, categories, favorites, and insight cards in SQLite.

The public API would keep the same shape. The top-level Worker would authenticate the API key, read the project ID, and route the request with `idFromName(projectId)`.

The cell would insert the event and update its local aggregates in one transaction.

I could also replace HTMX polling with a hibernatable WebSocket. The dashboard would connect to the project cell and receive new events immediately.

When nobody watches or writes to that project, the cell could hibernate.

This would turn Events Logger from one process with one database into a distributed service with one database per project.

The difficult part would be the global dashboard.

Cells are intentionally isolated. I cannot run one SQL query across every project database.

I would need an explicit index cell that stores a small summary for every project, or I would query several cells and merge the results.

That is not a flaw. It is the price of the isolation model.

It also forces a useful question: which data truly needs to be global?

Events Logger is a good experiment because I can keep the existing API contract and build the celld implementation beside it. I can push the same events to both versions, kill nodes, compare the results, and learn without moving production data.

## Factory Log could gain optional private sync

Factory Log is local-first.

Coding agents append events to a JSONL file on my Mac. A native SwiftUI app watches the file and turns those events into a live dashboard and daily chronicle.

Writers use an interprocess lock. Nothing is uploaded.

I like this privacy boundary, so I would not replace the local file with a mandatory cloud service.

But celld could power an optional private sync mode.

I would use one cell per project, not one cell per task.

All events for a project would stay ordered in one SQLite database. The CLI could append its local JSONL event and then send the same event to the project cell.

The macOS app could open one WebSocket and update when another machine or remote agent reports progress.

This would solve a real limitation: Factory Log currently shows the work on one Mac. A celld fleet could join reports from a laptop, desktop, remote server, and hosted coding agent without sharing one filesystem.

The local JSONL file should remain the immediate source of truth on each machine. Uploads need an outbox and stable event IDs, so retrying a sync cannot create duplicates.

The cell would become a converging shared view, not a reason the local CLI stops working when the network is down.

This is more work than changing the storage call.

celld gives me serialized server state. It does not give me offline-first synchronization automatically.

Still, one project per cell is a natural match.

## Waiting Lists could isolate every list

[Waiting Lists](https://flaviocopes.com/waitinglists-dev/) currently runs as one Cloudflare Worker with one D1 database.

Cloudflare Email Service sends confirmations. A Queue receives delivery events. Rate-limit bindings protect the endpoints. A [Cron Trigger](https://flaviocopes.com/cloudflare-cron-triggers/) removes expired pending subscriptions. [Turnstile](https://flaviocopes.com/cloudflare-turnstile/) protects the admin login.

A celld version could use one cell per waiting list.

The cell would store:

- subscribers
- confirmation state
- consent versions
- token hashes
- delivery events
- list settings

All signups for one list would be serialized by that cell.

Repeated submissions and confirmation requests could run in the same transaction. The cell could set an alarm to remove expired pending records.

An email provider such as [Resend](https://flaviocopes.com/resend-transactional-email-workers/) could be called through `fetch`. Delivery webhooks would route back to the same list cell.

This would give every list a separate SQLite database and a small failure boundary.

But the existing Worker would not run unchanged.

celld does not provide Cloudflare Email Service, Queues, Turnstile, managed rate limits, or Cron Triggers. I would need to replace those pieces or keep them outside the celld fleet.

I would also need an owner cell that knows which lists exist. Otherwise the admin dashboard cannot show a cross-list summary without discovering every database.

The design is possible and interesting.

The current Cloudflare version is still the safer production choice.

## Sitebase is almost designed for this model

[Sitebase](https://flaviocopes.com/sitebase/) is the strongest conceptual match.

Sitebase already gives each workspace its own D1 database. I chose that design to isolate customer data at the database level instead of adding `workspace_id` to every table.

Provisioning those databases takes real machinery.

The control database tracks accounts and routing. New tenant databases are created through the Cloudflare API. They are attached as Worker bindings. Deployments must preserve the dynamic binding list. Local development uses a pool of pre-created databases because it cannot provision them the same way.

celld makes one-database-per-tenant the default.

I could map one workspace or website to one cell. Creating the tenant would mean choosing a name. The first request would create its SQLite database.

No D1 provisioning API. No binding generation. No development pool.

The tenant’s widgets, submissions, subscribers, analytics, and settings would live together in one private database.

Cell alarms could handle per-tenant cleanup and monitoring schedules. Hibernation would fit the workload because most small websites are quiet most of the time.

But Sitebase also shows the current limits of celld better than any toy demo.

Sitebase uses KV, R2, Queues, [Analytics Engine](https://flaviocopes.com/cloudflare-analytics-engine/), Cron Triggers, Turnstile, email, and managed Cloudflare routing. celld does not reproduce those services.

I could replace some of them:

- cell alarms instead of cron for tenant-specific work
- direct object storage access for uploads
- an external email API through `fetch`
- a cell-backed queue for focused workflows
- a reverse proxy for domains and TLS

But I would be building a platform around the runtime.

This could remove the most awkward part of Sitebase, which is tenant database provisioning. It would also make me responsible for ingress, queues, storage adapters, monitoring, security, and failover.

The architecture is a great fit.

The operational trade is much larger.

## I would not rebuild everything with celld

New infrastructure is exciting. That does not make it useful everywhere.

I would not move flaviocopes.com to celld. The site is mostly static, and Cloudflare Pages already serves it well.

I would not rebuild HostingPicker around cells. Most of its value is curated data and client-side comparisons.

I would not move the core calculator of inferencecost.dev. Its state already lives in the URL, which is cheaper and simpler than any database.

StackPlan could use one cell per saved app or report, but it also depends on global provider data, AI Gateway, authentication, and billing. The cell model might help one part without improving the whole product.

My rule would be:

> Use celld when the product contains many independent, stateful things that need coordination.

The strongest signs are:

- one natural name per unit of state
- many units, with only a small active set
- concurrent writes to the same unit
- long-lived WebSockets
- per-tenant data isolation
- alarms or durable workflows
- a need to move state between replaceable machines

If the application is static, read-heavy, or built around global queries, a normal database is probably simpler.

## How I would test celld

I would start with Events Logger.

The first version would run one celld node and one R2 bucket. It would implement only project creation, event ingestion, recent events, and a live WebSocket feed.

Then I would add a second node on a private Tailscale network.

I would put a small reverse proxy in front and send requests to both nodes.

The test would be practical:

1. create 1,000 project cells
2. push numbered events to each project
3. keep WebSocket feeds open for a smaller active set
4. kill one node during writes
5. verify every acknowledged event after recovery
6. restart the node and watch it rejoin
7. compare latency and bucket operations with the current app

I would also test the ugly cases.

What happens when R2 returns `429`? What happens when the private network drops packets? What happens when the fleet has no spare memory? How easy is it to inspect one failed cell and restore it by hand?

celld’s own test strategy is good. It uses differential tests against workerd, deterministic simulations of the coordination protocol, and live fleets with injected failures.

I would still test my workload.

The application decides whether the architecture is useful.

## What I like about the project

I like that celld is small enough to understand as a system.

The architecture has a few strong pieces:

- JavaScript isolates for computation
- one SQLite database per stateful object
- LTX for durable replication
- conditional object storage writes for ownership
- replaceable Rust nodes for execution

The boundaries are visible.

Warm reads are local. Durable writes wait for object storage. Failover waits for lease and restoration. Global queries require an explicit design. Public ingress is the operator’s job.

There is no claim that self-hosting removes failure.

It changes who owns the failure and who can inspect it.

I also like the portability direction.

Durable Objects is a powerful model, but until now it was tied to one managed platform. celld gives that model another implementation.

Even if I keep deploying to Cloudflare, a second runtime can make the API stronger. Compatibility becomes testable. Applications have an exit path. The idea can evolve outside one provider.

## What would stop me from using it today

The alpha label is the main reason.

The supported API surface is still evolving. The security documentation says hostile multi-tenant use is unsafe. Peer traffic needs a private encrypted network. There is no managed ingress or global placement. Capacity and load-shedding behavior are still being tuned.

The project also moves quickly.

The current README, limitations page, and 0.1.0 release notes do not describe every pressure-shedding default in exactly the same way. That is normal two days after a launch, but it means I would read the code and current release notes before operating a fleet.

The largest missing piece for my projects is not a runtime API.

It is the surrounding platform.

Cloudflare gives me DNS, TLS, DDoS protection, logs, queues, email, object storage, AI services, deployment, and secrets beside Workers.

celld gives me a focused compute and state primitive.

That focus is what makes it interesting. It is also why adopting it means assembling more pieces myself.

## A very good architecture experiment

celld is not “Cloudflare, but free.”

It is a different way to package a great stateful programming model.

One named object owns one SQLite database. Active objects live beside the code using them. Idle objects collapse into object storage. A conditional bucket record decides ownership. A new machine can join by pointing at the same bucket.

That is a compact idea with a lot of power.

I can see Events Logger becoming one cell per project. Factory Log becoming one cell per synced project. Waiting Lists becoming one cell per list. Sitebase becoming one cell per workspace without database provisioning machinery.

I can also see exactly why I should not rewrite those production systems yet.

That is the right kind of new infrastructure project.

It changes how I think about architecture before it asks me to trust it with everything.

Read the [celld documentation](https://celld.dev/docs), inspect the [source code](https://github.com/denoland/celld), and pay close attention to the [limitations](https://celld.dev/docs/limitations) and [security boundary](https://celld.dev/docs/security).

I am going to keep an eye on this one.
