matrix: publish the appservice token from the swarm, not just read it
`glue-matrix-bao-token.nix` has read `secret/swarm/hives/<hive>/matrix/appservice-token` since it landed, but nothing ever wrote that path. The store was empty in every deployment, so every read degraded to "keep what activation minted" and each hive stayed the origin of a value the swarm has to agree on — two hives never converged. `swarm-secret-publish` is now the producer. It already holds a store identity, already writes under the hive prefix, and already runs per hive in the roster, so the mint is a third loop beside the two OIDC copies rather than a second shape of this unit. Idempotence comes from a record of its own, not from the store: this principal is granted `create`/`update` with no `read`, so it cannot ask whether a hive already has a token. It keeps what it minted under `StateDirectory=` (0700 dir, 0600 file) and mints only when that file is missing or empty; the `put` runs every time, because re-putting the same bytes changes nothing for a reader while a mint whose publish failed must not be left as a token this host holds and no hive can reach. The token never becomes a nix literal and never reaches argv: the mint redirects into a file, and the publish hands bao `value=@<path>` so bao opens it itself — the same handling the OIDC loops use. `hive-matrix.nix`'s activation mint stays as the genuine first-boot fallback. It already fires only when the token file is absent, so it cannot clobber a value the store delivered; `hs_token` has no swarm half and is still minted there for real. Refs #4402
This commit is contained in:
parent
7ee7080b21
commit
199afa41c8
5 changed files with 167 additions and 35 deletions
|
|
@ -132,14 +132,14 @@ when the system builds. The server names the offending file and refuses to run.
|
|||
|
||||
## Hive-level — one of each per hive
|
||||
|
||||
| secret | generated by | lives at |
|
||||
| ---------------------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
||||
| hive CA cert + key | `hive-tls.nix` first-boot unit | `<deploy.hive-controller.tls.stateDir>/ca.pem`, `ca-key.pem` (`0600`) |
|
||||
| hive leaf certs | `hive-tls.nix`, signed by the hive CA | `<deploy.hive-controller.tls.stateDir>/<name>.pem` |
|
||||
| matrix appservice token | a host activation script, on first boot | `/var/lib/hyperhive/matrix-appservice-token` (`0600`) |
|
||||
| the forge's copy of its OIDC secret | `hive-forge-oidc-secret.service` copies it from authelia's tree | `/var/lib/forgejo-oidc/<id>.secret` inside the forge container |
|
||||
| the homeserver's copy of its OIDC secret | `hive-matrix-oidc-secret.service`, same shape | `/var/lib/tuwunel-oidc/<id>.secret`, handed to tuwunel through `LoadCredential` |
|
||||
| the agent containers' queue credential | authelia, published to the store by `swarm-secret-publish` | `<deploy.hive-controller.queue.agentCredentialDir>/secret` (`0600`) and `/client_id` (`0644`) |
|
||||
| secret | generated by | lives at |
|
||||
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------- |
|
||||
| hive CA cert + key | `hive-tls.nix` first-boot unit | `<deploy.hive-controller.tls.stateDir>/ca.pem`, `ca-key.pem` (`0600`) |
|
||||
| hive leaf certs | `hive-tls.nix`, signed by the hive CA | `<deploy.hive-controller.tls.stateDir>/<name>.pem` |
|
||||
| matrix appservice token | `swarm-secret-publish`, published to the store; a host activation script only as a first-boot fallback | `/var/lib/hyperhive/matrix-appservice-token` (`0600`) |
|
||||
| the forge's copy of its OIDC secret | `hive-forge-oidc-secret.service` copies it from authelia's tree | `/var/lib/forgejo-oidc/<id>.secret` inside the forge container |
|
||||
| the homeserver's copy of its OIDC secret | `hive-matrix-oidc-secret.service`, same shape | `/var/lib/tuwunel-oidc/<id>.secret`, handed to tuwunel through `LoadCredential` |
|
||||
| the agent containers' queue credential | authelia, published to the store by `swarm-secret-publish` | `<deploy.hive-controller.queue.agentCredentialDir>/secret` (`0600`) and `/client_id` (`0644`) |
|
||||
|
||||
Both delivery units wait for authelia's first boot to mint the secret — a
|
||||
bounded wait, 120s — and then **fail loudly** rather than skipping. A silent skip
|
||||
|
|
@ -162,10 +162,32 @@ nobody. Every failure path — no such key, sealed store, unreachable store,
|
|||
empty value — leaves the locally minted token in place, so a hive with no store
|
||||
behaves exactly as it did before.
|
||||
|
||||
The store path is `swarm/hives/<hive>/matrix/appservice-token`. It was
|
||||
`…/matrix/registration-token` while the homeserver still took a shared
|
||||
registration secret; a value left at the old path is read by nothing, and the
|
||||
hive falls back to its local token until someone `put`s the new one.
|
||||
The store path is `swarm/hives/<hive>/matrix/appservice-token`, and the
|
||||
**producer is `swarm-secret-publish`** — the same unit that copies authelia's
|
||||
OIDC secrets in. It mints one token per hive in the swarm's roster and `put`s
|
||||
it there, so the store is the source of truth and every hive converges on the
|
||||
value it holds. The hive's own activation mint is still there, but it's a
|
||||
first-boot fallback now: it fires only when the token file is absent, and the
|
||||
reader overwrites whatever it produced.
|
||||
|
||||
The mint is **idempotent by keeping its own record**, not by asking the store.
|
||||
The publisher's grant is `create`/`update` under `swarm/hives/*` with no
|
||||
`read` — write-only on purpose, so a file-copier can't recover every hive's
|
||||
credentials — which means it can't check whether a hive already has a token.
|
||||
Instead it keeps the value it minted under its `StateDirectory`,
|
||||
`/var/lib/swarm-secret-publish/matrix-appservice-token/<hive>` (`0700` dir,
|
||||
`0600` file), and mints only when that file is missing or empty. The `put`
|
||||
itself runs every time: re-putting the same bytes changes nothing for any
|
||||
reader, while a mint whose publish failed must not be left as a token this
|
||||
host holds and no hive can reach.
|
||||
|
||||
Lose the state directory and the next run mints once more and republishes.
|
||||
That rotates the token, which readers pick up on their next start — nothing
|
||||
that already registered breaks, because the token authenticates the
|
||||
appservice rather than living inside any account it created.
|
||||
|
||||
The path was `…/matrix/registration-token` while the homeserver still took a
|
||||
shared registration secret; a value left at the old path is read by nothing.
|
||||
|
||||
The **second reader** is the agent containers' queue credential:
|
||||
`glue-queue-agent-credential.nix` lands it as two files, the client secret and
|
||||
|
|
|
|||
Loading…
Reference in a new issue