diff --git a/nix/host-modules/swarm.nix b/nix/host-modules/swarm.nix index 2d785c5f..e2756ca2 100644 --- a/nix/host-modules/swarm.nix +++ b/nix/host-modules/swarm.nix @@ -46,6 +46,13 @@ let # implicitly. Left out, its vhost falls back to the hive leaf and the # swarm's front page opens with a name mismatch. ++ lib.optional swarmCfg.ui.enable swarmCfg.ui.domain; + + # Hives whose entry still carries the removed `certFingerprint`. Scanned + # here, at top level, because that is the only place an assertion about a + # submodule field can actually live — see the option's own comment. + pinnedHives = lib.attrNames ( + lib.filterAttrs (_: hive: hive.certFingerprint != null) swarmCfg.hives + ); in { options.services.hyperhive.swarm.hives = lib.mkOption { @@ -128,6 +135,33 @@ in silently excluded from `wg-hive`). ''; }; + + # Removed, and re-declared only so an existing definition + # produces an error that says *what happened*. Without it the + # module system says `The option ... does not exist`, which + # tells an operator nothing about why it went or what replaced + # it — and this field was set on real deployments. + # + # ⚠️ `lib.mkRemovedOptionModule` cannot do this job here, and + # neither of its two halves survives the move into a submodule: + # its `apply = throw` fires only when the value is READ, and + # nothing reads this any more (that being the point of removing + # it); its `config.assertions` half would land on a submodule + # that declares no `assertions` option at all. It is a + # top-level tool. The assertion below is the working shape, and + # it is the same one ./swarm-peers-removed.nix uses for the + # analogous `peers..caCert`. + certFingerprint = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + visible = false; + internal = true; + description = '' + Removed. Trust inside a swarm comes from the swarm root CA + (`services.hyperhive.swarm.ca`, docs/swarm/ca.md), which + replaces per-hive leaf pinning entirely. + ''; + }; }; } ) @@ -232,6 +266,38 @@ in Declared hives: ${lib.concatStringsSep ", " (lib.attrNames swarmCfg.hives)} ''; } + { + # An error rather than a warning, deliberately. Re-declaring the + # removed option above is what stops "option does not exist"; on + # its own it would also turn a config that used to FAIL into one + # that quietly evaluates with the setting ignored, which is a + # worse answer than the unhelpful error it replaces. The operator + # asked for a removal error, so this stays fatal until the field + # is gone from their config. + # + # Wording follows `lib.mkRemovedOptionModule`'s, so it reads like + # every other removal the module system reports. + assertion = pinnedHives == [ ]; + message = '' + The option definition `services.hyperhive.swarm.hives..certFingerprint' + no longer has any effect; please remove it. + + Still set on: ${lib.concatStringsSep ", " pinnedHives} + + It pinned a peer hive's TLS leaf for hive-c0re's own peer HTTPS + checks, and was removed along with the dashboard feature it + existed to serve — nothing else ever consumed it. + + There is no replacement, and none is needed for a hive inside + this swarm: trust comes from the swarm root CA + (services.hyperhive.swarm.ca — see docs/swarm/ca.md), which + every hive chains to, so one anchor replaces per-hive pinning. + What that genuinely drops is trusting a hive whose root this + swarm does NOT own — another swarm's, or one keeping its own + CA. That is a cross-swarm problem and wants a mechanism + designed for it. + ''; + } ]; };