hyperhive/docs/swarm/ui.md
iris c20496604c fix Microsoft.Quotes vale findings - move punctuation inside quotes
Moved 27 instances of periods and commas from outside closing quote marks
to inside, following American-style punctuation placement. Changes span
across ~15 documentation files including integration, process, scheduler,
trust-boundary, and web-ui docs.
2026-09-07 16:28:06 +02:00

114 lines
5.3 KiB
Markdown

# Swarm UI
The swarm's own web surface, served by the gateway on the **swarm apex**
(`services.hyperhive.swarm.domain`) and readable only by operators.
Distinct from the per-hive dashboard, which lives on the hive domain and
answers for one host. This one is the view _across_ hives.
## Enabling
```nix
services.hyperhive.deploy.swarm-ui.enable = true; # defaults to deploy.swarm-controller.enable
```
Derived from the controller rather than from `allSwarmServices`:
the UI is a view onto the controller's state and reaches it over that
daemon's 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.
The UI answers on `services.hyperhive.swarm.domain` and nothing else. It
shares that name with the swarm-controller it fronts — one service to a
reader and to a certificate — so there is no separate option to pin.
> The apex must differ from `services.hyperhive.domain`. The gateway's
> default server already answers for the hive domain, and two vhosts
> claiming one `server_name` don't error — nginx picks one — so this is
> an assertion rather than a runtime surprise.
## 🔑 You must be in the `admins` group
**This is the step that separates "protected" from "locked out."** The
vhost's `auth_request` asks authelia _"is there a session"_; the rule
that makes it mean _"is this an operator"_ is an `access_control` entry
requiring `group:admins`. An account without that group authenticates
fine and still gets bounced.
```sh
swarmctl user add <you> --group admins
```
`admins` deliberately, not a new word: [`../getting-started/setup.md`](../getting-started/setup.md) has
told every operator to create exactly that group since the bootstrap step
existed, so an account made by following the guide already passes. This
is the first rule that _consumes_ a group name — inventing a second one
would have meant those accounts silently failing a check they were
supposed to pass.
An account created without any group needs re-adding with the flag —
`swarmctl` reads the existing entry out of `users.yml,` so the group is
what changes.
Why a group and not a list of usernames: agents are getting authelia
accounts of their own (matrix SSO), and _authenticated_ would then
include every agent in the hive. The group is the only thing standing
between "an operator's page" and "anyone with a session."
## What it costs to be reachable
The apex is published to the hive's resolver like every other swarm
service, so agent containers can resolve it. That's deliberate and it's
not a hole: **reachability isn't the access control here.** An agent
that resolves the name and connects still has no operator session, and
the subrequest denies it.
## Two wiring sites
Adding a swarm service name means touching two things. Missing the
second ships as a different flavour of "works from the host, broken from
a container":
| site | file |
| ---------------------------- | ---------------------------------------------------------------------- |
| vhost + `gateway.localNames` | the service's own module (for example `nix/host-modules/swarm-ui.nix`) |
| **certificate name** | `nix/host-modules/swarm.nix` (`serviceDomains`) |
The DNS record and the local-dev `/etc/hosts` entry need no separate
edit: both are derived from `services.hyperhive.gateway.localNames`,
which a service's own module already has to push its domain into to be
resolvable — see `nix/host-modules/hive-gateway/dnsmasq.nix` and
`.../default.nix`'s `networking.hosts`. `vhosts.nix` itself is scoped to
the surface the hive's own domain serves (dashboard, per-agent routing,
matrix discovery); a swarm service declares its own vhost next to its
own options, the way `swarm-ui.nix` and `swarm-authelia.nix` do.
⚠️ The certificate one is the hardest to predict and the most visible when
missed. `serviceDomains` is _both_ the services sub-CA's
`nameConstraints` set and the leaf's SAN list, and the apex is a
**sibling** of `forge.<swarm>` / `chat.<swarm>` / `auth.<swarm>`, not a
parent — no CA in the hierarchy issues for it implicitly. Left out, the
vhost falls back to the hive leaf and the swarm's front page opens with a
name mismatch.
## Quick links
The swarm UI's header carries a single 🔗 button, visible on every route,
opening a popover of links to other swarm-wide services — authelia,
matrix, forge, this UI's own swagger docs. Backed by `GET /api/links`
(swarm-controller), which serves `services.hyperhive.swarm.controller.links`
(a `listOf { label, icon, url }`, same shape as the per-agent
`hyperhive.dashboardLinks`).
Rather than one central hardcoded list, each service's own module
contributes its own entry when it's actually enabled on the controller's
host — `swarm-authelia.nix`, `hive-matrix.nix` and `hive-forge/default.nix`
all do, the same list-merge idiom `gateway.localNames` uses above. Adding a
link for a new service is a nix-only change to that service's own module
(or an operator adding an entry directly); no swarm-controller or swarm-ui
change needed. Empty list hides the button rather than showing an empty
popover.
## Cross-references
- [`sso.md`](sso.md) — the authelia instance itself, and the user store.
- [`../networking/gateway.md`](../networking/gateway.md) — the full vhost map and TLS modes.