Watch
0
0
Fork
You've already forked hyperhive
0

docs: drop statements about absent things, state current behaviour

This commit is contained in:
atlas 2026-10-02 09:26:41 +02:00
commit b83fdc60f3
4 changed files with 22 additions and 30 deletions

View file

@ -207,17 +207,14 @@ tuwunel has none.
Promoting a user to homeserver admin and resetting a password both need an Promoting a user to homeserver admin and resetting a password both need an
admin **sender**: `!admin …` messages into `#admins:<server_name>`, and admin **sender**: `!admin …` messages into `#admins:<server_name>`, and
tuwunel only treats a message as a command when its sender is already an tuwunel only treats a message as a command when its sender is already an
admin. `@hive-<hive>:` has no admin sender to make that call with. Both are admin. Only `@swarm` is a homeserver admin, so both are swarm-level
swarm-level operations. operations.
<details><summary>Store precedence for a hive's sender account</summary> <details><summary>Store precedence for a hive's sender account</summary>
`swarm/services/matrix/sender-token` is a shared path `hive-c0re` and
`swarm-controller` both build from the same function; nothing reads it.
`ensure_hive_user` reads the per-hive path `ensure_hive_user` reads the per-hive path
`swarm/hives/<hive>/matrix/sender-token` **first, on every sweep**, not `swarm/hives/<hive>/matrix/sender-token` **first, on every sweep**, not
just when that file is missing (`sender_source`'s decision), so a value just when that file is missing (`sender_source`'s decision).
at the shared path never takes effect once the per-hive path has one.
`swarm-controller` mints a per-hive token within five minutes of a hive `swarm-controller` mints a per-hive token within five minutes of a hive
appearing; the sweep then overwrites the per-hive file, no boot appearing; the sweep then overwrites the per-hive file, no boot
required. required.
@ -246,9 +243,7 @@ halves. `registrationTokenFile` is a removed option: a config that
still sets it fails to evaluate with a message naming the appservice. still sets it fails to evaluate with a message naming the appservice.
<!-- vale write-good.Passive = NO --> <!-- vale write-good.Passive = NO -->
- **Access tokens are independent of the registration token.** An - **Access tokens live on the device that minted them.**
access token lives on the device that minted it, so accounts and
sessions are unaffected by the registration token's presence.
`login_with_password` stays on, so the password fallback works `login_with_password` stays on, so the password fallback works
too. too.
- **The homeserver's `admin_execute` promotes only `@swarm` at boot** - **The homeserver's `admin_execute` promotes only `@swarm` at boot**
@ -271,9 +266,9 @@ Initial rollout settings:
until you list peers. until you list peers.
- `allow_registration = false`. tuwunel checks this flag only for - `allow_registration = false`. tuwunel checks this flag only for
requests that arrive **without** an appservice token, so the appservices requests that arrive **without** an appservice token, so the appservices
still create accounts and tuwunel refuses everyone else. It's not a still create accounts and tuwunel refuses everyone else. With no
hardening afterthought: with no registration token configured, registration token configured, `allow_registration = true` makes
`allow_registration = true` makes tuwunel refuse to start unless tuwunel refuse to start unless
`yes_i_am_very_very_sure_…_open_registration_…` is also set. `yes_i_am_very_very_sure_…_open_registration_…` is also set.
- `allow_encryption` — server-side E2EE switch, sourced from - `allow_encryption` — server-side E2EE switch, sourced from
`services.hyperhive.swarm.matrix.allowEncryption` (**default `false`**, opt-in). `services.hyperhive.swarm.matrix.allowEncryption` (**default `false`**, opt-in).

View file

