Watch
0
0
Fork
You've already forked hyperhive
0

swarm-ui: build the forge link from swarm.forge.domain

The swarm-controller builds the Forge quick link from
services.hyperhive.swarm.forge.domain, replacing hive-forge/default.nix's
per-host entry, so all seven swarm-service links come from swarm-level
options.

Also drops the remaining references to the removed matrix GUI switch:
the HiveUrls / Urls / hive_urls docs, the hivectl.md `open` note and
the gateway.md vhost-map rows, which name `gatewayHost` instead. The
grafana, victoriametrics and victorialogs modules' comments no longer
mention a quick-link they do not define.

Refs #4885
This commit is contained in:
atlas 2026-10-02 19:39:32 +02:00
commit 72e9e1bb4a
11 changed files with 25 additions and 31 deletions

View file

@ -16,8 +16,8 @@ You rarely switch it on yourself. `gateway.enable` defaults to off, and every mo
| `auth.<swarm>/` | authelia (`9091`) | `swarm-authelia.nix`, `deploy.authelia.enable` | | `auth.<swarm>/` | authelia (`9091`) | `swarm-authelia.nix`, `deploy.authelia.enable` |
| `forge.<swarm>/` | forgejo (`3000`) | `hive-forge/`, `deploy.forgejo.enable` | | `forge.<swarm>/` | forgejo (`3000`) | `hive-forge/`, `deploy.forgejo.enable` |
| `chat.<swarm>/_matrix/*` | tuwunel (`8008`) | `hive-matrix.nix`, `swarm.matrix.gatewayHost != null` | | `chat.<swarm>/_matrix/*` | tuwunel (`8008`) | `hive-matrix.nix`, `swarm.matrix.gatewayHost != null` |
| `chat.<swarm>/` | fluffychat-web static (404 with the GUI off) | `hive-matrix.nix`, `deploy.matrix.gui.enable` | | `chat.<swarm>/` | fluffychat-web static | `hive-matrix.nix`, `swarm.matrix.gatewayHost != null` |
| `chat.<swarm>/config.json` | inline JSON (FluffyChat boot config) | `hive-matrix.nix`, `deploy.matrix.gui.enable` | | `chat.<swarm>/config.json` | inline JSON (FluffyChat boot config) | `hive-matrix.nix`, `swarm.matrix.gatewayHost != null` |
| `grafana.<swarm>`, `metrics.<swarm>`, `logs.<swarm>`, `otel.<swarm>`, `bao.<swarm>` | the matching swarm service | that service's module → [`swarm/services.md`](../swarm/services.md) | | `grafana.<swarm>`, `metrics.<swarm>`, `logs.<swarm>`, `otel.<swarm>`, `bao.<swarm>` | the matching swarm service | that service's module → [`swarm/services.md`](../swarm/services.md) |
**The hive's own vhost**, named for the hive domain: **The hive's own vhost**, named for the hive domain:
@ -29,7 +29,7 @@ You rarely switch it on yourself. `gateway.enable` defaults to off, and every mo
| `<hive>/api/docs/` | themed Swagger UI dist (static) | always | | `<hive>/api/docs/` | themed Swagger UI dist (static) | always |
| `<hive>/agent/<name>/` | per-agent harness over its unix socket | `agents.conf` (runtime-generated) | | `<hive>/agent/<name>/` | per-agent harness over its unix socket | `agents.conf` (runtime-generated) |
| `<hive>/.well-known/matrix/{client,server}` | inline JSON | `deploy.matrix.enable` | | `<hive>/.well-known/matrix/{client,server}` | inline JSON | `deploy.matrix.enable` |
| `<hive>/matrix/` | 301 → `chat.<swarm>/` | `deploy.matrix.gui.enable` and `gatewayHost` set | | `<hive>/matrix/` | 301 → `chat.<swarm>/` | `deploy.matrix.enable` and `gatewayHost` set |
The catch-all `_` vhost answers any other `Host` with `444` (connection closed, no response). It's `mkDefault`, so to make your own vhost the default server, set `services.nginx.virtualHosts."_".default = false;` — an eval assertion names both when two claim it. The catch-all `_` vhost answers any other `Host` with `444` (connection closed, no response). It's `mkDefault`, so to make your own vhost the default server, set `services.nginx.virtualHosts."_".default = false;` — an eval assertion names both when two claim it.

View file

