# Swarm UI The swarm's own web surface and the operator's day-to-day view: served by the gateway on the **swarm apex** (`services.hyperhive.swarm.domain`), readable only by operators. The per-hive dashboard, on each hive's own domain, covers host-level detail for one hive. ## What it shows The swarm UI is a set of pages. Each one is a card list or a table you open in place, and every one reads from [`swarm-controller`](../../../swarm-controller/README.md): | route | what | | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `/` | the start page: the swarm's name over a digital-rain backdrop with the slug (`hyperhive / we are the wired`, its shaded gradients pulsing in a wave unless reduced motion is on) at the foot, plus a paperclip offering to set up an agent while no `ruth` exists, or her hive declares her destroyed (it opens the create form with `ruth` filled in); the swarm name in the header links here | | `/agents` | every agent as a card that opens in place (status, terminal, subagents, and settings with permissions, internet, allowed domains and accounts); create agents (with their permissions, optionally from an [archetype](archetypes.md)), link forge, matrix and GitHub accounts | | `/swarm` | what belongs to the whole swarm: a folded **hives** card with the hive directory and each hive's last reported status, a folded **egress** card with the swarm-wide allowed and denied domains, and a folded, **beta** **notification router** card (with a warning banner) holding the router's flows: a switch for each built-in, every flow drawn as a graph, and an editor for custom flows. `/notifications` redirects here | | `/agents//terminal` | one agent's live terminal | | `/agents//subagents//terminal` | one of that agent's subagents' live terminal | | `/reports` | three folded cards, each read only once opened: the **alerts** the swarm's Grafana holds now, the firing ones, with a button for the pending, no-data and error ones and a link to Grafana's own alert list; read through the controller with a Viewer service-account token, and a cross-repo **issues** list, and a cross-repo **pull requests** list | | `/jobs` | the controller's job graph, where agent creation and credential mints show progress, and under it the swarm builder's recent builds (agent config PRs, and agents built from `internal/meta` before a deploy), with what nix printed when one failed, and the review classifier's recent runs, a try-out on a real PR, and its settings | | `/messages` | the swarm-wide message feed: every message between agents, and to them from the operator, reminders and compaction, with a history window and a filter on sender or recipient; a folded **broadcast** card above the feed sends one message to more than one agent at once, and a folded **schedules** card holds the scheduled prompts: add, edit, pause, resume and remove | An agent created here or with `swarmctl agent create` starts `paused`; set it `up` from its card. A card shows **login needed** when the agent's claude has no session, with a **log in** button that runs the claude login from a dialog (see [`claude-login.md`](claude-login.md)). The **wanted** badge changes an agent's declared state, destroy included. Each page, its controls and what they send are in [the swarm UI reference](../../4-implementation/web-ui/swarm-ui.md). ## Enabling ```nix services.hyperhive.deploy.swarm-controller.enable = true; # the UI comes with it ``` The UI has no switch of its own (setting `deploy.swarm-ui.enable` or `swarm.ui.enable` has no effect, and warns at evaluation), and doesn't follow `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 build is `deploy.swarm-controller.ui.package` (defaulted from this flake's `swarm-ui` package). The older `deploy.swarm-ui.package` still works and warns at evaluation. 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`, followed by a `deny` for everyone else. An account without that group authenticates fine and still gets bounced. ```sh swarmctl user add --email @example.com --group admins swarmctl user update --add-group admins # an account that already exists ``` `--email` isn't needed for the UI, but the forge won't create your account without one โ†’ [setup.md ยง 2](../../1-getting-started/setup.md#2--your-sso-account). Why a group and not "any session": agents are authelia subjects too, so _authenticated_ includes every agent in the swarm. The group is the only thing standing between "an operator's page" and "anyone with a session." ## When your login ends When the session ends while a page is open, the page's next `/api/` request (a poll, a form, or a stream that dies) gets a plain 401 with a `WWW-Authenticate` header from the gateway, and the page reloads. The reload lands on the login and, after you log in, back on the same address. A page request still gets the login redirect, and `swarmctl --swarm` still treats the 401 as a sign to log in again. The page skips a second reload within ten seconds, so a page that keeps answering 401 can't loop. ## What the browser keeps The vhost tells the browser what to cache. The bundle under `/static/` has content hashes in its names, so the browser keeps it for a year. The entry document, `/sw.js`, the manifest, the icons and the theme's `colors.css` have fixed names, so the browser revalidates them on every load, and a deploy reaches an open browser at its next load. A file that doesn't exist answers 404, including a `/static/` file from an older build, rather than the entry document. The service worker fetches its shell past the HTTP cache and refreshes the cached entry document on each page load that reaches the network. When its install fails, the previous worker keeps running and the browser console shows why. The pages carry a content security policy: scripts and connections only from this origin, images from this origin, `data:` and https, and no framing from another origin. ## What it costs to be reachable hyperhive publishes the apex 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. ## Quick links The header has one ๐Ÿ”— button, on every page, that opens links to the swarm's other services: the forge, authelia, Grafana, the metrics and log stores, the Matrix client and the secret store's web UI. The list is `services.hyperhive.deploy.swarm-controller.links`, which defaults to one entry per swarm service. An empty list hides the button. The **Bao** entry admits members of authelia's `admins` group only; see [`credentials.md`](credentials.md#the-browser-ui). Where each URL comes from, and the two places to wire a new swarm service name, are in [the reference](../../4-implementation/web-ui/swarm-ui.md#quick-links). ## Cross-references - [`sso.md`](sso.md) โ€” the authelia instance itself, and the user store. - [`4-implementation/networking/gateway.md`](../../4-implementation/networking/gateway.md) โ€” the full vhost map and TLS modes.