@ -2,7 +2,7 @@
Every host's nginx: the one front door for whatever this host serves. A swarm service running here (forge, matrix, SSO, the swarm UI, the metrics and log stores) declares its own vhost through the gateway; the gateway itself adds the hive's own surface — dashboard, per-agent UIs, matrix discovery. Every host's nginx: the one front door for whatever this host serves. A swarm service running here (forge, matrix, SSO, the swarm UI, the metrics and log stores) declares its own vhost through the gateway; the gateway itself adds the hive's own surface — dashboard, per-agent UIs, matrix discovery.
_For the operator configuring `services.hyperhive.gateway.*` on a host._ nginx and the hive resolver (dnsmasq) run on the host next to hive-c0re, not in a container: they bind `:80`/`:443` and the bridge address, so a network namespace of their own would isolate nothing. _For the operator configuring `services.hyperhive.gateway.*` on a host._ nginx and the hive resolver (dnsmasq) run on the host next to hive-c0re, not in a container: they bind `:80`/`:443` and the bridge address.
You rarely switch it on yourself. `gateway.enable` defaults to off, and every module that serves a vhost or needs hive names to resolve sets `gateway.enable` / `gateway.dns.enable` with `mkDefault true` — the hive controller, each swarm service, CI. You rarely switch it on yourself. `gateway.enable` defaults to off, and every module that serves a vhost or needs hive names to resolve sets `gateway.enable` / `gateway.dns.enable` with `mkDefault true` — the hive controller, each swarm service, CI.
@ -41,7 +41,7 @@ Per-agent UIs stay sub-path; forge and matrix get sub-domains → [Sub-domain sh
## TLS modes ## TLS modes
The gateway always terminates TLS: there is no http-only mode. Which certificate it serves depends on what you configure: The gateway always terminates TLS. Which certificate it serves depends on what you configure:
| mode | config | cert source | `.well-known` scheme | | mode | config | cert source | `.well-known` scheme |
|---|---|---|---| |---|---|---|---|
@ -77,7 +77,7 @@ The issuer is a **host-held hive CA**, not a bare self-signed leaf. `hive-tls-ca
⚠️ **Keep the import unit.** It does two jobs nginx needs: it re-modes the key to `0640 root:nginx` (nginx's pre-start `nginx -t` runs as the nginx user and fails on the CA's `0600 root:root` key), and it makes sure **every cert path the config names exists** — when the swarm-services leaf is missing it installs the hive leaf in its place. nginx refuses a config naming a missing cert file, so without that fallback one missing leaf takes down every vhost, not just one. ⚠️ **Keep the import unit.** It does two jobs nginx needs: it re-modes the key to `0640 root:nginx` (nginx's pre-start `nginx -t` runs as the nginx user and fails on the CA's `0600 root:root` key), and it makes sure **every cert path the config names exists** — when the swarm-services leaf is missing it installs the hive leaf in its place. nginx refuses a config naming a missing cert file, so without that fallback one missing leaf takes down every vhost, not just one.
**Why a CA, not a bare leaf**: a bare self-signed leaf is its own trust anchor, so every regeneration is a new anchor every consumer must re-trust, and nothing can wire a runtime-generated leaf into an agent's build-time trust store. Agents and federation peers trust the stable CA once; leaf rotation never breaks them. **Why a CA, not a bare leaf**: a bare self-signed leaf is its own trust anchor, so every regeneration is a new anchor every consumer must re-trust, and an agent's build sets its trust store once, before any leaf exists. Agents and federation peers trust the stable CA once; leaf rotation never breaks them.
**What consumers trust**: `trust-bundle.pem` in the same state dir, not `ca.pem`. The hive CA is an intermediate under the swarm root ([`swarm/ca.md`](../swarm/ca.md) has the hierarchy), and a verifier can't stop at an intermediate — so the bundle carries the hive CA plus its root. nginx serves the leaf with the hive CA appended for the same reason. Agents (via `security.pki.certificateFiles`), the CI and forge containers and federating peers all read the bundle. **What consumers trust**: `trust-bundle.pem` in the same state dir, not `ca.pem`. The hive CA is an intermediate under the swarm root ([`swarm/ca.md`](../swarm/ca.md) has the hierarchy), and a verifier can't stop at an intermediate — so the bundle carries the hive CA plus its root. nginx serves the leaf with the hive CA appended for the same reason. Agents (via `security.pki.certificateFiles`), the CI and forge containers and federating peers all read the bundle.
@ -107,7 +107,7 @@ nginx reads the directory directly. Keep the key readable by nginx:
### Fronting with an external TLS terminator ### Fronting with an external TLS terminator
The gateway has no plain-http upstream mode. Either give the gateway the real cert (`tls.certDir` or `tls.acme`) so it serves proper TLS itself, or front it over a unix socket rather than a plain-http TCP port. `.well-known/matrix/*` responses always advertise `https` ([Discovery flow](#discovery-flow-matrix)). Give the gateway the real cert (`tls.certDir` or `tls.acme`) so it serves proper TLS itself, or front it over a unix socket rather than a plain-http TCP port. `.well-known/matrix/*` responses always advertise `https` ([Discovery flow](#discovery-flow-matrix)).
## HTTP Basic auth ## HTTP Basic auth
@ -234,7 +234,7 @@ matrix-dart-sdk (FluffyChat and others) always fetches the well-known over `http
Federation peers fetch `.well-known/matrix/server` → `{"m.server":"chat.<swarm>:<httpsPort>"}`. The port is always explicit, even 443: a delegated host without a port means the federation default 8448, not 443. Peers then reach `/_matrix/` on the chat vhost through the gateway, so the gateway must be reachable from them (`gateway.openFirewall`). Federation peers fetch `.well-known/matrix/server` → `{"m.server":"chat.<swarm>:<httpsPort>"}`. The port is always explicit, even 443: a delegated host without a port means the federation default 8448, not 443. Peers then reach `/_matrix/` on the chat vhost through the gateway, so the gateway must be reachable from them (`gateway.openFirewall`).
⚠️ The gateway serves both `.well-known` routes on the **hive** vhost. Matrix looks them up at the `serverName`, which defaults to the bare swarm domain, and the swarm UI's apex vhost serves no `.well-known/matrix/*` route. ⚠️ The gateway serves both `.well-known` routes on the **hive** vhost only. Matrix looks them up at the `serverName`, which defaults to the bare swarm domain, so a lookup against the swarm domain reaches the swarm UI's apex vhost and finds nothing — pin `serverName` to the hive domain, or serve `.well-known` at the swarm domain yourself.
## Sub-domain shape (rationale) ## Sub-domain shape (rationale)
@ -266,7 +266,7 @@ The `chat.<swarm>` vhost (fluffychat) serves a flutter/SPA bundle via the Accept
- hard-refresh on a sub-route must serve `index.html` (SPA's client-side router takes over after JS bootstrap) - hard-refresh on a sub-route must serve `index.html` (SPA's client-side router takes over after JS bootstrap)
- a non-navigation request that isn't an on-disk asset must NOT get HTML with the wrong content-type - a non-navigation request that isn't an on-disk asset must NOT get HTML with the wrong content-type
Solution: an `nginx http`-context `map $http_accept $matrix_spa_target { ... }` keyed on the request's Accept header. Browser navigations (`Accept: text/html,...`) get `index.html`; everything else (`Accept: image/*`, `*/*`, `application/json`, `text/event-stream`, …) gets a sentinel nonexistent path, so `try_files $uri $uri/ $matrix_spa_target =404` falls through to a plain `404` — a missing asset is just missing. No extension allowlist, no `if` block, no regex heuristics. Solution: an `nginx http`-context `map $http_accept $matrix_spa_target { ... }` keyed on the request's Accept header. Browser navigations (`Accept: text/html,...`) get `index.html`; everything else (`Accept: image/*`, `*/*`, `application/json`, `text/event-stream`, …) gets a sentinel nonexistent path, so `try_files $uri $uri/ $matrix_spa_target =404` falls through to a plain `404` — a missing asset is just missing.
#### Dashboard: path-based routing (not Accept-header) #### Dashboard: path-based routing (not Accept-header)

View file

@ -7,9 +7,7 @@ need the bridge set it with `mkDefault true` — the hive controller, CI,
the hive collector, and the gateway's resolver, which every swarm service the hive collector, and the gateway's resolver, which every swarm service
turns on. Configured via `services.hyperhive.network.*`. turns on. Configured via `services.hyperhive.network.*`.
Isolation is the only mode; agent containers never share the host netns. Agent containers always run in a private netns.
`services.hyperhive.network.isolateContainers` and
`services.hyperhive.network.upstreamDns` don't exist.
## Network map ## Network map

View file

@ -13,12 +13,12 @@ catalogued below.
Telemetry crosses two collectors, and which one you configure depends on what Telemetry crosses two collectors, and which one you configure depends on what
the host is: the host is:
| | runs where | receives from | does | | | runs where | receives from | does |
| ------------------------------------ | ---------------------- | --------------------------------- | --------------------------------------------------------------------- | | ------------------------------------ | ---------------------- | --------------------------------- | ----------------------------------------------- |
| **swarm tier** — `deploy.swarm-otel` | once per swarm | every hive's collector | writes the swarm's stores and exports upstream | | **swarm tier** — `deploy.swarm-otel` | once per swarm | every hive's collector | writes the swarm's stores and exports upstream |
| **hive tier** — `otel.enable` | every hive with agents | that hive's agents, on the bridge | forwards to the swarm tier. Holds no credential, picks no destination | | **hive tier** — `otel.enable` | every hive with agents | that hive's agents, on the bridge | forwards to the swarm tier. Holds no credential |
An all-local host runs both, and needs nothing said about the hop between them. An all-local host runs both; the hop between them configures itself.
```nix ```nix
services.hyperhive.otel = { services.hyperhive.otel = {
@ -31,7 +31,7 @@ services.hyperhive.otel = {
## Enabling export ## Enabling export
`otel.enable` is the single gate on a hive: one switch in the host config `otel.enable` is the single gate on a hive: one switch in the host config
covers every agent container on it, with no per-agent opt-in or opt-out. covers every agent container on it.
`endpoint` is where telemetry ends up after it leaves the swarm — optional, `endpoint` is where telemetry ends up after it leaves the swarm — optional,
because the swarm's own metrics store (`deploy.victoriametrics`) is a because the swarm's own metrics store (`deploy.victoriametrics`) is a
destination in its own right. With both, telemetry goes to both. See destination in its own right. With both, telemetry goes to both. See
@ -50,7 +50,7 @@ The hive collector reaches the swarm collector by its gateway name
(`swarm.otel.domain`, default `otel.<swarm domain>`) — the same DNS-and-CA-trust (`swarm.otel.domain`, default `otel.<swarm domain>`) — the same DNS-and-CA-trust
shape every hive-to-swarm-service hop uses. On the host running the swarm shape every hive-to-swarm-service hop uses. On the host running the swarm
collector the hive's dnsmasq answers that name; elsewhere it resolves through collector the hive's dnsmasq answers that name; elsewhere it resolves through
ordinary DNS. Nothing here needs setting for the split-host case. ordinary DNS.
⚠️ **The hive collector carries every bit of that hive's telemetry.** It ⚠️ **The hive collector carries every bit of that hive's telemetry.** It
runs on the same host as the agents and restarts on failure. Telemetry isn't runs on the same host as the agents and restarts on failure. Telemetry isn't
@ -62,8 +62,7 @@ the control plane, so degraded telemetry isn't degraded operation.
every agent needs the credential — and the only place to hand it to an agent every agent needs the credential — and the only place to hand it to an agent
container is somewhere the agent itself can read, its own claude settings container is somewhere the agent itself can read, its own claude settings
among them. `0600` protects a secret from other containers, not from the among them. `0600` protects a secret from other containers, not from the
agent it belongs to. An option that could select that path would reopen the agent it belongs to.
hole.
**The tiers stay separate on one box.** An all-local hive is a statement about **The tiers stay separate on one box.** An all-local hive is a statement about
_where_ processes run, not about the shape of the deployment. A boundary that _where_ processes run, not about the shape of the deployment. A boundary that