swarm-controller: serve swarm-wide service quick links (hyperhive#3289)

New `services.hyperhive.swarm.controller.links` option (listOf {label,
icon, url}, same shape as the per-agent hyperhive.dashboardLinks) plus
a new GET /api/links route serving it, same pattern as the existing
hives/GET /api/hives.

Rather than one central hardcoded list, each service's own module
contributes its own entry when actually enabled on the controller's
host: swarm-authelia.nix, hive-matrix.nix (gated on gui.enable too,
since / on that vhost only serves fluffychat then) and
hive-forge/default.nix (gated on behindGateway) each push one entry,
the same list-merge idiom services.hyperhive.gateway.localNames
already uses. swarm-ui.nix contributes a static entry for its own
same-origin swagger docs. Adding a future service's link is a nix-only
change to that service's own module.

Verified: cargo build/clippy/test -p swarm-controller clean, a
throwaway nixosSystem eval confirms all 4 entries merge correctly into
SWARM_CONTROLLER_LINKS, nix build .#swarm-controller succeeds.
This commit is contained in:
iris 2026-08-15 13:47:24 +02:00 committed by mara
commit ba3a9ed94f
6 changed files with 206 additions and 1 deletions

View file

@ -112,6 +112,53 @@ in
new directory, not just the daemon.
'';
};
links = lib.mkOption {
type = lib.types.listOf (
lib.types.submodule {
options = {
label = lib.mkOption {
type = lib.types.str;
description = "Display label for the link.";
};
icon = lib.mkOption {
type = lib.types.str;
default = "";
description = "Optional icon emoji or short glyph.";
};
url = lib.mkOption {
type = lib.types.str;
description = "Full URL.";
};
};
}
);
default = [ ];
example = lib.literalExpression ''
[ { label = "Wiki"; icon = "📖"; url = "https://wiki.example.com/"; } ]
'';
description = ''
Quick links to swarm-wide services, surfaced by the swarm UI's
links menu (`GET /api/links`). Same shape and same
zero-code-change-to-extend idea as `hyperhive.dashboardLinks`
(`nix/agent-modules/dashboard-links.nix`), one level up: rather
than one central hardcoded list, each service's own module
contributes its own entry when it is actually enabled on this
host `swarm-authelia.nix`, `hive-matrix.nix` and
`hive-forge/default.nix` all do the same list-merge idiom
`services.hyperhive.gateway.localNames` already uses. A future
service module can push its own entry the same way, and an
operator can add arbitrary extra entries here directly; neither
needs a swarm-controller or swarm-ui change.
Only meaningful on the host that actually runs the controller
entries contributed on any other host are computed but never
read. In a swarm that splits `swarm-authelia`/`hive-matrix`/
`hive-forge` across hosts other than the controller's, this list
only reflects what is enabled locally; see each contributing
module's own activation condition.
'';
};
};
config = lib.mkIf (config.services.hyperhive.enable && cfg.enable) {
@ -184,6 +231,10 @@ in
inherit (h) domain;
}) config.services.hyperhive.swarm.hives
);
# The merged links list — see `links`' description above for who
# contributes to it. Consumed by `GET /api/links`
# (swarm-controller/src/main.rs::load_links).
environment.SWARM_CONTROLLER_LINKS = builtins.toJSON cfg.links;
};
};
}