swarm: move the matrix packages to deploy, where their enable already lives

Which build a service runs is a decision of the host that runs it, so
`*.package` belongs with `enable` rather than in the namespace every hive
must agree on byte for byte.

The `gui` pair was the sharpest case and the reason this starts here:
`gui.enable` moved to `deploy.*` in an earlier slice while `gui.package`
stayed behind, so one web client's two halves sat in two namespaces —
and hive-matrix.nix said so, in a comment directly above the option.
The diff removes a stated inconsistency rather than introducing a rule.

Both old paths keep working through `mkRenamedOptionModule`; both are
settable (neither is `readOnly`), so both get a shim. The `matrixOldPath`
fixture in module-eval.nix now sets eight options through their
pre-rename paths instead of six, so dropping either new shim fails the
eval rather than silently dropping a definition.

Prose swept in the same commit: three comments claimed the package was
swarm-wide or named the pre-rename path, and docs/web-ui/dashboard.md
named `swarm.matrix.gui.package` two lines above
`deploy.matrix.gui.enable` — the same asymmetry this commit exists to
remove, displayed in the docs. A shim keeps the old spelling valid, so
no build, lint or broken-reference search would have found that line.

Refs #3772.
This commit is contained in:
atlas 2026-09-07 18:35:34 +02:00
commit d6f23ff371
4 changed files with 61 additions and 46 deletions

View file

@ -625,7 +625,7 @@ existing in the page DOM.
A static matrix web client (default `pkgs.fluffychat-web` rebuilt A static matrix web client (default `pkgs.fluffychat-web` rebuilt
with `--base-href /matrix/`, swappable via with `--base-href /matrix/`, swappable via
`services.hyperhive.swarm.matrix.gui.package`) served by the hive-gateway `services.hyperhive.deploy.matrix.gui.package`) served by the hive-gateway
nginx container at `/matrix/` when nginx container at `/matrix/` when
`services.hyperhive.deploy.matrix.gui.enable` is on (defaults to `services.hyperhive.deploy.matrix.gui.enable` is on (defaults to
`matrix.enable`). c0re signals availability via the `matrix.enable`). c0re signals availability via the

View file

@ -335,6 +335,20 @@ in
[ "services" "hyperhive" "swarm" "wireguard" "persistentKeepalive" ] [ "services" "hyperhive" "swarm" "wireguard" "persistentKeepalive" ]
[ "services" "hyperhive" "deploy" "wireguard" "persistentKeepalive" ] [ "services" "hyperhive" "deploy" "wireguard" "persistentKeepalive" ]
) )
# Which build a service runs is a decision of the host that runs it, so
# every `*.package` follows its `enable` across. The split these replace
# was the sharpest case: `gui.enable` had already moved while
# `gui.package` stayed, so one client's two halves sat in two namespaces
# and ./hive-matrix.nix said so in a comment above the option.
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "matrix" "package" ]
[ "services" "hyperhive" "deploy" "matrix" "package" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "matrix" "gui" "package" ]
[ "services" "hyperhive" "deploy" "matrix" "gui" "package" ]
)
]; ];
# ⚠️ `deploy.forgejo` is declared in ./hive-ci.nix, not here, and it is the # ⚠️ `deploy.forgejo` is declared in ./hive-ci.nix, not here, and it is the

View file

