Watch
0
0
Fork
You've already forked hyperhive
0

matrix: drop the per-hive appservice and its unused token

Every hive's homeserver loaded a per-hive `hyperhive` appservice
registration, and the swarm minted and delivered an as_token for it
through bao. No client presented that token: swarm-controller creates
every `@hive-<hive>` sender and every agent account with the swarm
appservice token (hive_sender.rs, agent_token.rs), and no Rust code read
the per-hive one.

Removed, with everything that existed only to serve it:
- hive-matrix.nix: the `hyperhive` registration, its activation-time
  render, the host bind mount of /var/lib/hyperhive/matrix-appservice,
  its LoadCredential entry, and the internal options
  `deploy.matrix.appserviceTokenFile` / `appserviceRegistrationScript`.
  tuwunel now loads only the swarm registration.
- glue-matrix-bao-token.nix and its `swarm-bao-matrix-token` unit.
- glue-bao-tls.nix: the matrix-token leaf and its defaults.
- swarm-bao.nix: the `swarm-matrix-token-<hive>` roles and policies
  (`swarm-bao-matrix-token-policy`), and the options
  `deploy.bao.matrixTokenClient{Cert,Key}File` and
  `matrixTokenCommonNamePrefix`, now mkRemovedOptionModule.
- swarm-secret-publisher.nix: the per-hive token mint and its state dir.
- swarm.nix: the reserved `<matrixTokenCommonNamePrefix>-<hive>` CN.
- swarm-secret-client: `matrix::appservice_token_path`.

The swarm registration (swarm-matrix-ctl, swarm-controller) is untouched.
Comments and docs that pointed at the removed glue file now point at
glue-queue-agent-credential.nix, which has the same reader shape.

Refs #4428
This commit is contained in:
atlas 2026-10-04 22:52:22 +02:00 • committed by mara
commit 48067d07e8
31 changed files with 362 additions and 1233 deletions

View file

