Six places asserted the old design as fact, and none of them mention the
change by name -- the class of doc breakage that is found by asking what
a diff made untrue, not by grepping for a feature:
- swarmctl/README.md and swarm-authelia-bridge/README.md both described
their own private canonical store. The bridge's "known limitation"
section described the seam as unsolved; it is what this fixes, so it
becomes what both writers must uphold instead.
- docs/swarm/{sso,ui,secrets}.md described a rendered artifact.
- The repo CLAUDE.md entry for swarmctl said the same.
- docs/tools/swarmctl-cli.md is regenerated (CI diffs it against the
clap tree), picking up the removed --store flag.
Operator-facing where it is read: the hand-editing consequence (values
survive a rewrite, comments do not) is stated in sso.md, where an
operator is being told to edit the file, rather than only in a module doc.
113 lines
5 KiB
Markdown
113 lines
5 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.swarm.ui.enable = true; # defaults to swarm.controller.enable
|
|
```
|
|
|
|
Derived from the controller rather than from `enableRequiredServices`:
|
|
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.
|
|
|
|
`swarm.ui.domain` defaults to the swarm apex and can be pinned, the same
|
|
way `swarm.forge.domain` and `swarm.matrix.gatewayHost` can.
|
|
|
|
> 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` do not 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: [`../setup.md`](../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 is deliberate and it is
|
|
not a hole: **reachability is not 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 (e.g. `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 least obvious 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.
|
|
- [`../gateway.md`](../gateway.md) — the full vhost map and TLS modes.
|