feat(nix): the matrix server_name follows the swarm domain too

A swarm runs one homeserver, so its identity belongs to the swarm and
not to whichever hive happens to host it -- otherwise relocating the
container between hives reads as a different homeserver rather than a
move. `gatewayHost` moved for that reason a commit ago; `serverName`
was left behind, which made the identity hive-shaped and the routing
swarm-shaped.

⚠️ This is the one value on the page that a rebuild cannot undo.
`gatewayHost` is a routing detail clients rediscover through
`.well-known`; `server_name` is embedded in every user and room id, so
adopting a new one strands the existing accounts and rooms rather than
renaming them -- their ids still name a homeserver that stops
answering. Moving the DEFAULT is safe; moving a RUNNING deployment is
not, and existing hives must pin the value they already minted ids
under before rebuilding.

The fallback stays total on a null swarm domain for the same reason as
its neighbours: the required-domain assertion in hive-network.nix is
what should fire, not a coercion error from an unrelated option
interpolating null.

The legacy-pin eval probe now covers `serverName` as well. It existed
to answer "what do I set so old deployments don't change", and was
proving that only for the two values that are cheap to change -- the
irreversible one was the one it did not assert.
This commit is contained in:
atlas 2026-08-09 17:09:54 +02:00 committed by mara
commit a5210311bf
2 changed files with 81 additions and 44 deletions

View file

@ -33,29 +33,39 @@ Two distinct hostnames:
*irrevocably* in every `@user:<server_name>` and `!room:<server_name>` *irrevocably* in every `@user:<server_name>` and `!room:<server_name>`
identifier minted on this homeserver. Cannot be changed later identifier minted on this homeserver. Cannot be changed later
without abandoning every account and chat history. Defaults to the without abandoning every account and chat history. Defaults to the
bare `services.hyperhive.domain`; clients auto-discover the actual bare `services.hyperhive.swarm.domain`; clients auto-discover the
API endpoint via the `.well-known/matrix/{client,server}` routes actual API endpoint via the `.well-known/matrix/{client,server}`
the hive-gateway serves at that domain. routes the gateway serves at that domain.
- **`gatewayHost`** — the API listener hostname, where the gateway's - **`gatewayHost`** — the API listener hostname, where the gateway's
matrix vhost proxies `/_matrix/*` to tuwunel. Defaults to matrix vhost proxies `/_matrix/*` to tuwunel. Defaults to
`chat.<services.hyperhive.swarm.domain>` — the **swarm's** domain, `chat.<services.hyperhive.swarm.domain>`. Set to `null` to skip the
because a swarm runs one homeserver. Set to `null` to skip the
gateway vhost (tuwunel stays direct on `httpPort`). gateway vhost (tuwunel stays direct on `httpPort`).
⚠️ Only this one moved. `serverName` still defaults to the bare hive Both now default under the **swarm** domain, because a swarm runs one
domain, and the two are independent by design: `gatewayHost` is a homeserver: tying its identity to a single hive's domain would make
routing detail clients rediscover through `.well-known`, while relocating the container between hives look like a different
`serverName` is baked into every user and room id. A deployment that homeserver.
wants its old API hostname pins
`gatewayHost = "matrix.<hive-domain>"` — exactly what the previous
default rendered.
**Breaking change**: `serverName` used to default to ⚠️ **They are still not interchangeable, and the difference is the
`matrix.${services.hyperhive.domain}`. Existing homeservers must set cost of changing one.** `gatewayHost` is a routing detail clients
the option explicitly to preserve their existing user / room IDs rediscover through `.well-known`, so it is safe to move on a running
before rebuilding. The default flipped because the bare hive-domain deployment. `serverName` is baked into every user and room id, so
makes for cleaner matrix IDs and `.well-known` delegation hides the adopting a new one does **not** rename the existing users and rooms —
sub-domain from the user-facing identifier. it strands them, because their ids still name a homeserver that no
longer answers.
**Breaking change — pin `serverName` before rebuilding.** Its default
has now moved twice: from `matrix.${services.hyperhive.domain}`, to
the bare hive domain, and now to the swarm domain. Any homeserver that
has already minted ids must name the value it minted them under:
```nix
services.hyperhive.swarm.matrix = {
# whichever this deployment already uses
serverName = config.services.hyperhive.domain;
gatewayHost = "matrix.${config.services.hyperhive.domain}";
};
```
## Default-closed firewall ## Default-closed firewall

View file