@ -172,8 +172,9 @@ in
[ "services" "hyperhive" "swarm" "matrix" ] [ "services" "hyperhive" "swarm" "matrix" ]
) )
# "does THIS host run it" is a per-host decision, and `swarm.*` is the # "does THIS host run it" is a per-host decision, and `swarm.*` is the
# namespace every host in the swarm agrees on. The rest of the matrix # namespace every host in the swarm agrees on. What stays under
# settings genuinely are swarm-wide and stay where they are. # `swarm.matrix` is the homeserver's identity as every hive sees it —
# its server name, its addresses, its federation policy.
(lib.mkRenamedOptionModule (lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "matrix" "enable" ] [ "services" "hyperhive" "swarm" "matrix" "enable" ]
[ "services" "hyperhive" "deploy" "matrix" "enable" ] [ "services" "hyperhive" "deploy" "matrix" "enable" ]
@ -191,17 +192,6 @@ in
]; ];
options.services.hyperhive.swarm.matrix = { options.services.hyperhive.swarm.matrix = {
package = lib.mkOption {
type = lib.types.package;
default = pkgs.matrix-tuwunel;
defaultText = lib.literalExpression "pkgs.matrix-tuwunel";
description = ''
matrix-tuwunel package to run inside the container. Defaults
to nixpkgs's `pkgs.matrix-tuwunel`. Override to pin a
specific upstream if you need an unreleased feature.
'';
};
serverName = lib.mkOption { serverName = lib.mkOption {
type = lib.types.nullOr lib.types.str; type = lib.types.nullOr lib.types.str;
default = null; default = null;
@ -338,26 +328,6 @@ in
''; '';
}; };
gui = {
# Whether this host serves it is `deploy.matrix.gui.enable`.
package = lib.mkOption {
type = lib.types.package;
default = fluffychat-web-fixed;
defaultText = lib.literalMD ''
`pkgs.fluffychat-web` with a `postInstall` patch that adds
the three files `flutter341.buildFlutterApplication` skips.
'';
description = ''
Static web client dist served at `gatewayHost`'s vhost
(`chat.<swarm-domain>` by default). Override
to swap fluffychat for hydrogen-web, cinny, element-web, or
an out-of-tree dist any replacement is mounted at the
sub-domain root with the upstream-default `<base href "/">`,
no sub-path gymnastics needed.
'';
};
};
# This homeserver always delegates login to the swarm's authelia, as # This homeserver always delegates login to the swarm's authelia, as
# an OIDC relying party — matrix SSO (`m.login.sso`), offered # an OIDC relying party — matrix SSO (`m.login.sso`), offered
# alongside password login. No toggle: a homeserver in a swarm is a # alongside password login. No toggle: a homeserver in a swarm is a
@ -398,13 +368,24 @@ in
}; };
# What stays above is what the homeserver IS from any hive's point of view: # What stays above is what the homeserver IS from any hive's point of view:
# its package, the name it answers to, the ports and URLs it is reached on, # the name it answers to, the ports and URLs it is reached on, and the client
# and the client id it is registered under. What lives here is what the host # id it is registered under. What lives here is what the host running it
# running it decides — whether it is exposed, which peers it trusts, how large # decides — which build it runs, whether it is exposed, which peers it trusts,
# a request it accepts, and where its host-local secrets sit. Same rule as # how large a request it accepts, and where its host-local secrets sit. Same rule as
# ./swarm-victorialogs.nix; `enable` already lives in ./deploy.nix, which also # ./swarm-victorialogs.nix; `enable` already lives in ./deploy.nix, which also
# carries the renames. # carries the renames.
options.services.hyperhive.deploy.matrix = { options.services.hyperhive.deploy.matrix = {
package = lib.mkOption {
type = lib.types.package;
default = pkgs.matrix-tuwunel;
defaultText = lib.literalExpression "pkgs.matrix-tuwunel";
description = ''
matrix-tuwunel package to run inside the container. Defaults
to nixpkgs's `pkgs.matrix-tuwunel`. Override to pin a
specific upstream if you need an unreleased feature.
'';
};
openFirewall = lib.mkOption { openFirewall = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = false; default = false;
@ -509,11 +490,28 @@ in
off, the dashboard's `M4TR1X ` tab is hidden. See off, the dashboard's `M4TR1X ` tab is hidden. See
`docs/networking/gateway.md` for the discovery flow that lets clients `docs/networking/gateway.md` for the discovery flow that lets clients
auto-find the sub-domain. The client build itself is auto-find the sub-domain. The client build itself is
`swarm.matrix.gui.package` which client, as opposed to whether `deploy.matrix.gui.package` which client, as opposed to whether
this host serves it. this host serves it.
''; '';
}; };
gui.package = lib.mkOption {
type = lib.types.package;
default = fluffychat-web-fixed;
defaultText = lib.literalMD ''
`pkgs.fluffychat-web` with a `postInstall` patch that adds
the three files `flutter341.buildFlutterApplication` skips.
'';
description = ''
Static web client dist served at `gatewayHost`'s vhost
(`chat.<swarm-domain>` by default). Override
to swap fluffychat for hydrogen-web, cinny, element-web, or
an out-of-tree dist any replacement is mounted at the
sub-domain root with the upstream-default `<base href "/">`,
no sub-path gymnastics needed.
'';
};
sso.clientSecretFile = lib.mkOption { sso.clientSecretFile = lib.mkOption {
type = lib.types.nullOr lib.types.str; type = lib.types.nullOr lib.types.str;
default = null; default = null;
@ -634,7 +632,7 @@ in
# fluffychat at sub-domain root, SPA-fallback via the # fluffychat at sub-domain root, SPA-fallback via the
# Accept-header `$matrix_spa_target` map above. # Accept-header `$matrix_spa_target` map above.
"/" = { "/" = {
alias = "${cfg.gui.package}/"; alias = "${deployCfg.matrix.gui.package}/";
extraConfig = '' extraConfig = ''
try_files $uri $uri/ $matrix_spa_target =404; try_files $uri $uri/ $matrix_spa_target =404;
''; '';
@ -999,7 +997,7 @@ in
services.matrix-tuwunel = { services.matrix-tuwunel = {
enable = true; enable = true;
package = cfg.package; package = deployCfg.matrix.package;
settings.global = { settings.global = {
server_name = effectiveServerName; server_name = effectiveServerName;
# `address` + `port` are upstream `listOf` — wrap singles. # `address` + `port` are upstream `listOf` — wrap singles.
@ -1084,7 +1082,7 @@ in
"oidc_client_secret:${toString deployCfg.matrix.sso.clientSecretFile}" "oidc_client_secret:${toString deployCfg.matrix.sso.clientSecretFile}"
]; ];
environment.systemPackages = [ cfg.package ]; environment.systemPackages = [ deployCfg.matrix.package ];
}; };
}; };

View file

@ -89,11 +89,12 @@ let
]; ];
}; };
# The homeserver's turn to split. All six host-side options are set through # The homeserver's turn to split. All eight host-side options are set through
# their pre-rename paths — including `gui.enable`, whose value is deliberately # their pre-rename paths — including `gui.enable`, whose value is deliberately
# the opposite of its default so the definition has to actually land. All six # the opposite of its default so the definition has to actually land, and both
# so that dropping any single shim entry fails the eval, not just the two the # packages, stubbed to a derivation neither option defaults to for the same
# assertion reads. # reason. All eight so that dropping any single shim entry fails the eval, not
# just the ones the assertions read.
matrixOldPath = hive { matrixOldPath = hive {
deploy.matrix.enable = true; deploy.matrix.enable = true;
swarm.matrix.openFirewall = true; swarm.matrix.openFirewall = true;
@ -101,6 +102,8 @@ let
swarm.matrix.maxRequestSize = 31457280; swarm.matrix.maxRequestSize = 31457280;
swarm.matrix.registrationTokenFile = "/etc/matrix/register.token"; swarm.matrix.registrationTokenFile = "/etc/matrix/register.token";
swarm.matrix.gui.enable = false; swarm.matrix.gui.enable = false;
swarm.matrix.package = pkgs.emptyDirectory;
swarm.matrix.gui.package = pkgs.emptyDirectory;
swarm.matrix.sso.clientSecretFile = "/etc/matrix/oidc.secret"; swarm.matrix.sso.clientSecretFile = "/etc/matrix/oidc.secret";
}; };