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:
parent
e4b170f8c6
commit
48067d07e8
31 changed files with 362 additions and 1233 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -66,7 +66,6 @@ of the cell says how.
|
|||
| `swarm/agents/<agent>/forge-token` | `swarm-controller`, at agent creation and in a pass every 5 minutes over every agent with a store identity | the agent container itself, under its own certificate, fetched to `/run/hive-agent-forge-token/token` | ✅ the controller re-mints when the stored token is missing or no longer matches the forge (last eight characters and scopes) | ✅ the agent re-fetches on a 10-minute timer |
|
||||
| `swarm/agents/<agent>/forge/<label>` | `swarm-controller`, when an operator links an external forge account in the swarm UI | `hive-agent-forge-accounts` in the agent container, under the agent's own certificate, into `<state>/forge-<label>-token` and `forge-<label>.json` | ❌ an operator's token; replaced only by deleting it and linking the label again | ✅ the agent re-fetches on a 2-minute timer |
|
||||
| `swarm/agents/<agent>/github-token` | `swarm-controller`, when an operator links a GitHub account in the swarm UI | `hive-agent-github-token` in the agent container, under the agent's own certificate, into `<state>/github-token` | ❌ an operator's token; replaced only by deleting it and linking it again | ✅ the agent re-fetches on a 2-minute timer |
|
||||
| `swarm/hives/<hive>/matrix/appservice-token` | one minter, on the authelia host | the hive process that presents the token to its homeserver, under the hive's own certificate | must be stated | must be stated |
|
||||
| `swarm/hives/<hive>/matrix/sender-token` | `swarm-controller`, with the swarm's appservice token, for every hive in its directory in a five-minute pass | `swarm-controller` under its own certificate, before it decides whether to mint, and hive-c0re's `stored_sender_token()`, under the hive's own certificate | ✅ the controller's pass re-mints when the stored token is missing, unknown to the homeserver, or someone else's | ✅ hive-c0re's matrix sweep reads the store every run and overwrites its token file when the store's token differs |
|
||||
| `swarm/hives/<hive>/queue/agent` | authelia | `swarm-bao-queue-agent` on the hive's host, under its own per-hive certificate; no agent's policy reaches it | must be stated | must be stated |
|
||||
| `swarm/services/<clientId>/oidc/client` | authelia | that service's own reader, under its own certificate; the bao UI's by the store's granter | must be stated | must be stated |
|
||||
|
|
|
|||
|
|
@ -90,8 +90,7 @@ with nothing to authenticate with still receives every hive's telemetry —
|
|||
only its own pushes to the stores go out unauthenticated and get refused
|
||||
there. `swarm-bao-otel-oidc.service` therefore doesn't render at all without
|
||||
`deploy.bao.otelOidcClientCertFile` / `otelOidcClientKeyFile`, the same choice
|
||||
`glue-matrix-bao-token.nix` and `glue-queue-agent-credential.nix` make for
|
||||
their own optional readers, rather than the hard assertion `swarm-grafana.nix`
|
||||
`glue-queue-agent-credential.nix` makes for its own reader, rather than the hard assertion `swarm-grafana.nix`
|
||||
uses for its non-optional one. The operator can still deliver the secret by
|
||||
hand, at `services.hyperhive.deploy.swarm-otel.clientSecretFile`, on a
|
||||
collector this unit never reaches.
|
||||
|
|
@ -159,7 +158,6 @@ when the system builds. The server names the offending file and refuses to run.
|
|||
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------- |
|
||||
| hive leaf certs | `hive-tls.nix`, issued by the secret store's `pki` mount | `<deploy.hive-controller.tls.stateDir>/<name>.pem` |
|
||||
| the hive's own store identity | `glue-bao-tls.nix` on the store's host; placed by hand on every other host ([`ca.md`](ca.md#the-gateway-leaf-comes-from-the-secret-store)) | `deploy.hive-controller.tls.baoHiveIdentityCertFile` / `baoHiveIdentityKeyFile` |
|
||||
| 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`) |
|
||||
|
|
@ -169,50 +167,7 @@ bounded wait, 120s — and then **fail loudly** rather than skipping. A silent s
|
|||
produces a service whose login button always fails, which is a symptom many
|
||||
layers from its cause.
|
||||
|
||||
The store's **first reader** is the matrix appservice token, and it's worth
|
||||
saying why that one: it's an opaque 32-byte value with no second file and no
|
||||
format. Authelia's OIDC secret needs a `.secret` _and_ a matching `.digest`, so
|
||||
starting there would have meant debugging "can a reader authenticate and get
|
||||
bytes back" and "is authelia's file format right" at once, with an
|
||||
SSO outage as the failure mode.
|
||||
|
||||
`glue-matrix-bao-token.nix` fetches it, writes the file `hive-matrix.nix`
|
||||
already reads, and then runs that module's own renderer to re-stamp the
|
||||
appservice registration naming the token — so the homeserver never learns the
|
||||
store exists. The re-render isn't housekeeping: the token is half an
|
||||
agreement, and a registration carrying the previous value authenticates
|
||||
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`, 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 leave behind 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; nothing reads a value left at the old path.
|
||||
|
||||
The **second reader** is the agent containers' queue credential:
|
||||
The store's **first reader** is the agent containers' queue credential:
|
||||
`glue-queue-agent-credential.nix` lands it as two files, the client secret and
|
||||
the client id it authenticates, because that's how a queue client reads them —
|
||||
the secret by path, the id by value. No local fallback exists, and none is
|
||||
|
|
@ -237,7 +192,7 @@ never reads the bytes at any point: it runs as `hive-core`, which is the reason
|
|||
a copy wasn't an option either. When the files aren't there the daemon forwards
|
||||
nothing and says so in its journal, and the container starts without them.
|
||||
|
||||
The **third reader** is Grafana's OIDC client secret, and it's the first one
|
||||
The **second reader** is Grafana's OIDC client secret, and it's the first one
|
||||
that's a _swarm service's_ own credential rather than a hive's. It has exactly
|
||||
one delivery route: `swarm-bao-grafana-oidc.service` reads
|
||||
`swarm/services/<client-id>/oidc/client` out of the store, on **every** host
|
||||
|
|
@ -265,7 +220,7 @@ nothing said anything. Registration is separate and stays where authelia is
|
|||
so it's declared on the host running authelia, which isn't necessarily the host
|
||||
running Grafana.
|
||||
|
||||
The **fourth reader** is the swarm collector's own OIDC client secret, the
|
||||
The **third reader** is the swarm collector's own OIDC client secret, the
|
||||
same shape one level down: `swarm-bao-otel-oidc.service` reads
|
||||
`swarm/services/<client-id>/oidc/client` out of the store on every host that
|
||||
runs the collector, and registration lives separately in
|
||||
|
|
@ -275,13 +230,12 @@ telemetry with none, so `swarm-otel.nix` doesn't assert
|
|||
`deploy.bao.otelOidcClientCertFile` / `otelOidcClientKeyFile` the way Grafana
|
||||
does — it
|
||||
doesn't render the reading unit without them, the shape
|
||||
`glue-matrix-bao-token.nix` and `glue-queue-agent-credential.nix` use for
|
||||
their own optional reads.
|
||||
`glue-queue-agent-credential.nix` uses for its own read.
|
||||
|
||||
The **fifth reader** is the secret store forwarder's OIDC client secret:
|
||||
The **fourth reader** is the secret store forwarder's OIDC client secret:
|
||||
`swarm-bao-forwarder-oidc.service`, on the store's own host, registered by
|
||||
`glue-swarm-bao-otel-oidc-client.nix` on authelia's. It differs from the
|
||||
fourth in both directions. It renders unconditionally, because the export it
|
||||
third in both directions. It renders unconditionally, because the export it
|
||||
authenticates has no unauthenticated mode to degrade into — and it orders
|
||||
itself **after** `container@swarm-bao`, because the store it reads runs in the
|
||||
container it delivers into. Nothing circular sits behind that: the identity it
|
||||
|
|
@ -340,59 +294,55 @@ next controller restart.
|
|||
### Per-principal identities
|
||||
|
||||
Bao matches a cert-auth role on the certificate's subject, so a certificate is an
|
||||
identity and sharing one merges the identities. These five units read one path
|
||||
identity and sharing one merges the identities. These four units read one path
|
||||
each and each holds a leaf, a role and a policy of its own:
|
||||
|
||||
| unit | option pair under `deploy.bao.` | reads |
|
||||
| -------------------------- | ------------------------------------------------------------ | -------------------------------------------------------- |
|
||||
| `swarm-bao-matrix-token` | `matrixTokenClientCertFile` / `matrixTokenClientKeyFile` | `swarm/hives/<hive>/matrix/appservice-token` |
|
||||
| `swarm-bao-queue-agent` | `queueAgentClientCertFile` / `queueAgentClientKeyFile` | `swarm/hives/<hive>/queue/agent` |
|
||||
| `swarm-bao-grafana-oidc` | `grafanaOidcClientCertFile` / `grafanaOidcClientKeyFile` | `swarm/services/<grafana client id>/oidc/client` |
|
||||
| `swarm-bao-otel-oidc` | `otelOidcClientCertFile` / `otelOidcClientKeyFile` | `swarm/services/<collector client id>/oidc/client` |
|
||||
| `swarm-bao-forwarder-oidc` | `forwarderOidcClientCertFile` / `forwarderOidcClientKeyFile` | `swarm/services/<store forwarder client id>/oidc/client` |
|
||||
|
||||
The first two exist **per hive**, because the path they read carries a hive
|
||||
name and every hive runs its own reader. Their subjects are
|
||||
`<deploy.bao.matrixTokenCommonNamePrefix>-<hive>` and
|
||||
`<deploy.bao.queueAgentCommonNamePrefix>-<hive>`; `swarm.nix` reserves both
|
||||
composed spellings as hive names, so nobody can name a hive into another hive's
|
||||
The first exists **per hive**, because the path it reads carries a hive name
|
||||
and every hive runs its own reader. Its subject is
|
||||
`<deploy.bao.queueAgentCommonNamePrefix>-<hive>`; `swarm.nix` reserves that
|
||||
composed spelling as a hive name, so nobody can name a hive into another hive's
|
||||
role. The other three read a path that names a swarm service rather than a hive,
|
||||
so one role each is enough and their subjects are the flat
|
||||
`deploy.bao.grafanaOidcCommonName`, `deploy.bao.otelOidcCommonName` and
|
||||
`deploy.bao.forwarderOidcCommonName`.
|
||||
|
||||
On a host that mints its own PKI, `glue-bao-tls.nix` signs all five and defaults
|
||||
all ten options, and there is nothing to do. Elsewhere you issue each leaf from
|
||||
On a host that mints its own PKI, `glue-bao-tls.nix` signs all four and defaults
|
||||
all eight options, and there is nothing to do. Elsewhere you issue each leaf from
|
||||
that CA out of band and name it here — one file per principal rather than one file
|
||||
shared by five, which is the whole of what this buys.
|
||||
shared by four, which is the whole of what this buys.
|
||||
|
||||
On a host without the store, where the fifth never runs, forgetting one of the
|
||||
other eight isn't a quiet degrade. Three of the four
|
||||
readers used to render only where their leaf existed, so a hand-configured
|
||||
remote-store hive that named the hive's own `clientCertFile` and missed a
|
||||
principal's pair just lost that unit; `swarm-grafana.nix` was alone in refusing
|
||||
the build. All four now refuse it, each naming its own option pair — see
|
||||
On a host without the store, where the forwarder never runs, forgetting one of
|
||||
the other six isn't a quiet degrade. A hand-configured remote-store hive that
|
||||
names the hive's own `clientCertFile` and misses a principal's pair has the
|
||||
build refused, by the module of the reader it missed, naming that pair — see
|
||||
[what a missing leaf costs](#what-a-missing-leaf-costs) below.
|
||||
|
||||
### What a missing leaf costs
|
||||
|
||||
Each of the four modules asserts its own pair, on the same condition: **this host
|
||||
Each of the three modules asserts its own pair, on the same condition: **this host
|
||||
already reads the store** — it holds `deploy.bao.clientCertFile` and
|
||||
`clientKeyFile` — **and the consumer is on, and this principal's pair is null**.
|
||||
The refusal names the two options, so an operator acts on the message without
|
||||
opening the nix.
|
||||
|
||||
That condition fires on exactly one shape, the hand-configured remote-store hive
|
||||
that named seven of the eight. It stays silent everywhere else, and each half of
|
||||
that named the hive's own pair and missed a principal's. It stays silent everywhere else, and each half of
|
||||
that matters on its own:
|
||||
|
||||
- **A host with no store identity at all** holds no `clientCertFile` either, so
|
||||
the first clause is false. That's the supported no-store deployment, and also
|
||||
the one an operator passes through while bringing a hive up — neither should
|
||||
fail to evaluate.
|
||||
- **A host that runs the store** gets all eight from `glue-bao-tls.nix` as
|
||||
- **A host that runs the store** gets every pair from `glue-bao-tls.nix` as
|
||||
defaults, so the third clause is false. Nothing to set, nothing to refuse.
|
||||
- **A consumer that's off** — no homeserver, no collector, no Grafana — has no
|
||||
- **A consumer that's off** — no collector, no Grafana — has no
|
||||
unit to skip, so the second clause is false. Only the queue-credential reader
|
||||
has no toggle of its own to check: every hive runs one for its own agents, so
|
||||
reading the store at all is what asks for it.
|
||||
|
|
|
|||
Loading…
Reference in a new issue