@ -105,8 +105,9 @@ delegation (the latter lives in `gateway.md::Discovery flow`).
## Provisioning flow (appservice)
<!-- vale write-good.Passive = NO -->
Registration is closed. **Appservices** create accounts: agents never see
an appservice token, and an agent only ever receives its own `access_token`.
Registration is closed. **The swarm's appservice** creates accounts: agents
never see an appservice token, and an agent only ever receives its own
`access_token`.
<!-- vale write-good.Passive = YES -->
The appservice has no URL (`url: null` in its registration), so the
@ -116,48 +117,42 @@ no secret has to be equal on both sides of the wire, and why account
creation doesn't depend on registration being open to anyone who learns
a token.
1. **System activation** mints a 32-byte random hex appservice token (64
chars) at `/var/lib/hyperhive/matrix-appservice-token` and its
spec-required `hs_token` sibling, mode `0600 root:root`, then renders
the registration to
`/var/lib/hyperhive/matrix-appservice/hyperhive.yaml` (also `0600`).
The script mints the tokens only when missing; the registration is
re-rendered every time, because the token file can be overwritten in
place by the swarm secret store and a registration naming a stale
token authenticates nobody. Runs at activation time, before any
container start, because the directory is bind-mounted and
nixos-container refuses to start when a bind source is missing.
2. **Read-only bind-mount** maps that directory into the tuwunel
container at the same path.
3. **systemd `LoadCredential=`** inside the container copies the
registration into
`/run/credentials/tuwunel.service/hyperhive-appservice.yaml`, owned by
tuwunel's dynamic user with mode `0400`, at service start. The host
1. **`swarm-matrix-ctl appservice render`**, inside the `hive-matrix`
container and before tuwunel starts, mints the registration's
`as_token` and its spec-required `hs_token` when they're missing, and
renders the registration to
`/var/lib/swarm-matrix-appservice/swarm.yaml` (mode `0600 root:root`).
2. **systemd `LoadCredential=`** copies the registration into
`/run/credentials/tuwunel.service/swarm-appservice.yaml`, owned by
tuwunel's dynamic user with mode `0400`, at service start. The source
file stays `root:root 0600` — no `chown :tuwunel` / `chmod 0640` /
GID-pin gymnastics required. Keeps `DynamicUser = true` +
`PrivateUsers = true` intact.
4. tuwunel's `appservice_dir` points at the credentials directory, not at
the bind-mount path. It reads only `.yaml`/`.yml` entries from there,
so the sibling credentials are invisible to it. The `.yaml` suffix on
the credential id is what makes this work.
5. **hive-c0re** doesn't read the appservice token and creates no
account. Its `@hive-<hive>:` token comes from the swarm store (below).
6. **Agents' accounts aren't this hive's.** `swarm-controller` creates each
one with the swarm's own appservice token (next section), stores its
token at `swarm/agents/<agent>/matrix/main`, and the agent's
`hive-matrix-daemon` reads it from there as the agent itself.
3. tuwunel's `appservice_dir` points at the credentials directory. It
reads only `.yaml`/`.yml` entries from there, so the sibling
credentials are invisible to it. The `.yaml` suffix on the credential
id is what makes this work.
4. **hive-c0re** holds no appservice token and creates no account. Its
`@hive-<hive>:` token comes from the swarm store (below).
5. **`swarm-controller` creates every account**: each agent's, with its
token stored at `swarm/agents/<agent>/matrix/main` for the agent's
`hive-matrix-daemon` to read as the agent itself, and each hive's
sender account (below).
A homeserver whose `swarm-matrix-ctl` has no store identity
(`services.hyperhive.deploy.matrix.ctlBaoClientCertFile`) loads no
registration, and nothing creates accounts on it.
### The swarm's appservice, and its admin sender
A second registration sits beside the hive's: id `swarm`, sender `@swarm`,
the same non-exclusive namespace. It's the swarm's identity on the
homeserver, and `swarm-controller` creates every agent's account with it.
tuwunel loads every `.yaml` in `appservice_dir` and refuses only a
duplicate `id` or `as_token`, so the two overlapping namespaces coexist.
The registration's id is `swarm`, its sender `@swarm`, and its user
namespace non-exclusive. It's the swarm's identity on the homeserver:
`swarm-controller` creates every agent's account and every hive's sender
account with it.
`swarm-matrix-ctl appservice render` mints its tokens inside the
`hive-matrix` container, before tuwunel starts, and renders the
registration tuwunel loads as a second credential. `appservice publish`
registration tuwunel loads as a credential. `appservice publish`
then writes its `as_token` to
`swarm/controller/swarm-controller/matrix/appservice-token`. No hive's
policy reaches that path: only matrix-ctl (which writes it) and
@ -199,13 +194,12 @@ lands on.
`services.hyperhive.deploy.swarm-controller.authBridgeUrl` and
`services.hyperhive.deploy.swarm-controller.matrixHomeserverUrl` set.
### The appservice's sender account, and why it isn't an admin
### A hive's sender account, and why it isn't an admin
`@hive-<hive>:<server_name>` is the appservice's own `sender_localpart`,
which the homeserver creates itself when it loads the registration — on a
zero-user database, inside startup, before the HTTP listener accepts
anything. It's an **ordinary account**: nothing promotes it; the
homeserver's `admin_execute` promotes only `@swarm`.
`@hive-<hive>:<server_name>` is the account a hive acts as.
`swarm-controller` creates it with the swarm's appservice token, for every
hive in its directory. It's an **ordinary account**: nothing promotes it;
the homeserver's `admin_execute` promotes only `@swarm`.
**One account per hive.** The localpart carries the hive's name, so a swarm
whose hives share a homeserver gives each of them its own identity: the
@ -220,8 +214,8 @@ appservice token and publishes it to `swarm/hives/<hive>/matrix/sender-token`, a
reads it from there under its own certificate. That path sits inside the
hive's own read grant (`swarm/hives/<hive>/*`), so a hive fetches its own
token and gets a refusal on any other hive's. The name says what it
authenticates as — an account the appservice registration brings into being
— rather than any privilege level, because it carries none.
authenticates as — an account the swarm's appservice creates — rather
than any privilege level, because it carries none.
It needs no promotion for what the hive does with it. Creating the hive
Space and the chat room, writing their hierarchy and join rules, and
@ -251,8 +245,8 @@ required.
<details><summary>Appservice identity</summary>
The activation script mints the appservice token and renders the
registration before the homeserver restarts, so every boot has both
`swarm-matrix-ctl appservice render` mints the appservice tokens and
renders the registration before tuwunel starts, so every boot has both
halves.
<!-- vale write-good.Passive = NO -->
@ -261,11 +255,10 @@ halves.
too.
- **The homeserver's `admin_execute` promotes only `@swarm` at boot**
(above); a hive's sender account is never promoted.
- **The value that matters lives at `matrix/appservice-token`.**
`swarm-secret-publish` on the authelia host mints and `put`s it
there; the hive uses its locally minted token only until the first
successful read. See [`../swarm/secrets.md`](../swarm/secrets.md)
for how that mint stays idempotent across runs.
- **The value that matters lives at
`swarm/controller/swarm-controller/matrix/appservice-token`.**
`appservice publish` writes the container's copy there whenever the
store's differs, and `swarm-controller` reads it from there.
<!-- vale write-good.Passive = YES -->
</details>
@ -277,8 +270,8 @@ Initial rollout settings:
restart. `trusted_servers = []` keeps it effectively closed
until you list peers.
- `allow_registration = false`. tuwunel checks this flag only for
requests that arrive **without** an appservice token, so the appservices
still create accounts and tuwunel refuses everyone else. With no
requests that arrive **without** an appservice token, so the swarm's
appservice still creates accounts and tuwunel refuses everyone else. With no
registration token configured, `allow_registration = true` makes
tuwunel refuse to start unless
`yes_i_am_very_very_sure_…_open_registration_…` is also set.