Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/docs/swarm/ui.md
atlas 8e23feb01b github: PATs live in swarm bao; the agent fetches them itself
An operator links an agent's GitHub personal access token in the swarm UI
(LinkGithubAccountForm, "link github account" on /agents). swarm-controller's
PUT /api/hives/{hive}/agents/{agent}/github-account stores it at
swarm/agents/<agent>/github-token (swarm_secret_client::github), a flat leaf
under the agent's prefix that the agent's existing read grant already covers:
no policy change, and no list grant, since there is one token per agent.

In the agent, hive-agent-github-token (oneshot + 2-minute timer, as the agent
user, under its own store certificate, ordered before hive-github-notify)
reads that path and writes <state>/github-token, 0600 and agent-owned, the
file the gh wrapper, git credential helper and hive-github-notify already
read. It replaces the file by rename only when the bytes changed and never
deletes it: a hive-written github-token stays until a token is linked in the
swarm UI. It is installed only with a store address and
services.hyperhive.agent.github.enable.

Removed: the dashboard's CR3D3NTIALS page (credentials.html/js/css, its
build entries and H0M3 tile; GITHUB was its only tab), hive-c0re's
dashboard/matrix_accounts.rs with GET/POST /api/github-account,
priv_client::write_agent_github_token, the host socket's
SetAgentGithubToken and `hivectl github set-token`, and hive-priv's
WriteAgentGithubToken with write_agent_state_file, its only caller gone.

Docs: integrations/github.md and swarm/ui.md describe the swarm path,
swarm/credentials.md gains the store-path row, and the hive UI docs,
hivectl docs and security.md's hive-priv table drop the removed pieces.

Closes #4347
2026-10-02 17:48:27 +02:00

159 lines
8.1 KiB
Markdown

# 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
| route | what |
| ------------------------- | --------------------------------------------------------------------------------------------------- |
| `/` | the hive directory, each hive with its last reported status |
| `/agents` | every agent: status, config PR, wanted state; create agents, link forge, matrix and GitHub accounts |
| `/agents/<name>/terminal` | one agent's live terminal |
| `/jobs` | the controller's job graph — where agent creation and credential mints show progress |
| `/issues` | a cross-repo issue report |
Everything it shows comes from [`swarm-controller`](../../swarm-controller/README.md).
An agent created here or with `swarmctl agent create` starts `paused`; set it
`up` from its card.
### Linking external accounts
Each agent on `/agents` opens three dialogs that write a credential for it
into the swarm secret store through swarm-controller. All three are blind
set/update actions: no route lists linked accounts or hands a token back.
- **link a matrix account** — `PUT /api/hives/{hive}/agents/{agent}/matrix-accounts/{account}`,
either a pasted bearer token or a user id + password that
swarm-controller logs in with, storing the token it gets back.
- **link a forge account** — `PUT /api/hives/{hive}/agents/{agent}/forge-accounts/{label}`
with a forge URL and an access token, stored at
`swarm/agents/<agent>/forge/<label>`. The agent's
`hive-agent-forge-accounts` unit fetches it into
`<state>/forge-<label>-token` and `<state>/forge-<label>.json`, the files
`hive-forge -f <label>` reads. The unit never deletes a pair: linking
the same label again overwrites both files, and a pair whose label the
store doesn't list stays untouched.
- **link a github account** — `PUT /api/hives/{hive}/agents/{agent}/github-account`
with a personal access token, stored at `swarm/agents/<agent>/github-token`.
One token per agent: linking again replaces it. The agent's
`hive-agent-github-token` unit fetches it into `<state>/github-token`,
the file its `gh` wrapper, git credential helper and GitHub notification
poller read. A `github-token` already in place stays when the store holds
none. What the token needs and how the agent uses it:
[GitHub accounts](../integrations/github.md).
Where each credential lives and who reads it:
[`credentials.md`](credentials.md).
## 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> --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](../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."
## 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 swarm UI's header carries a single 🔗 button, visible on every route,
opening a popover of links to other swarm-wide services. 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 `services.hyperhive.agent.dashboardLinks`).
Each service's own module contributes its entry when it's enabled on the
controller's host — `swarm-authelia.nix`, `hive-matrix.nix`,
`hive-forge/default.nix`, `swarm-grafana.nix`, `swarm-victorialogs.nix`, and
`swarm-ui.nix` for this UI's own API docs. Adding a link for a new service is
a nix-only change to that service's module, or an operator adding an entry
directly. An empty list hides the button.
The **Matrix** entry opens the swarm's matrix web client (fluffychat,
`services.hyperhive.deploy.matrix.gui.package`) at the homeserver's
gateway host, `chat.<swarm domain>` by default. `hive-matrix.nix` adds it
only when `services.hyperhive.deploy.matrix.gui.enable` is on and the
homeserver has a gateway host, the same condition under which that vhost
serves the client at `/`.
<details><summary>Adding a swarm service name: the two wiring sites</summary>
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`) |
<!-- vale write-good.Passive = NO -->
The DNS record and the local-dev `/etc/hosts` entry need no separate
edit: both derive 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.
<!-- vale write-good.Passive = YES -->
⚠️ The certificate one is the hardest to predict and the most visible when
missed. `serviceDomains` is _both_ the `allowed_domains` the secret
store's `pki/roles/swarm-services` narrows to and the list each gateway's
leaf draws its SANs from (it carries the ones that host fronts),
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.
</details>
## 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.