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.
This commit is contained in:
atlas 2026-08-18 12:13:05 +02:00 committed by mara
commit b5a631c3a3

View file

@ -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:-<the name already in use>}\";"
fi
''
);
system.activationScripts.hive-matrix-register-token = lib.stringAfter [ "var" ] ''
tokenFile=${lib.escapeShellArg (toString cfg.registrationTokenFile)}
if [ ! -s "$tokenFile" ]; then