1,100+ tests · rocky 10 · selinux enforcing gossip :7300 raft :7302 api :7304 rocky 10 / systemd 257

Architecture

Gossip for liveness, Raft for decisions, and a hard rule about which one answers what.

Regions, not one global cluster

At hundreds of nodes across datacenters, a single Raft group is the wrong tool: WAN latency lands in the write path, and one partitioned datacenter freezes everything. So each region gets its own Raft group and its own scheduler.

Two regions, each an independent Raft group, joined only by WAN gossip Each region has three to five control nodes running Raft and the scheduler, and any number of worker nodes that only gossip. Workers are never in the Raft membership. The two regions are joined by a second gossip pool between control nodes alone. region eu-west 3-5 CONTROL nodes raft · scheduler · alias table its own Raft group — writes never cross the WAN region us-east 3-5 CONTROL nodes raft · scheduler · alias table a partition here freezes scheduling, not serving WAN gossip LAN gossip (SWIM) rk-01 worker rk-02 worker rk-03 worker … rk-247 worker workers are never in the Raft membership consensus stays at 3-5 members however large the fleet gets — they subscribe to desired state from any control node instead
One Raft group per region, not one global group: WAN latency would otherwise sit in every write path, and one partitioned datacenter would freeze the whole cluster.

Workers are never in the Raft membership. That is what keeps hundreds of nodes viable: consensus cost stays fixed at three to five members no matter how large the fleet gets.

A partitioned region keeps serving; it just cannot schedule anything new. Its peers report it as unreachable rather than pretending it is healthy.

The invariant worth memorising

Three components could plausibly answer "is rk-07 up?". If all three are allowed to, they will disagree, and every incident review will open by arguing about which one was right. So, by fiat:

  • SWIM is the only liveness authority. Nothing else decides up or down.
  • The Raft roster is the only membership authority. Admission is a join token minted for a role; who votes is roster/control/<id>, written from an attestation made with the control key. A role or Raft address a node gossips about itself decides nothing.
  • In the roster but not in SWIM means down. In SWIM but not in the control roster means a worker, whatever it claims to be.

Liveness must never be pushed through Raft. Routing heartbeats through consensus is the most reliable way to melt a Raft cluster.

What goes in Raft, what does not

The rule: consensus for decisions the cluster makes, eventual consistency for facts a node observes about itself. Anything where two divergent copies would cause a double-run, a lost write, or a port conflict belongs in Raft.

Raft (linearizable)Observed-state store (eventual)
Workload specs and allocationsNode CPU, memory, disk, load
Alias → deployment tableWhich node holds which artifact
Cluster-wide port leasesUsage counters
Secrets (encrypted)Observed workload state
Control-group membershipDescriptive node labels

It is called the observed-state store, never the "config store". The word config invites people to put decisions in it, and every decision that lands there is a future outage.

The Raft side is built: leader election, replication, restart from disk, linearizable reads, transactions and leases. Consensus covers what it guarantees and where the sharp edges are.

The cluster bus

Every datagram is wrapped in an HMAC-SHA256 envelope carrying a version, a timestamp and a nonce, under the key for its traffic — gossip, fetch or control, which are held by different nodes (see Security). Five things make it hold up:

  • Length-prefixed signing. Fixed-width fields plus a length-prefixed payload, so no choice of payload can be made to reproduce a different message's signed bytes.
  • A nonce cache that is actually checked. A freshness window alone does not stop a replay inside the window — it only bounds how long the attack stays possible.
  • A channel tag is signed. Gossip, Raft and artifact fetch are different conversations; with the tag inside the MAC, an envelope captured on one does not verify on another, whether or not they share a key today.
  • The recipient is signed. An envelope addressed to node A and replayed to node B inside the window used to verify at B, whose nonce cache had never seen it. Now B refuses anything addressed to somebody else, and a reply is addressed to the nonce of the request it answers.
  • Failure is per-datagram. A forged packet is counted and discarded; the receive loop keeps running. A loop that dies on one bad packet is a one-packet denial of service.

One byte in front of each payload demultiplexes SWIM traffic from application traffic. It sits inside the signed envelope, so it cannot be tampered with.

Crates

CrateResponsibility
soli-one-protoWire types and the signed envelope. No I/O, almost no dependencies — so proxy/, db/ and es/ can speak to the cluster without pulling the whole tree.
soli-one-busAuthenticated UDP transport, nonce cache, framing.
soli-one-membershipSWIM via foca; node identity and address-conflict resolution.
soli-one-storeContent-addressed artifacts: chunking, manifests, materialize, GC, peer fetch.
soli-one-consensusRaft via openraft: the durable journal, the state machine, the linearizable KV. Nothing outside it names openraft.
soli-one-schedPlacement, the alias table, cluster port leases. The planner is pure — see Scheduling.
soli-one-execWorkload supervision through transient systemd units; adoption, restart policy, ports, containers.
soli-one-vipVirtual IP failover. The address is bound if and only if the lease is held — see Operations. Library only: no binary links it yet.
soli-one-federationWhat may cross a WAN link, and where a cross-region request goes.
soli-one-fuseThe /soli view. Read-only, off the critical path. Library only: no binary links it yet, so nothing mounts it.
soli-one-acmeThe elected ACME orderer: the lease, the rate-limit ledger, the sealed bundle. Library only: no binary links it yet; each proxy orders its own certificates.
soli-one-ovhOVHcloud request signing, DNS reconciliation, Additional-IP moves. Library only: no binary links it yet.
soli-one-agentsoli-oned: fact sampling, the cluster registry, the local socket.
soli-one-clione: the aggregate views.