Skip to content
Go back

How celld Gets Rid of the Control Plane

Published:  at  11:00 AM
celld

Durable Objects unbundled — what happens when a bucket becomes the durability layer, the coordination service, and the control plane all at once

4 of 4

TL;DR

• Nodes discover each other by writing and reading lease records in the bucket — no account service, no join command, no fixed membership list.

• Failure detection is just a lease timing out. Load balancing is nodes reading a shared fleet sample every five seconds. Neither needs a dedicated service.

• celld's own words: 'no membership protocol, no failure detector, no consensus service.' A whole category of distributed-systems infrastructure simply isn't there.

• The cost of collapsing all of that into one bucket: whoever holds the bucket credentials controls deployments, all cell state, ownership, and can impersonate any peer in the fleet.

The second post in this series covered how a single cell gets claimed: a conditional write to the bucket, one winner, an epoch that fences out the loser. Zoom out from one cell to the whole fleet, and the same trick is doing something bigger — it’s standing in for an entire control plane.


What a control plane usually does

Ask what infrastructure a distributed system typically needs just to know about itself, and the list is long:

Something has to track which nodes exist right now — membership. Something has to notice when one of them stops responding — failure detection, usually a purpose-built protocol like SWIM or a Phi Accrual detector, not just “did it time out.” Something has to decide which node should run which piece of work, and rebalance when the picture changes — scheduling and placement. And something has to let a node find where a specific piece of state currently lives — discovery.

Kubernetes has a control plane built from several of these services working together. Consul and ZooKeeper exist largely to provide the coordination primitives underneath them. This is normally not optional infrastructure — it’s assumed.

celld doesn’t have any of it, as a separate thing you run.


The bucket is the directory

A celld node starts with two things: bucket credentials, and an address its peers can reach it at. It writes a lease record to the bucket. Other nodes read that record to learn it exists.

node starts


writes lease to bucket
(address, load metrics)


other nodes read it


node is now discoverable

There’s no join command. There’s no membership file anyone maintains. celld’s own documentation puts it plainly: “every node discovers owners and peers from bucket leases; there is no account or join service.”

That lease isn’t just a heartbeat — it carries load information. Each node’s record in the bucket includes figures like how many cells it currently owns, how many it’s keeping resident in memory, its WebSocket count, RSS, CPU, and file-descriptor pressure. Every node reads a shared fleet-wide sample of these roughly every five seconds, and rebalances ownership toward whichever node is carrying the least load relative to its weight.

That’s the scheduler. It’s a periodic read of a file in a bucket, not a service.


Failure detection is just a timeout

The classical failure-detector literature — Phi Accrual, SWIM, and the rest — exists to answer a genuinely hard question: is that node actually dead, or is the network just slow right now? Getting this wrong in either direction is expensive, so a lot of engineering has gone into doing it well.

celld sidesteps the question rather than answering it more cleverly. A node’s lease has an expiry. If it isn’t renewed, the lease lapses, and the node is presumed gone:

lease not renewed


    lease expires


node presumed gone


its cells become claimable

There’s no gossip, no phi-accrual scoring, no quorum of observers agreeing a node is unreachable. Ownership arbitration for a cell — covered in the second post in this series — is a conditional write to the bucket, so exactly one node ever owns a cell at a time. celld’s own framing: “a conditional bucket write gives a node ownership of a cell, so exactly one node owns a cell at a time — no membership protocol, no failure detector, no consensus service.”

That’s three named categories of distributed-systems infrastructure, in celld’s own words, that this design does not include as separate components.


What this actually buys you operationally

None of this is subtle sleight of hand — it’s a real trade of one kind of infrastructure for another you were probably already running.

There’s no ZooKeeper ensemble to keep healthy. No etcd cluster to back up, upgrade, and reason about quorum loss on. No separate scheduler service with its own failure modes. The bucket you already need for durability — covered in the first post and the third — is doing double duty as the fleet’s directory and arbiter.

The corollary, worth sitting with, is that this doesn’t reduce the amount of coordination happening. It relocates all of it into one artifact.


The bucket is also the vault

Here’s where the design gets sharper-edged. The fleet bucket doesn’t just hold cell state and ownership records. celld’s own documentation lists what it stores: the deployments, the cell state, the ownership leases, the node leases, and the shared peer-authentication secret.

That last item matters more than it might look at first glance. Peer-to-peer traffic between celld nodes — cell handoffs, the operator API, tunnel requests — is authenticated with an HMAC derived from that shared fleet secret. It lives in the same bucket as everything else.

fleet bucket

    ├── deployments
    ├── cell state
    ├── ownership leases
    ├── node leases
    └── peer-authentication secret

Put those together and the implication is direct: whoever holds valid bucket credentials doesn’t just get read access to your data. They can deploy code, claim ownership of any cell, and mint valid HMACs to impersonate any peer in the fleet. There is no second, independent credential standing between “has the bucket” and “controls everything the fleet does.”

celld’s security documentation is correspondingly blunt about this: give each credential access to one fleet bucket only, and rotate a credential immediately after any suspected disclosure. Scoping isn’t a hardening suggestion here — it’s the only boundary that exists.

There’s a second, unrelated gap worth flagging alongside it: celld doesn’t terminate TLS on its listeners. Peer traffic crosses the network as plaintext HTTP once a tunnel is established, protected only by that HMAC signature on the handshake, not by encryption on the bytes after it. The documentation’s recommendation is to run nodes on a private network you trust, or lay an encrypted overlay like WireGuard or Tailscale on top. That’s an operational requirement, not a default.


What this isn’t trying to be

It’s worth being precise about what “no control plane” does and doesn’t mean here, because it’s easy to over-read.

Eliminating membership protocols, failure detectors, and a consensus service is a deliberate architectural choice — a category of infrastructure genuinely isn’t needed, not just hidden. But celld’s own limitations documentation is equally direct about what else is absent, and frames it differently: “a fleet runs one application; celld has no account service, multi-tenant scheduler, or managed ingress.” That’s not an elegant trade-off — it’s a boundary. You don’t get multi-tenancy, and you don’t get a scheduler that spans multiple applications competing for the same nodes.

This is the same caution the first post in this series raised about celld versus Cloudflare’s managed platform: the absence of a control plane for coordination is a genuine design win. The absence of an account service, scheduler, and ingress layer is just things you’d have to build or bring yourself.


One bucket, three jobs

The pattern across this whole series is the same idea wearing different clothes. The bucket is the ownership arbiter, deciding which node runs which cell — that was the second post. It’s the backstop of the durability layer, the thing a cell is rebuilt from when every peer holding a write is gone — that was the third. Now it’s the fleet directory and the root of trust for the entire network of peers.

That concentration is exactly what makes the architecture as simple as it is — one artifact to reason about instead of four separate coordination services. It’s also exactly why bucket-credential hygiene isn’t a footnote in celld’s operational story. It’s the whole story. There’s no second door to lock if the first one is already open.


Comments

Loading comments...

Leave a comment