docs: follow the swarm service names to the swarm domain

The three moves above falsify prose in five files, and none of it is
caught by a gate: clippy, cargo test and nix flake check read exactly
zero markdown.

Corrected where a doc stated a DEFAULT or enumerated the vhosts --
gateway (ACME requirements + the header table + the two Forgejo
reachability lines), matrix (gatewayHost, firewall rationale, agent
reachability), network (container table + the HTTP resolution bullet),
turn-loop config, dashboard.

Two mentions are deliberately left alone. `docs/tools/hivectl.md` says
the URL is read from the daemon "instead of assuming forge.<domain>" --
that sentence is about not assuming a shape and stays true. And
matrix.md's breaking-change note describes what `serverName` USED to
default to; it is history, correctly labelled, and rewriting it would
erase the record of an earlier migration.

The ACME line gained a requirement rather than a rename: with the
service names under the swarm domain, every one of those names has to
resolve to this host, not just the hive's own.
This commit is contained in:
atlas 2026-08-05 23:30:53 +02:00 committed by mara
commit a1243fe04a
5 changed files with 24 additions and 15 deletions

View file

@ -184,7 +184,7 @@ services.hyperhive.gateway = {
nginx inside the gateway container obtains and auto-renews certs via the ACME HTTP-01 challenge on `port` (default 80). The gateway container shares the host network namespace (`privateNetwork = false`) so outbound ACME requests work without any extra routing. Certs are stored inside the container's persistent state dir (`/var/lib/acme/` inside `hive-gateway`; survives restarts because `ephemeral = false`).
**Requirements**: `services.hyperhive.domain` must be publicly DNS-resolvable to this host, and `openFirewall = true` so Let's Encrypt can reach `/.well-known/acme-challenge/`. Each active vhost (main domain, `forge.<domain>`, `matrix.<domain>`) gets its own cert via separate ACME challenges.
**Requirements**: `services.hyperhive.domain` must be publicly DNS-resolvable to this host, and `openFirewall = true` so Let's Encrypt can reach `/.well-known/acme-challenge/`. Each active vhost (main domain, `forge.<swarm-domain>`, `chat.<swarm-domain>`) gets its own cert via separate ACME challenges — the swarm services default to names under `services.hyperhive.swarm.domain`, so **every one of those names must resolve to this host too**, not just the hive's own.
Mutual exclusion: `tls.certDir` set together with `tls.acme.enable = true` fails an assertion — pick one external TLS source (or neither, for the self-signed default).
@ -308,8 +308,8 @@ The forge container shares the host network namespace
host-side service — nixos-container is here for state + systemd-unit
isolation, not network isolation. Note this is the FORGE container;
agent containers are network-isolated and reach the forge through the
gateway by `forge.<domain>` (see `HIVE_FORGE_URL` above), not via the
host's loopback.
gateway by `forge.<swarm-domain>` (see `HIVE_FORGE_URL` above), not via
the host's loopback.
State lives at `/var/lib/nixos-containers/hive-forge/var/lib/forgejo/`
and survives container restart / host reboot. To wipe, destroy the
@ -336,7 +336,7 @@ via `-p 2222`). Port 22 is left alone on the host for openssh.
`openFirewall` (default **false**) controls whether `httpPort` and
`sshPort` are opened in the host firewall. Off by default (secure by
default): agents reach Forgejo through the gateway (`forge.<domain>` on
default): agents reach Forgejo through the gateway (`forge.<swarm-domain>` on
the bridge), not the raw port, so no firewall hole is needed. Flip to
`true` when you need:
- The operator's browser to reach `http://<host>:<httpPort>/` directly
@ -542,7 +542,7 @@ ordering, preventing the subrequest from looping back through
## Security headers
The following headers are emitted at server scope on every gateway
vhost (`_`, `forge.<domain>`, `matrix.<domain>`):
vhost (`_`, `forge.<swarm-domain>`, `chat.<swarm-domain>`):
| Header | Value |
|--------|-------|

View file

