swarm: remove swarm.ui.domain, serve the UI on the swarm domain
The UI and the swarm-controller it fronts are one service to a reader and to a
certificate, so a separate option only ever created two spellings of one
address — and the controller's own public URL was built from one of them while
an allow-list entry named the other.
Removed rather than aliased. A config still setting it was pinning a name the
controller never used; an alias would preserve exactly that mismatch, so
`mkRemovedOptionModule` says so and fails the build.
⚠️ The option was TOTAL and `swarm.domain` is NULLABLE. That is not incidental:
`ui.domain` fell back to `swarm.invalid` so hive-network.nix's required-domain
assertion is what an operator sees, rather than a coercion error from a
consumer. Each site keeps that property its own way — swarm-ui binds a local
total (the idiom swarm-otel.nix already uses as `domainBase`), and the three
sites already gated on `deploy.swarm-ui.enable` gain `&& domain != null`, which
is stricter than a placeholder and puts no invented name into rendered config.
One behaviour change worth naming: `swarm.nix`'s certificate SAN list used to
receive the placeholder `swarm.invalid` on a null domain and now receives
nothing. `lib.optional` would otherwise have put a literal `null` in it.
Verified with `checks.module-eval` rather than `nix-instantiate --parse`: parse
proves syntax, and every failure this change could plausibly cause — a dead
binding, a renamed reference — is an evaluation error.
This commit is contained in:
parent
3f98bfb09d
commit
5aef2d1afc
7 changed files with 43 additions and 44 deletions
|
|
@ -55,33 +55,32 @@ let
|
|||
'';
|
||||
swarmCfg = config.services.hyperhive.swarm;
|
||||
hiveDomain = config.services.hyperhive.domain;
|
||||
|
||||
# The swarm apex this UI answers on. Total on a null domain (`.invalid`,
|
||||
# RFC 2606) so `hive-network.nix`'s required-domain assertion is what fires,
|
||||
# rather than a coercion error from here — same idiom as `swarm-otel.nix`'s
|
||||
# `domainBase`.
|
||||
apex = if swarmCfg.domain == null then "swarm.invalid" else swarmCfg.domain;
|
||||
in
|
||||
{
|
||||
# `enable` moved to `services.hyperhive.deploy.swarm-ui.enable` — see
|
||||
# ./deploy.nix, where it still derives from the controller's own deploy
|
||||
# toggle for the same reason. What stays here is what the UI IS: its
|
||||
# domain and wiring.
|
||||
# toggle for the same reason. `domain` is gone too: the UI answers on the
|
||||
# swarm apex, which the swarm already declares once.
|
||||
imports = [
|
||||
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "ui" "domain" ] ''
|
||||
The swarm UI answers on services.hyperhive.swarm.domain and nothing
|
||||
else. It shares that name with the swarm-controller it fronts — they
|
||||
are one service to a reader and to a certificate — so a second option
|
||||
only ever created two spellings of one address.
|
||||
|
||||
Removed rather than aliased: a config still setting it was pinning a
|
||||
name the controller's own public URL never used, and silently keeping
|
||||
the alias would preserve exactly that mismatch.
|
||||
'')
|
||||
];
|
||||
|
||||
options.services.hyperhive.swarm.ui = {
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = if swarmCfg.domain == null then "swarm.invalid" else swarmCfg.domain;
|
||||
defaultText = lib.literalExpression "services.hyperhive.swarm.domain";
|
||||
example = "swarm.example.com";
|
||||
description = ''
|
||||
Host name the swarm UI answers on. Defaults to the swarm apex
|
||||
itself — the swarm's front page is the swarm's name.
|
||||
|
||||
An option rather than a hardcoded derivation so a hive can pin a
|
||||
different name, the same way `swarm.forge.domain` and
|
||||
`swarm.matrix.gatewayHost` can.
|
||||
|
||||
Total on a null swarm domain (`.invalid`, RFC 2606) so the
|
||||
required-domain assertion is what fires rather than a coercion
|
||||
error naming this option — same reasoning as
|
||||
`hive-network.nix`'s.
|
||||
'';
|
||||
};
|
||||
|
||||
package = lib.mkOption {
|
||||
type = lib.types.package;
|
||||
defaultText = lib.literalExpression "hyperhive.packages.\${system}.swarm-ui";
|
||||
|
|
@ -108,16 +107,16 @@ in
|
|||
# conflict — so the failure would surface as "the dashboard is
|
||||
# sometimes the swarm UI", which is far harder to read than an
|
||||
# eval failure naming both options.
|
||||
assertion = cfg.domain != hiveDomain;
|
||||
assertion = apex != hiveDomain;
|
||||
message = ''
|
||||
services.hyperhive.swarm.ui.domain (${cfg.domain}) must differ
|
||||
from services.hyperhive.domain (${hiveDomain}) — the hive
|
||||
domain is already served by the gateway's default vhost
|
||||
(dashboard + agent routes), and two vhosts claiming one
|
||||
server_name silently resolve to whichever nginx picks.
|
||||
services.hyperhive.swarm.domain (${apex}) must differ from
|
||||
services.hyperhive.domain (${hiveDomain}) — the hive domain is
|
||||
already served by the gateway's default vhost (dashboard +
|
||||
agent routes), and two vhosts claiming one server_name
|
||||
silently resolve to whichever nginx picks.
|
||||
|
||||
Set services.hyperhive.swarm.domain to a name distinct from
|
||||
this hive's, or pin swarm.ui.domain explicitly.
|
||||
this hive's.
|
||||
'';
|
||||
}
|
||||
];
|
||||
|
|
@ -132,7 +131,7 @@ in
|
|||
# child of anything the resolver already answers for, so the
|
||||
# `/<hive domain>/` rule does not cover it and this record is what
|
||||
# makes the name resolve at all.
|
||||
services.hyperhive.gateway.localNames = [ cfg.domain ];
|
||||
services.hyperhive.gateway.localNames = [ apex ];
|
||||
|
||||
# This UI's own swagger docs, always same-origin (`/api/docs/` below)
|
||||
# so — unlike authelia/matrix/forge's entries — this one needs no
|
||||
|
|
@ -172,8 +171,8 @@ in
|
|||
# than broken, so they keep `addSSL` and the asymmetry stays local
|
||||
# to the vhost whose correctness depends on the scheme.
|
||||
# `removeAttrs` because nixos asserts on a vhost declaring both.
|
||||
services.nginx.virtualHosts."${cfg.domain}" =
|
||||
(builtins.removeAttrs (gatewayCfg.lib.tlsFor cfg.domain) [ "addSSL" ])
|
||||
services.nginx.virtualHosts."${apex}" =
|
||||
(builtins.removeAttrs (gatewayCfg.lib.tlsFor apex) [ "addSSL" ])
|
||||
// {
|
||||
forceSSL = true;
|
||||
listen = gatewayCfg.lib.listen;
|
||||
|
|
|
|||
Loading…
Reference in a new issue