From b5a631c3a3e3a30f9a90d41c5dd69ac002095f67 Mon Sep 17 00:00:00 2001 From: atlas Date: Tue, 18 Aug 2026 12:13:05 +0200 Subject: [PATCH 1/2] feat(#3162): warn when a hive with an existing homeserver has not pinned serverName `serverName` is baked irrevocably into every user and room id, so a hive that rebuilds onto a new default is a *different homeserver*, not a renamed one: existing accounts and rooms are stranded, and reverting the config does not undo it. Its neighbours (`gatewayHost`, the forge domain) are routing, rediscovered through `.well-known` and fixed by editing them back. Same diff shape, three orders of magnitude apart in blast radius -- which is an asymmetry a module should carry rather than an operator. An activation script and not `warnings`, which is where this obviously belongs and does not work: the condition needs the host filesystem, and `nixos-rebuild switch --flake` evaluates purely, where `builtins.pathExists "/var/lib/..."` answers false rather than throwing. A `warnings` entry gated on it would evaluate, deploy, and print nothing on every real deployment. Rendered only when `serverName` is null, so a pinned hive has no script rather than a script that stays quiet -- a guard that cries wolf at a correctly-configured deployment makes the next real one read as noise. Never fails the activation: it warns about a choice that cannot be undone, and refusing the rebuild of a hive that already chose deliberately is the opposite of helping. The probed path is read out of the container's own evaluated config rather than hardcoded. A guessed path resolves cleanly and silently never matches, which is the same failure this guard exists to catch one level up. --- nix/host-modules/hive-matrix.nix | 45 ++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/nix/host-modules/hive-matrix.nix b/nix/host-modules/hive-matrix.nix index 0661aa6b..b2089c34 100644 --- a/nix/host-modules/hive-matrix.nix +++ b/nix/host-modules/hive-matrix.nix @@ -758,6 +758,51 @@ in # would hand tuwunel an empty file on first boot and break every # registration until restart. Idempotent; # docs/matrix.md::Provisioning flow. + # Tell an operator whose homeserver already exists that `serverName` is + # unpinned, at the one moment they are looking: the rebuild. + # + # ⚠️ An activation script and NOT `warnings`, which is where this + # obviously belongs and does not work. The condition needs the host + # filesystem — does a homeserver already exist here? — and + # `nixos-rebuild switch --flake` evaluates PURELY, where + # `builtins.pathExists "/var/lib/…"` answers **false** rather than + # throwing. A `warnings` entry gated on it would evaluate, deploy, and + # print nothing, on every real deployment. Same shape as an option whose + # consumer is disabled: renders perfectly, does nothing. + # + # Only rendered when `serverName` is null, so a hive that pinned it + # cannot be nagged — the script does not exist there rather than + # existing and choosing to stay quiet. A guard that cries wolf at a + # correctly-configured deployment is worse than no guard, because the + # next real one is read as noise too. + # + # Never fails. This warns about a choice that cannot be undone; refusing + # the activation would break the rebuild of a hive that had already made + # that choice deliberately, which is the opposite of helping. + system.activationScripts.hive-matrix-servername-pin = lib.mkIf (cfg.serverName == null) ( + lib.stringAfter [ "var" ] '' + # The homeserver's own database, asked of the container's evaluated + # config rather than hardcoded: a guessed path resolves cleanly and + # silently never matches, which is exactly the failure this guard + # exists to avoid one level up. + dbDir=${ + lib.escapeShellArg ( + # Same host-side container-root prefix this module already writes + # by hand for the SSO secret copy above — not a second convention. + "/var/lib/nixos-containers/hive-matrix" + + config.containers.hive-matrix.config.services.matrix-tuwunel.settings.global.database_path + ) + } + if [ -d "$dbDir" ]; then + echo "hive-matrix: WARNING — services.hyperhive.swarm.matrix.serverName is unset, and this host already has a homeserver at $dbDir." + echo "hive-matrix: it is defaulting to ${effectiveServerName}, which is baked into every NEW user and room id." + echo "hive-matrix: if ids here were minted under a different name, existing accounts and rooms are stranded — reverting the config does NOT undo it." + echo "hive-matrix: pin whichever name this homeserver already uses, e.g.:" + echo "hive-matrix: services.hyperhive.swarm.matrix.serverName = \"''${HIVE_MATRIX_EXISTING_SERVER_NAME:-}\";" + fi + '' + ); + system.activationScripts.hive-matrix-register-token = lib.stringAfter [ "var" ] '' tokenFile=${lib.escapeShellArg (toString cfg.registrationTokenFile)} if [ ! -s "$tokenFile" ]; then From 834ea88420a4e6aee51a31a217e4c241b0501187 Mon Sep 17 00:00:00 2001 From: atlas Date: Tue, 18 Aug 2026 12:22:13 +0200 Subject: [PATCH 2/2] docs(#3162): point the serverName pin section at the new rebuild warning argus's optional note. Nothing in the doc was inaccurate, but an operator who sees the activation-time warning has no thread back to the page that explains why pinning matters -- and this section is the explanation. States the one property they need from it: it never fails the rebuild, so acting on it is theirs to do before the ids are minted. --- docs/matrix.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/matrix.md b/docs/matrix.md index b0c0040f..5bc4328e 100644 --- a/docs/matrix.md +++ b/docs/matrix.md @@ -67,6 +67,12 @@ services.hyperhive.swarm.matrix = { }; ``` +A rebuild on a host that already has a homeserver prints a +`hive-matrix: WARNING — … serverName is unset` line when this is missing, +naming the value it is about to default to. That warning is why this +section exists; it never fails the rebuild, so it is on you to act on it +before the ids are minted. + ## Default-closed firewall `openFirewall` defaults to `false` (secure-by-default): the host