The store path and every identifier around it called this an admin token. It is not one: of ~15 hive-c0re call sites only two need homeserver admin, and the homeserver no longer promotes the account at boot, so the name overstated both what the credential is and what it may do. Renaming it to the account was not enough either. "The `@hive:` token" reads as the token of a hive user, and no such user is provisioned — `@hive:<server_name>` is the appservice registration's own `sender_localpart`, an account the homeserver creates for itself when it loads the registration. So it is the **sender token**: the matrix appservice sender account's access token, at `swarm/services/matrix/sender-token`. The name says what it authenticates as rather than what it may do, which is the part that was wrong. The path has one constructor, and the bao grant, the grant assertion and three unit tests pin its literal independently — so a half-finished rename fails a check rather than leaving the minter and its readers disagreeing at runtime. `tracing` messages are renamed with the code, so the journal reads the way the source does. The host-side file keeps its name (`matrix/access-token`): it carried no admin framing, and renaming it would orphan the file on every deployed hive for nothing. `docs/tools/hivectl-cli.md` is regenerated from the clap tree.
44 lines
2 KiB
Markdown
44 lines
2 KiB
Markdown
# swarm-matrix-minter
|
|
|
|
A boot-time oneshot that runs **inside `containers.hive-matrix`**, beside the
|
|
homeserver, and puts the appservice sender account's access token into the swarm's secret
|
|
store under an identity of its own.
|
|
|
|
## Why it lives in the matrix container
|
|
|
|
The credential it mints is authorised by the appservice `as_token`, and the
|
|
container already holds that: `nix/host-modules/hive-matrix.nix` bind-mounts the
|
|
rendered appservice registration into it read-only, because that is how tuwunel
|
|
itself is handed the registration. Minting anywhere else would mean copying the
|
|
`as_token` to a second holder — and the point of this component is that the hive
|
|
stops being one.
|
|
|
|
It is not the swarm controller for the same reason, plus a structural one: a
|
|
homeserver has exactly **one** `@hive:` account and a swarm runs one
|
|
homeserver, so "mint it once" needs no lock, no lease and no trigger surface — it
|
|
is a property of the thing being minted.
|
|
|
|
## Idempotency
|
|
|
|
The **store** is the key, not the homeserver. A run reads
|
|
`swarm/services/matrix/sender-token` first and returns without touching the
|
|
homeserver when something is already there. Only an empty path reaches the mint
|
|
ladder:
|
|
|
|
1. `POST /_matrix/client/v3/register` with `"type": "m.login.application_service"`
|
|
— one round trip, no UIAA.
|
|
2. `M_USER_IN_USE` (the expected arm on a homeserver that has already loaded the
|
|
registration, since the account is the appservice's own `sender_localpart`) →
|
|
`POST /_matrix/client/v3/login` as the appservice, same pinned `device_id`, so
|
|
the old device is replaced rather than duplicated.
|
|
3. Write the result to the store.
|
|
|
|
A crash between the homeserver call and the store write is recoverable: the next
|
|
run takes arm 2.
|
|
|
|
## 🩸 A secret is a path, never a value
|
|
|
|
Nothing here logs, prints or interpolates a token. The mint ladder's errors are
|
|
built from the homeserver's _status_ and its `errcode`, never its body, because a
|
|
`/login` response body is an access token. The one identifier this binary logs is
|
|
the store path it wrote.
|