kolu
Docs

The daemon spine

A surface in a browser tab lives and dies with the tab. A surface in a daemon outlives every client that connects to it — and, more awkwardly, outlives its own code. You will ship a new build while a client is mid-session, and the old process is still there, holding the socket, serving the old surface. The daemon stratum exists to make that moment safe.

The daemon spine — binary and supervisorThe daemon binary acquires a single-instance pid gate, serves its router over a unix socket, waits its lifetime, then closes the socket and releases the gate. The supervisor drives it: ensure kills, waits for the pid to go, spawns, connects and handshakes; restart captures, drains, recycles live, and reattaches. The frozen control-core identity is probed version-agnostically before the versioned handshake, so it survives a skew.DAEMON (binary) — the served lifetimeacquirePidGatesingle-instanceserve routerover unix socketwait lifetimeclose socket ·release gateDaemonExitfrozen control core — identity probeversion-agnostic · read BEFORE the handshakeso it survives a version skewSUPERVISOR (client) — drives the lifecycleensure()killwaitForPidGonespawnsurvivableconnecthandshakeversioned① identity② then handshakerestart()capturedrainrecyclereattachthe LIVE recyclea brief reconnect, not a dropped sessionsingle-instance pid gatethe unix socket connects the halves
Two coupled halves. The daemon runs a served lifetime behind a single-instance pid gate; the supervisor drives it with ensure() and a live restart(). The frozen control-core identity is read before the versioned handshake — the version-agnostic probe that survives a skew.

The problem a daemon has that a page does not

A page never has to answer “which version of me are you talking to?” — there is only one, and it reloads. A daemon has to answer it constantly, because the process a client is attached to and the build you just shipped can be different things. If the client cannot tell them apart, an upgrade either drops every session or, worse, quietly serves stale behaviour under the new build’s name.

So a daemon needs two things a page does not: a way to say which build it is (covered in How to bake an identity), and a small part of itself that never changes shape, so that question can always be asked — even across a version skew. That unchanging part is the spine.

The control core is frozen so it survives a skew

Identity is read over a version-agnostic channel, before the versioned handshake. That ordering is the whole trick: if reading a daemon’s identity required speaking its current contract, then the one situation where you most need the identity — a version mismatch — is exactly when you could not read it. By freezing the control core and probing it pre-connect, a supervisor can always learn what it is looking at, then decide whether to talk to it, recycle it, or drain it.

Everything behind the spine is free to churn. The surface can grow cells, the handlers can change, the build can be replaced wholesale — as long as the frozen core still answers “who are you?” the same way it always has.

Recycle without lying

With a stable identity and a version-agnostic probe, a live upgrade becomes a decision rather than a gamble. converge reads the running identity, compares it to what you baked, and acts by a declared policy — recycle on skew, or drain the newer build in. The client sees a brief reconnect, not a dropped session. The contract is the promise the daemon keeps across that swap: a recycle may replace every byte of the process, but it must not change what the wire says without saying so. This is the same instinct as reactive honesty, one layer down — a recovered connection must not silently be talking to a different thing.

Why the halves are two packages

The daemon binary code and the supervising code are split into two packages, and the split is load-bearing, not tidiness. A supervisor runs in the client and must never be able to change what a daemon restart loads — so it is kept out of the daemon’s hashed closure entirely. The package boundary is the hash boundary: you cannot accidentally make a supervisor edit look like a new daemon build, because the supervisor is not in the build id at all.

A daemon is not a fleet

It is tempting to assume that “long-lived” and “runs on other machines” travel together. They do not. kaval is the proof: a daemon on your own box, one process, no ssh, no map — everything in this stratum, none of the multi-host one. The spine is what a daemon needs to be recycled honestly; it says nothing about where the daemon runs. That is a separate axis, with its own exemplar.

The trade

Freezing a control core is a real cost: you are committing to a shape forever, because the entire recycle story depends on old and new agreeing on how to ask “who are you?” Change that, and you break the one channel that was supposed to survive change. The daemon stratum accepts a small, permanent rigidity at the core in exchange for total freedom to replace everything around it. For a process that must be upgraded under load without dropping its clients, that is the trade worth making — and the narrowness of the frozen part is what keeps the price low.