@ -18,7 +18,7 @@ Same shape as [`gateway.md::hive-forge container shape`](gateway.md):
separate state dir.
- Container shares the host network namespace
(`privateNetwork = false`) for state + systemd-unit isolation. Agents
reach the homeserver at `matrix.<domain>` via the gateway (agents
reach the homeserver at `chat.<swarm-domain>` via the gateway (agents
run in private netns and can't access host loopback directly).
- Persistent state at
`/var/lib/nixos-containers/hive-matrix/var/lib/matrix-tuwunel/`
@ -38,9 +38,17 @@ Two distinct hostnames:
the hive-gateway serves at that domain.
- **`gatewayHost`** — the API listener hostname, where the gateway's
matrix vhost proxies `/_matrix/*` to tuwunel. Defaults to
`matrix.<services.hyperhive.domain>` (sub-domain shape). Set to
`null` to skip the gateway vhost (tuwunel stays direct on
`httpPort`).
`chat.<services.hyperhive.swarm.domain>` — the **swarm's** domain,
because a swarm runs one homeserver. Set to `null` to skip the
gateway vhost (tuwunel stays direct on `httpPort`).
⚠️ Only this one moved. `serverName` still defaults to the bare hive
domain, and the two are independent by design: `gatewayHost` is a
routing detail clients rediscover through `.well-known`, while
`serverName` is baked into every user and room id. A deployment that
wants its old API hostname pins
`gatewayHost = "matrix.<hive-domain>"` — exactly what the previous
default rendered.
**Breaking change**: `serverName` used to default to
`matrix.${services.hyperhive.domain}`. Existing homeservers must set
@ -53,7 +61,7 @@ sub-domain from the user-facing identifier.
`openFirewall` defaults to `false` (secure-by-default): the host
reaches the homeserver on loopback, and agent containers reach it
at `matrix.<domain>` via the gateway — so the firewall hole only
at `chat.<swarm-domain>` via the gateway — so the firewall hole only
matters for access from *outside* the host. Flip to `true` when
announcing the homeserver to other hives or when an external matrix
client needs to reach the client-server API directly.

View file

@ -46,7 +46,7 @@ untouched by any of it.
| container | netns | IPv4 | listens / reached via |
| -------------- | ----------------------- | -------------------- | -------------------------------------------------------------------------------------------- |
| `hive-gateway` | host (shared) | host addresses | nginx `:80`/`:443` (every vhost); dnsmasq `bridgeIp:53` + DHCP `:67` on the bridge |
| `hive-forge` | host (shared) | host addresses | forgejo `:3000` http, `:2222` git-ssh; fronted by the `forge.<domain>` vhost |
| `hive-forge` | host (shared) | host addresses | forgejo `:3000` http, `:2222` git-ssh; fronted by the `forge.<swarm-domain>` vhost |
| `hive-matrix` | host (shared) | host addresses | tuwunel `:8008` (+ optional federation port); fronted by the matrix vhost |
| `hive-ci` | private, veth on bridge | DHCP pool | outbound only (runner → forge); no inbound surface |
| `h-<agent>` | private, veth on bridge | DHCP pool | web UI via UDS `/run/hive-agent/<name>` → nginx sub-path; in-container UI port hashed 81008999 |
@ -58,8 +58,9 @@ The flows, end to end:
- **DNS** — agents query `bridgeIp:53`; hive zones are answered
authoritatively with the bridge IP, everything else forwards to the
host's resolvers (see *Resolver behaviour* below).
- **HTTP**`forge.<domain>` / matrix / dashboard names all resolve
to the bridge IP, land on nginx `:80`/`:443`, and proxy to forgejo
- **HTTP**`forge.` and `chat.` (under `swarm.domain`) plus the hive's
own dashboard name resolve to the bridge IP, land on nginx
`:80`/`:443`, and proxy to forgejo
`:3000`, tuwunel `:8008`, hive-c0re `127.0.0.1:7000`, or a per-agent
UI unix socket.
- **Internet egress** — agent default route points at the bridge IP;

View file

@ -157,7 +157,7 @@ evaluated outside a hive.
**`hyperhive.matrix.url`** — homeserver URL used by
`hive-matrix-daemon` when connecting via the matrix-sdk. hive-c0re
writes it into every agent at deploy time as the gateway-routed
`matrix.<domain>` URL, so isolated agents can reach the homeserver.
`chat.<swarm-domain>` URL, so isolated agents can reach the homeserver.
Override per-agent when an agent should talk to a different homeserver
— for example a remote hive's tuwunel reached over a VPN, or an
external Matrix server for a federation-only agent.

View file

@ -692,7 +692,7 @@ in once with the in-host tuwunel homeserver URL
(`http://localhost:8008` or whatever the matrix module exposes).
The unified nginx-front re-root to
`https://matrix.${hyperhive.domain}` + `.well-known/matrix/client`
`https://chat.${hyperhive.swarm.domain}` + `.well-known/matrix/client`
auto-discovery lives in `docs/gateway.md` (atlas's lane).
## FL0W page (`/flow.html`)