A hive publishing its own status needs the same connect the controller already has - mint an authelia token, present it at CONNECT for the callout responder, let async-nats re-run the callback per attempt. Only the use differs: the controller reads, a hive writes. Copying it would put credential handling in two places, and a token-refresh fix would then have to be found twice. That is the same reasoning that already put hive-sock-client in its own crate rather than in each daemon that speaks to a unix socket. `from_env` takes a prefix rather than hardcoding SWARM_CONTROLLER_*: the variables belong to the consuming unit, since a NixOS module sets them alongside its other options. What is shared is the RULE - all four together or none at all - not the spelling. The half-set case gains a test, because it is the case the rule exists for and it previously had none. No jetstream/kv feature on the crate: it ends at a connected client, and what a consumer does with it should be visible in that consumer's own Cargo.toml. Behaviour-preserving, and proven that way rather than by inspection: the full behavioural gate (real nats-server, credential rotation, mutation) is 20/0 unchanged, and the controller's own tests still pass.
55 lines
2.5 KiB
Markdown
55 lines
2.5 KiB
Markdown
# swarm-queue-client
|
|
|
|
Connecting to the swarm message queue as an authenticated client. Shared by
|
|
every process that participates: the swarm controller reads hive status out of
|
|
the queue, a hive publishes its own status into it.
|
|
|
|
## Why a crate and not a module per binary
|
|
|
|
The *connect* is identical for every participant — mint an authelia token,
|
|
present it at CONNECT for the `auth_callout` responder to introspect, let
|
|
`async-nats` re-run the callback on each connection attempt. Only the **use**
|
|
differs.
|
|
|
|
Two copies of that would be two copies of credential handling, and a
|
|
token-refresh fix would have to be found twice. The same reasoning already put
|
|
`hive-sock-client` in its own crate rather than in each daemon that speaks to a
|
|
unix socket.
|
|
|
|
## The two properties that constrain the code
|
|
|
|
**A token expires.** Authelia issues `client_credentials` access tokens with
|
|
`expires_in: 3599`. Authentication happens at CONNECT, so a long-lived
|
|
connection is fine — but a *reconnect* an hour later needs a token minted an
|
|
hour later.
|
|
|
|
**The refresh therefore lives in the auth callback, not in a timer.**
|
|
`async-nats` invokes it per connection attempt, so there is no window in which
|
|
the client holds a token it minted for a previous connection. The alternative —
|
|
mint once, own the reconnect loop — fails in the way this subsystem exists to
|
|
prevent: the process keeps serving while its data quietly stops moving, and
|
|
nothing says so until someone reads a dashboard.
|
|
|
|
## Configuration
|
|
|
|
`QueueConfig::from_env(prefix)` reads `<prefix>_NATS_URL`,
|
|
`<prefix>_OIDC_TOKEN_ENDPOINT`, `<prefix>_OIDC_CLIENT_ID` and
|
|
`<prefix>_OIDC_CLIENT_SECRET_FILE`.
|
|
|
|
The prefix is a parameter because the variables belong to the consuming unit —
|
|
a NixOS module sets them alongside its other options. What is shared is the
|
|
rule, not the spelling: **all four together or none at all.** A half-set
|
|
environment is a hard error, because the failure it would otherwise produce is
|
|
the expensive kind — the process comes up "fine", never connects, and the data
|
|
it was supposed to move silently stops.
|
|
|
|
The client secret is a **path, not a value**: putting it in the environment
|
|
would publish it to anything that can read `/proc/<pid>/environ`. It is read
|
|
per token request rather than cached, so a rotation the operator believes took
|
|
effect actually did.
|
|
|
|
## What this crate does not do
|
|
|
|
It ends at a connected client. No `jetstream`/`kv` feature is enabled here —
|
|
what a consumer does with the connection is its own business, and its
|
|
`Cargo.toml` is where that requirement should be visible.
|