matrix: swarm-controller is the only minter
Every hive is in a swarm and every swarm runs matrix, so every swarm has a swarm-controller, and since #4810 its hive_sender pass mints each hive's @hive-<hive>: sender token into the store every five minutes. The two other minters of that token go: - swarm-matrix-ctl mint: the systemd.services.swarm-matrix-ctl unit in the hive-matrix container, Command::Mint and src/mint.rs. The binary, its appservice render/publish verbs, ctlPackage, ctlActive and the ctl cert role stay. bao-matrix-reader's checks on the deleted unit are removed; the leaf-identity and no-token-in-env checks now look at swarm-matrix-appservice-publish, which runs under the same identity. - the hive-side mint ladder in hive-c0re's ensure_hive_user (register/appservice-login/password-login with the local as_token), with read_appservice_token, paths::matrix_appservice_token and the helpers only it used. ensure_hive_user now takes the store's token, keeps the file when the store has none or can't be reached, and fails otherwise. - hivectl matrix sync-admin: the verb, HostRequest::MatrixSyncAdmin and handle_matrix_sync_admin. The periodic MatrixSweep (ensure_all) is unchanged apart from no longer reading the local as_token. This removes the double-mint race #4810's review flagged: two minters logging in on one pinned device could leave a dead token in the store until the next pass. Closes #4813 Closes #4814
This commit is contained in:
parent
91e47732a6
commit
ddb7d7196d
22 changed files with 187 additions and 1162 deletions
|
|
@ -93,9 +93,8 @@ delegation (the latter lives in `gateway.md::Discovery flow`).
|
|||
## Provisioning flow (appservice)
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
Registration is closed. The hive's own **appservice** creates accounts:
|
||||
hive-c0re holds the appservice token, agents never see
|
||||
it, and an agent only ever receives its own `access_token`.
|
||||
Registration is closed. **Appservices** create 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
|
||||
|
|
@ -110,7 +109,7 @@ a token.
|
|||
spec-required `hs_token` sibling, mode `0600 root:root`, then renders
|
||||
the registration to
|
||||
`/var/lib/hyperhive/matrix-appservice/hyperhive.yaml` (also `0600`).
|
||||
hive-c0re mints the tokens only when missing; the registration is
|
||||
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
|
||||
|
|
@ -129,10 +128,8 @@ a token.
|
|||
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** reads the appservice token and creates its own
|
||||
`@hive-<hive>:` account. It never mints the token itself: the value
|
||||
has to be the one the rendered registration names, and only the nix
|
||||
side writes that.
|
||||
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
|
||||
|
|
@ -183,9 +180,8 @@ hive's standing leaves the others alone.
|
|||
Its access token is the **sender token**, and it's the credential
|
||||
hive-c0re presents for every homeserver call it makes on the hive's
|
||||
behalf. It's per hive for the same reason the account is:
|
||||
`swarm-controller` mints it for every hive with the swarm's appservice token (and
|
||||
`swarm-matrix-ctl`, inside the `hive-matrix` container, for its own hive) and
|
||||
publishes it to `swarm/hives/<hive>/matrix/sender-token`, and the hive
|
||||
`swarm-controller`, its only minter, mints it for every hive with the swarm's
|
||||
appservice token and publishes it to `swarm/hives/<hive>/matrix/sender-token`, and the hive
|
||||
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
|
||||
|
|
@ -212,34 +208,19 @@ Nothing to do, and no window where the hive is without an account.
|
|||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
- **The old shared value at `swarm/services/matrix/sender-token` is read by
|
||||
nothing.** `hive-c0re` and `swarm-matrix-ctl` both build the path from the
|
||||
nothing.** `hive-c0re` and `swarm-controller` both build the path from the
|
||||
same function, and it now carries the hive's name — so the old object
|
||||
stays in the store, unread, until an operator deletes it. Delete it or
|
||||
leave it; the accounts it authenticates as keep their own standing either
|
||||
way, since an access token lives on the device that minted it.
|
||||
- **The hive re-mints, per hive, on the next sweep.** `ensure_hive_user`
|
||||
reads the new per-hive store path **first, on every sweep**, not just when
|
||||
the file is missing (`sender_source`'s decision). If a per-hive token is
|
||||
already there — `swarm-controller` mints one for every hive — the sweep
|
||||
takes it and overwrites the file, so the shared token stops being served
|
||||
as soon as one exists in the store, no boot required. If the store has
|
||||
nothing yet, the file is left untouched (still the shared token, right
|
||||
after the upgrade), so nothing breaks mid-sweep. Only when neither the
|
||||
store nor the file holds anything does the sweep fall through to the
|
||||
register-or-appservice-login ladder against `@hive-<hive>:`, using only
|
||||
the `as_token`, which is per hive and on local disk, so that step works
|
||||
with or without a reachable store.
|
||||
- **To move a hive onto its own account now**, clear both copies: the
|
||||
sender-token file, and the store's `swarm/hives/<hive>/matrix/sender-token`
|
||||
if `swarm-controller` or `swarm-matrix-ctl` has already published one for
|
||||
it (otherwise the next sweep just re-adopts that value instead of minting
|
||||
a new one). With both empty, the next sweep — or `hivectl matrix
|
||||
sync-admin` — runs the ladder, registers `@hive-<hive>:`, and persists
|
||||
that account's token to the file. The ladder never writes the store — on
|
||||
a swarm that runs `swarm-controller`, its own mint pass will reach the
|
||||
same account on its next tick and write a token there too, on the same
|
||||
pinned device, which replaces whichever token was minted last. Only once
|
||||
both copies agree does the hive stop presenting the shared one.
|
||||
- **The hive switches on the next sweep.** `ensure_hive_user` reads the
|
||||
per-hive store path **first, on every sweep**, not just when the file is
|
||||
missing (`sender_source`'s decision). `swarm-controller` mints a token
|
||||
there for every hive within five minutes; the sweep takes it and
|
||||
overwrites the file, so the shared token stops being served as soon as
|
||||
one exists in the store, no boot required. While the store has nothing
|
||||
yet, the file is left untouched (still the shared token, right after the
|
||||
upgrade), so nothing breaks mid-sweep.
|
||||
- **The rooms the shared account created don't follow the new account, and
|
||||
this is the one step that needs a decision.** Membership is per account.
|
||||
`ensure_hive_space` takes the stored room id first, so the sweep hands the
|
||||
|
|
|
|||
Loading…
Reference in a new issue