@ -109,6 +109,7 @@ same whichever host runs each service:
| link | URL built from | | link | URL built from |
| -------- | ------------------------------------------------- | | -------- | ------------------------------------------------- |
| Forge | `services.hyperhive.swarm.forge.domain` |
| Authelia | `services.hyperhive.swarm.authelia.domain` | | Authelia | `services.hyperhive.swarm.authelia.domain` |
| Grafana | `services.hyperhive.swarm.grafana.domain` | | Grafana | `services.hyperhive.swarm.grafana.domain` |
| Metrics | `services.hyperhive.swarm.victoriametrics.domain` | | Metrics | `services.hyperhive.swarm.victoriametrics.domain` |
@ -116,11 +117,9 @@ same whichever host runs each service:
| Matrix | `services.hyperhive.swarm.matrix.gatewayHost` | | Matrix | `services.hyperhive.swarm.matrix.gatewayHost` |
| Bao | `services.hyperhive.swarm.bao.ui.domain` | | Bao | `services.hyperhive.swarm.bao.ui.domain` |
`nix/host-modules/swarm-controller.nix` builds these entries. The Forge `nix/host-modules/swarm-controller.nix` builds these entries. `swarm-ui.nix`
entry, and `swarm-ui.nix`'s entry for this UI's own API docs, come from adds one more, for this UI's own API docs. An operator can add entries
those services' own modules, and only when the controller's host runs that directly. An empty list hides the button.
service. An operator can add entries directly. An empty list hides the
button.
The **Matrix** entry opens the swarm's matrix web client (fluffychat, The **Matrix** entry opens the swarm's matrix web client (fluffychat,
`services.hyperhive.deploy.matrix.gui.package`) at the homeserver's `services.hyperhive.deploy.matrix.gui.package`) at the homeserver's

View file

@ -273,5 +273,5 @@ note, not an error.
A surface has no URL when it isn't browser-reachable: `home` needs A surface has no URL when it isn't browser-reachable: `home` needs
`services.hyperhive.domain`; `forge` needs `services.hyperhive.domain`; `forge` needs
`services.hyperhive.swarm.forge.publicUrl` (set by default); `matrix` needs `services.hyperhive.swarm.forge.publicUrl` (set by default); `matrix` needs
`services.hyperhive.deploy.matrix.gui.enable = true`. In those cases the command `services.hyperhive.swarm.matrix.gatewayHost` (set by default). In those cases the command
exits with a hint naming the option to set. exits with a hint naming the option to set.

View file

