docs(3167): the setup guide gains a swarm UI step, and one group name

Per mara on the PR: the guide someone follows on a fresh deploy is a
different page from the one they read while debugging, and only the
second existed.

Also fixes a contradiction the request surfaced: setup.md and
swarm/sso.md have told every operator to create '--group admins' since
the bootstrap step existed, while the new rule required 'operators'.
This is the first rule that CONSUMES a group name, so it takes the one
the guide already creates - inventing a second would have meant every
account made by following the guide silently failing the check it was
supposed to pass, and for mara specifically a migration step that now
may not be needed at all.

setup.md's step 3 says why --group is not decoration; the new step 4
says what decides whether the page opens (the group, and the name
resolving) rather than listing a command, because there is nothing to
run. Steps renumbered, including the matrix block's internal 4a-4e.
This commit is contained in:
atlas 2026-08-12 17:57:52 +02:00
commit 4dd5093c6f
4 changed files with 51 additions and 16 deletions

View file

@ -49,30 +49,51 @@ start. Skipping this step looks like a broken proxy.
swarmctl user add mara --display-name Mara --email mara@example.com --group admins swarmctl user add mara --display-name Mara --email mara@example.com --group admins
``` ```
⚠️ **Keep `--group admins`.** It is not decoration: operator-only
surfaces (the swarm UI below) are gated on that group, and an account
without it authenticates successfully and is then refused — which reads
like a broken login rather than a missing group.
Detail, including what the password is and why this stays manual: Detail, including what the password is and why this stays manual:
[`swarm/sso.md`](swarm/sso.md). [`swarm/sso.md`](swarm/sso.md).
### 4 · Matrix ### 4 · Swarm UI (only when `swarm.ui.enable`, on by default with the controller)
Nothing to run — it is served on the swarm apex
(`https://<swarm.domain>/`) as soon as the host rebuilds. Two things
decide whether you can actually open it:
- **You are in `admins`** (step 3). The gateway asks authelia whether
you have a session; the rule that makes it mean *operator* wants the
group. Without it you log in and still get bounced.
- **The name resolves to this host.** It is published to the hive's own
resolver and to `/etc/hosts` when `gateway.localHostsEntry` is on; from
anywhere else it needs a real DNS record like any other public name.
Detail, including why reachability is deliberately not the access
control: [`swarm/ui.md`](swarm/ui.md).
### 5 · Matrix
```bash ```bash
# 4a. Ensure the hive-internal admin account exists first # 5a. Ensure the hive-internal admin account exists first
hivectl matrix sync-admin hivectl matrix sync-admin
# 4b. Provision ruth's own matrix account # 5b. Provision ruth's own matrix account
hivectl matrix create-user ruth hivectl matrix create-user ruth
# 4c. Create a human matrix account # 5c. Create a human matrix account
hivectl matrix create-user mara --password hunter2 hivectl matrix create-user mara --password hunter2
# 4d. Invite the operator to the hive Space (and optionally to rooms) # 5d. Invite the operator to the hive Space (and optionally to rooms)
hivectl matrix invite mara hivectl matrix invite mara
hivectl matrix invite @mara:yourserver --room '#hive-chat:yourserver' hivectl matrix invite @mara:yourserver --room '#hive-chat:yourserver'
# 4e. Promote the operator to homeserver admin if needed # 5e. Promote the operator to homeserver admin if needed
hivectl matrix promote-user mara hivectl matrix promote-user mara
``` ```
### 5 · Spawn sub-agents ### 6 · Spawn sub-agents
Sub-agent creation goes through the approval queue — ruth proposes, the Sub-agent creation goes through the approval queue — ruth proposes, the
operator approves, the container builds. From ruth's own turn (inside operator approves, the container builds. From ruth's own turn (inside
@ -93,7 +114,7 @@ request_init_config(name: "iris")
See [`approvals.md`](approvals.md) for the full flow. See [`approvals.md`](approvals.md) for the full flow.
### 6 · Useful host commands ### 7 · Useful host commands
```bash ```bash
# Roster: all agents, status, rev, parent, pending reminders # Roster: all agents, status, rev, parent, pending reminders

View file

@ -97,7 +97,7 @@ another container, and the three ways that one gets delivered:
## Swarm UI ## Swarm UI
The operator-only web surface on the swarm apex, why reaching it needs The operator-only web surface on the swarm apex, why reaching it needs
the `operators` group rather than just a session, and the four sites a the `admins` group rather than just a session, and the four sites a
swarm service name has to be wired into: [`ui.md`](ui.md). swarm service name has to be wired into: [`ui.md`](ui.md).
## The swarm's hive directory ## The swarm's hive directory

View file

@ -25,21 +25,28 @@ way `swarm.forge.domain` and `swarm.matrix.gatewayHost` can.
> claiming one `server_name` do not error — nginx picks one — so this is > claiming one `server_name` do not error — nginx picks one — so this is
> an assertion rather than a runtime surprise. > an assertion rather than a runtime surprise.
## 🔑 You must be in the `operators` group ## 🔑 You must be in the `admins` group
**This is the step that separates "protected" from "locked out".** The **This is the step that separates "protected" from "locked out".** The
vhost's `auth_request` asks authelia *"is there a session"*; the rule 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 that makes it mean *"is this an operator"* is an `access_control` entry
requiring `group:operators`. An account without that group authenticates requiring `group:admins`. An account without that group authenticates
fine and still gets bounced. fine and still gets bounced.
```sh ```sh
swarmctl user add <you> --group operators swarmctl user add <you> --group admins
``` ```
An account created before this existed has no groups. Re-add it with the `admins` deliberately, not a new word: [`../setup.md`](../setup.md) has
flag — `swarmctl` treats an existing entry as the canonical store, so the told every operator to create exactly that group since the bootstrap step
group is what changes. 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` treats the existing entry as the canonical store, so the group
is what changes.
Why a group and not a list of usernames: agents are getting authelia Why a group and not a list of usernames: agents are getting authelia
accounts of their own (matrix SSO), and *authenticated* would then accounts of their own (matrix SSO), and *authenticated* would then

View file

@ -43,7 +43,14 @@ let
# here because this module writes the rule that enforces it and # here because this module writes the rule that enforces it and
# `swarmctl user add --group <this>` is what grants it — the two must # `swarmctl user add --group <this>` is what grants it — the two must
# agree, and one constant is how they stay agreeing. # agree, and one constant is how they stay agreeing.
operatorGroup = "operators"; #
# ⚠️ `admins` and not a new word, because `docs/setup.md` and
# `docs/swarm/sso.md` have been telling every operator to create
# exactly that group since the bootstrap step existed. This is the
# first rule that CONSUMES a group name; picking a different one would
# have meant every account created by following the guide silently
# failing the check it was supposed to pass.
operatorGroup = "admins";
# Upstream's `services.authelia.instances.<name>` derives the unit, # Upstream's `services.authelia.instances.<name>` derives the unit,
# user, group and StateDirectory from the instance name # user, group and StateDirectory from the instance name