# 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. ''; } ]; }; }