diff --git a/docs/swarm/ca.md b/docs/swarm/ca.md index f9e24793..1d128c35 100644 --- a/docs/swarm/ca.md +++ b/docs/swarm/ca.md @@ -154,9 +154,8 @@ this: it self-signs exactly as it always has. ## Distributing the root -The root **key** is a runtime file for an obvious reason: the nix store -is world-readable and content-addressed, so a key committed to a flake -is a key *published* to every consumer of that flake. +The root **key** is a runtime file for the reason in +[Constraints on the material](#constraints-on-the-material). The root **certificate** is a runtime file as a *consequence* — it lives beside the key under `swarm.ca.stateDir` — and that has a cost worth diff --git a/nix/host-modules/swarm-ca.nix b/nix/host-modules/swarm-ca.nix index 29a29bfe..74869644 100644 --- a/nix/host-modules/swarm-ca.nix +++ b/nix/host-modules/swarm-ca.nix @@ -1,33 +1,19 @@ # The swarm root CA: the anchor a whole swarm shares, and the issuer of # each hive's own CA (which is where it gets used — see ./hive-tls.nix). # -# Why a hierarchy at all: cross-hive trust used to be hand-pinned per -# peer, so every hive had to name every other one — O(n²) configuration -# that a new hive could only join by editing all the others. One root -# makes it O(1): trust the root once and every present *and future* peer -# validates. The per-peer CA field is gone precisely because this -# replaced it. +# One root rather than per-peer pinning, because cross-hive trust is then +# O(1): trust the root once and every present *and future* peer validates, +# instead of every hive having to name every other one. # -# Two provisioning modes, ONE structure — what differs is who puts the -# artifacts on disk, never what the artifacts are: +# Two provisioning modes share ONE structure — what differs is who puts +# the artifacts on disk, never what the artifacts are. Which mode does +# what, and what moving between them costs: +# `docs/swarm/ca.md::Two provisioning modes, one structure`. # -# - autoconfigured (`services.hyperhive.deploy.singleHostSwarm`, or -# `autoConfigure` set directly): the unit below generates the root -# here on first boot. -# - operator-provided (multi-host): the operator installs the root cert -# — and, on a host that does not hold the root key, the hive CA too — -# into the state dirs, and this unit does nothing. Splitting a -# single-host swarm across hosts is then a matter of moving the -# service dirs and turning `autoConfigure` off, not of a second code -# path that has to be kept agreeing with the first. -# -# The root KEY is the reason this is a runtime file and not a nix option: -# the store is world-readable and content-addressed, so a key committed -# to a flake is a key *published* to every consumer of that flake. -# -# ⚠️ The root CERT is therefore a runtime file too, which costs something: -# nothing whose trust store is built at build time can name it. See -# docs/swarm/ca.md § "Distributing the root". +# ⚠️ Root key AND root cert are runtime files, never nix options. The key +# for the obvious reason; the cert as a consequence, and that one costs +# something real — nothing whose trust store is built at build time can +# name it: `docs/swarm/ca.md::Distributing the root`. { lib, config,