@ -7,14 +7,29 @@
let let
cfg = config.services.hyperhive.swarm.matrix; cfg = config.services.hyperhive.swarm.matrix;
networkCfg = config.services.hyperhive.network; networkCfg = config.services.hyperhive.network;
hyperhiveDomain = config.services.hyperhive.domain;
swarmDomain = config.services.hyperhive.swarm.domain; swarmDomain = config.services.hyperhive.swarm.domain;
# ⚠️ Falls back to the HIVE domain, and must keep doing so even though # Falls back to the SWARM domain: a swarm runs one homeserver, so its
# `gatewayHost` moved to the swarm's: `serverName` is the matrix # identifier belongs to the swarm rather than to whichever hive happens
# identifier baked into every user and room id, so changing it is a # to host it — otherwise moving the container between hives would look
# different homeserver rather than a rename. The two are independent on # like a different homeserver.
# purpose — see the `gatewayHost` description below. #
effectiveServerName = if cfg.serverName != null then cfg.serverName else hyperhiveDomain; # ⚠️ Changing this default is a BREAKING change in a way that moving
# `gatewayHost` was not: `serverName` is baked irrevocably into every
# user and room id, so a deployment that rebuilds onto a new one is a
# *different homeserver*, not a renamed one. Existing hives pin the old
# value explicitly (see the option's description); the default is what
# a fresh swarm gets.
#
# Total on a null swarm domain, deliberately: the required-domain
# assertion in hive-network.nix is what should fire, not a coercion
# error from an unrelated option interpolating null.
effectiveServerName =
if cfg.serverName != null then
cfg.serverName
else if swarmDomain != null then
swarmDomain
else
"invalid";
# fluffychat-web build fixes: nixpkgs's `flutter341.buildFlutterApplication` # fluffychat-web build fixes: nixpkgs's `flutter341.buildFlutterApplication`
# skips the dart web-worker compile + the emscripten native_imaging # skips the dart web-worker compile + the emscripten native_imaging
@ -137,21 +152,32 @@ in
Matrix `server_name` the host part of every user ID Matrix `server_name` the host part of every user ID
(`@argus:<server_name>`) and room ID minted on this (`@argus:<server_name>`) and room ID minted on this
homeserver. CRITICAL: must be stable from day one because homeserver. CRITICAL: must be stable from day one because
it's embedded irrevocably in the identifiers. Defaults to it's embedded irrevocably in the identifiers.
`services.hyperhive.domain` (the bare hive domain). Combined
with the `.well-known/matrix/{client,server}` routes the Defaults to `services.hyperhive.swarm.domain` (the bare swarm
hive-gateway serves at that domain, clients auto-discover the domain), because **a swarm runs one homeserver** tying its
identity to a single hive's domain would make relocating the
container between hives look like a different homeserver.
Combined with the `.well-known/matrix/{client,server}` routes
the gateway serves at that domain, clients auto-discover the
actual matrix endpoint without needing a subdomain. Override actual matrix endpoint without needing a subdomain. Override
here only if you need a different server_name shape (e.g. here only if you need a different server_name shape (e.g.
`matrix.<domain>` if you want the subdomain split, or
`chat.example.org` for a bespoke hostname). `chat.example.org` for a bespoke hostname).
**Breaking change**: this used to default to **Breaking change, and the one on this page that cannot be
`matrix.''${services.hyperhive.domain}`. matrix IDs embed undone by rebuilding.** This default has now moved twice from
the server_name irrevocably, so existing homeservers must `matrix.''${services.hyperhive.domain}`, then to the bare hive
set `services.hyperhive.swarm.matrix.serverName = "matrix.''${services.hyperhive.domain}";` domain, and now to the swarm domain. Every existing homeserver
explicitly to preserve their existing user / room IDs must pin whichever value it already minted ids under, e.g.
before rebuilding.
```nix
services.hyperhive.swarm.matrix.serverName =
config.services.hyperhive.domain; # or "matrix.''${…domain}"
```
before rebuilding. Adopting a new `server_name` does not rename
the old users and rooms it strands them, because their ids
still name a homeserver that no longer answers.
''; '';
}; };
@ -219,13 +245,14 @@ in
bottom of that doc. bottom of that doc.
**`gatewayHost` and `serverName` are different things, and **`gatewayHost` and `serverName` are different things, and
only this one moved.** `gatewayHost` is the API listener they carry very different costs.** `gatewayHost` is the API
hostname (where nginx proxies `/_matrix/*`) and is free to listener hostname (where nginx proxies `/_matrix/*`) and is
change: it is a routing detail clients rediscover through free to change: it is a routing detail clients rediscover
`.well-known`. `serverName` is the matrix-identifier domain through `.well-known`. `serverName` is the matrix-identifier
embedded **irrevocably** in every user and room id changing domain embedded **irrevocably** in every user and room id
that is a different homeserver, not a rename, so it still adopting a new one is a different homeserver, not a rename.
defaults to the bare hive domain and is untouched here. Both defaults now sit under the swarm domain, but only this
one is safe to move on a running deployment.
A deployment that was running before this moved keeps its A deployment that was running before this moved keeps its
current name by pinning current name by pinning