The page-by-page and control-by-control description of the swarm UI moves to 4-implementation/web-ui/swarm-ui.md, with the quick-links wiring. The handbook page keeps the route table, how to reach the UI, who may and what the browser keeps. Links to the moved sections point at the new page.
10 KiB
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:
| 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), 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/<name>/terminal |
one agent's live terminal |
/agents/<name>/subagents/<subagent>/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). 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.
Enabling
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 oneserver_namedon'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.
swarmctl user add <you> --email <you>@example.com --group admins
swarmctl user update <you> --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.
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. Where each URL comes from, and
the two places to wire a new swarm service name, are in
the reference.
Cross-references
sso.md— the authelia instance itself, and the user store.4-implementation/networking/gateway.md— the full vhost map and TLS modes.