hyperhive/nix/host-modules/swarm-ui.nix
atlas 40e1ed2967 fix(3167): wire the swarm-ui package from the flake, not a pkgs attr
The option defaulted to pkgs.swarm-ui, which does not exist: this
project has no overlay - flake.nix's nixosModules.default wires package
options with mkDefault from its own package set, and swarm.controller
does exactly that. The default would have failed to evaluate on any real
deployment, not just in a test harness.

Found by the gate forcing .package: the earlier probes passed because
they only read option values that never touched it.
2026-08-12 17:49:21 +02:00

97 lines
3.8 KiB
Nix

# The swarm-level web UI: a static bundle served by the gateway's nginx,
# behind authelia. Distinct from the per-hive dashboard (hive-c0re's, on
# the hive domain) — this one answers for the swarm apex and is the
# operator's view across hives.
#
# Options only. The vhost itself is declared in hive-gateway/vhosts.nix
# alongside forge/matrix/authelia: a service module says *how* it is
# reached, the gateway says *whether this host serves it*.
{
lib,
config,
...
}:
let
cfg = config.services.hyperhive.swarm.ui;
swarmCfg = config.services.hyperhive.swarm;
hiveDomain = config.services.hyperhive.domain;
in
{
options.services.hyperhive.swarm.ui = {
enable = lib.mkOption {
type = lib.types.bool;
default = swarmCfg.controller.enable;
defaultText = lib.literalExpression "services.hyperhive.swarm.controller.enable";
example = true;
description = ''
Serve the swarm UI from this host.
Derived from `swarm.controller.enable` rather than from
`enableRequiredServices`: the UI is a view onto the controller's
state and reaches it over that daemon's unix socket, so the host
that runs the controller is the host that can serve the UI. A
hive that merely *uses* a swarm has nothing to serve here.
'';
};
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";
description = ''
Static build of the swarm UI. nginx serves this store path
directly there is no server-side component beyond the
controller's own API.
Wired by default from this flake's own package set (see
`flake.nix`), the same way `swarm.controller.package` is. There
is deliberately **no overlay** in this project, so a
`pkgs.swarm-ui` default here would name an attribute that does
not exist on any real deployment.
'';
};
};
config = lib.mkIf (config.services.hyperhive.enable && cfg.enable) {
assertions = [
{
# The `_` default server already answers for the hive domain
# (dashboard, per-agent routes). A second vhost claiming the same
# server_name is not an error to nginx — it picks one and logs a
# 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;
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.
Set services.hyperhive.swarm.domain to a name distinct from
this hive's, or pin swarm.ui.domain explicitly.
'';
}
];
};
}