@ -853,7 +853,7 @@ fn is_broad_scope(scope: &LifecycleScope) -> bool {
/// Assemble this hive's domain + browser-facing web URLs from c0re's /// Assemble this hive's domain + browser-facing web URLs from c0re's
/// service env (injected by the hyperhive NixOS module). Each field is `None` when its /// service env (injected by the hyperhive NixOS module). Each field is `None` when its
/// surface isn't browser-reachable (domain unset, forge `publicUrl` /// surface isn't browser-reachable (domain unset, forge `publicUrl`
/// null, matrix GUI off), so the CLI can hint precisely instead of /// null, matrix `gatewayHost` null), so the CLI can hint precisely instead of
/// opening a dead link. Scheme matches the existing `HIVE_FORGE_PUBLIC_URL` /// opening a dead link. Scheme matches the existing `HIVE_FORGE_PUBLIC_URL`
/// convention (gateway terminates TLS, so https). /// convention (gateway terminates TLS, so https).
fn hive_urls() -> hive_host_sock::HiveUrls { fn hive_urls() -> hive_host_sock::HiveUrls {

View file

@ -213,7 +213,7 @@ pub enum HostRequest {
/// forge / matrix URLs, daemon-sourced so custom forge/matrix /// forge / matrix URLs, daemon-sourced so custom forge/matrix
/// domains resolve correctly. Each URL is `None` when its subsystem /// domains resolve correctly. Each URL is `None` when its subsystem
/// is unreachable from a browser (e.g. forge `publicUrl` null, /// is unreachable from a browser (e.g. forge `publicUrl` null,
/// matrix GUI disabled). Backs `hivectl open` + the federation /// matrix `gatewayHost` null). Backs `hivectl open` + the federation
/// peer-config block (which reads the bare `domain`). /// peer-config block (which reads the bare `domain`).
Urls, Urls,
/// Fetch one or more job-queue nodes plus their live subtrees, as /// Fetch one or more job-queue nodes plus their live subtrees, as
@ -451,7 +451,7 @@ impl LifecycleScope {
/// This hive's canonical domain plus the browser-facing URLs for its /// This hive's canonical domain plus the browser-facing URLs for its
/// web surfaces — the `Urls` request result. Every field is `None` when /// web surfaces — the `Urls` request result. Every field is `None` when
/// the corresponding surface can't be reached from a browser (domain /// the corresponding surface can't be reached from a browser (domain
/// unset, forge `publicUrl` null, matrix GUI disabled), so the CLI /// unset, forge `publicUrl` null, matrix `gatewayHost` null), so the CLI
/// can give a precise hint instead of opening a dead link. /// can give a precise hint instead of opening a dead link.
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct HiveUrls { pub struct HiveUrls {
@ -465,8 +465,8 @@ pub struct HiveUrls {
/// `swarm.forge.publicUrl` is `null`. /// `swarm.forge.publicUrl` is `null`.
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
pub forge: Option<String>, pub forge: Option<String>,
/// Matrix GUI (fluffychat) browser URL — `None` when the matrix GUI /// Matrix GUI (fluffychat) browser URL — `None` when
/// is disabled. /// `swarm.matrix.gatewayHost` is `null`.
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
pub matrix: Option<String>, pub matrix: Option<String>,
} }

View file

@ -252,16 +252,6 @@ in
# The forge container resolves the rest of the hive through dnsmasq. # The forge container resolves the rest of the hive through dnsmasq.
services.hyperhive.gateway.dns.enable = lib.mkDefault true; services.hyperhive.gateway.dns.enable = lib.mkDefault true;
# This swarm-ui quick-links entry. See
# `services.hyperhive.swarm.controller.links`'s description.
services.hyperhive.swarm.controller.links = [
{
label = "Forge";
icon = "⚒";
url = "https://${cfg.domain}/";
}
];
# The metrics endpoint, declared once: the collector both scrapes this # The metrics endpoint, declared once: the collector both scrapes this
# URL and derives from it the audience its token is minted for. # URL and derives from it the audience its token is minted for.
# #

View file

@ -31,6 +31,11 @@ let
url = "https://${s.domain}/"; url = "https://${s.domain}/";
}) })
[ [
{
label = "Forge";
icon = "⚒";
inherit (swarmCfg.forge) domain;
}
{ {
label = "Authelia"; label = "Authelia";
icon = "🔑"; icon = "🔑";
@ -462,13 +467,13 @@ in
add arbitrary extra entries here directly with no swarm-controller add arbitrary extra entries here directly with no swarm-controller
or swarm-ui change. or swarm-ui change.
The Authelia, Grafana, Metrics and Logs entries come from The Forge, Authelia, Grafana, Metrics and Logs entries come from
`services.hyperhive.swarm.<service>.domain`, the Bao entry from `services.hyperhive.swarm.<service>.domain`, the Bao entry from
`services.hyperhive.swarm.bao.ui.domain`, and the Matrix entry `services.hyperhive.swarm.bao.ui.domain`, and the Matrix entry
from `services.hyperhive.swarm.matrix.gatewayHost` when it is set, from `services.hyperhive.swarm.matrix.gatewayHost` when it is set,
so they are present whichever host runs each service. so they are present whichever host runs each service.
`hive-forge/default.nix` and `swarm-ui.nix` contribute their own `swarm-ui.nix` contributes its own API-docs entry wherever the
entries, and only where they are enabled on this host. swarm UI is enabled on this host.
Read only on the host that runs the controller. Read only on the host that runs the controller.
''; '';

View file

@ -388,7 +388,7 @@ in
}; };
config = lib.mkIf deployCfg.grafana.enable { config = lib.mkIf deployCfg.grafana.enable {
# The gateway name and the quick-link, both inside `deploy.grafana` — that # The gateway name, inside `deploy.grafana` — that
# guard is the load-bearing part. Every hive in a swarm may know this UI # guard is the load-bearing part. Every hive in a swarm may know this UI
# exists, but only the host that RUNS it may claim the name; a client # exists, but only the host that RUNS it may claim the name; a client
# hive declaring the vhost would answer for a service it does not have. # hive declaring the vhost would answer for a service it does not have.

View file

@ -101,7 +101,7 @@ in
# hosts are separate evaluations. # hosts are separate evaluations.
services.hyperhive.swarm.otel.scrapeTargets.victorialogs = "127.0.0.1:${toString cfg.port}"; services.hyperhive.swarm.otel.scrapeTargets.victorialogs = "127.0.0.1:${toString cfg.port}";
# The gateway name and the quick-link, both inside `deployCfg.victorialogs.enable` — same # The gateway name, inside `deployCfg.victorialogs.enable` — same
# "only the host that runs the service may claim the name" guard every # "only the host that runs the service may claim the name" guard every
# sibling swarm-service module uses (`swarm-grafana.nix`, # sibling swarm-service module uses (`swarm-grafana.nix`,
# `swarm-victoriametrics.nix`). # `swarm-victoriametrics.nix`).

View file

@ -59,7 +59,7 @@ in
}; };
config = lib.mkIf deployCfg.victoriametrics.enable { config = lib.mkIf deployCfg.victoriametrics.enable {
# The gateway name and the quick-link, both inside `deployCfg.victoriametrics.enable` — that # The gateway name, inside `deployCfg.victoriametrics.enable` — that
# guard is the load-bearing part. Every hive in a swarm may know this # guard is the load-bearing part. Every hive in a swarm may know this
# store exists, but only the host that RUNS it may claim the name; a # store exists, but only the host that RUNS it may claim the name; a
# client hive declaring the vhost would answer for a service it does not # client hive declaring the vhost would answer for a service it does not

View file

@ -117,7 +117,7 @@ let
Logs = s.victorialogs.domain; Logs = s.victorialogs.domain;
Matrix = s.matrix.gatewayHost; Matrix = s.matrix.gatewayHost;
Bao = s.bao.ui.domain; Bao = s.bao.ui.domain;
# forge: added once hive-forge/default.nix drops its per-host link Forge = s.forge.domain;
}; };
swarmServiceLinksOf = swarmServiceLinksOf =
cfg: cfg: