swarm-controller: accept an agent's external matrix account and put it in the store
The swarm UI had nowhere to POST an external matrix account to: this daemon had no matrix-account code at all and no `swarm-secret-client` dependency, so the last leg of #3726 — a credential reaching an agent — had no entry point. `PUT /api/hives/{hive}/agents/{agent}/matrix-accounts/{account}` writes the credential to the store under the agent's own path and publishes a `CredentialNotice` on that hive's credential subject. All three path names are load-bearing: agent + account locate the secret, hive routes the notice. The account is a path segment rather than a body field so that splitting the 1:1 account-to-agent mapping later is a new route, not a changed payload. Store first, notify second, and the order cannot be swapped: a notice that overtakes its own write reaches a hive that reads nothing, and the hive deliberately does not retry. The publish is followed by a flush for the reason `publish_deploy` flushes — `publish` hands the message to the connection's write buffer and returns, so the response could otherwise outrun the notice it reports as sent. The store client is built per request rather than held in `AppState`, matching what the hive side does inside `deliver`: a login that expires is not worth caching for a route this cold. `swarm_hive` is `declaration_target`'s two name checks, extracted so this handler makes them identically rather than in a second copy free to drift. `declaration_target` still tests the writer first, so a deployment with no queue answers 503 whatever the caller spelled. ## The nix half #4081 minted the controller's leaf and gave it `baoClientCertFile` / `baoClientKeyFile`, deliberately stopping there — the leaf is minted whether or not a controller runs on that host. Nothing consumed those options, so the identity never reached the process. Measured before writing: `git grep baoClientCertFile` returned 5 sites and zero consumers, against a control (`tokenEndpoint`, 4 hits in the same file) proving the search can see consumption where it exists. The unit now gets `BAO_ADDR` / `BAO_CLIENT_CERT` / `BAO_CLIENT_KEY` / `BAO_CACERT` and the matching `LoadCredential` entries, following `hive-c0re/environment.nix`'s `%d` credential shape. The gate is `deploy.swarm-controller.baoClientCertFile`, NOT `deploy.bao.clientCertFile`. The latter is the hive reader's identity and its policy scopes a hive's own secrets; wiring it here would evaluate, deploy, and fail only when the daemon tried to write an agent's credential. Two `module-eval` arms cover exactly that. The presence arm asserts the `LoadCredential` *source path* (`…:/var/lib/swarm-bao-pki/controller.pem`) and not just the `%d` name, because a `%d`-only assertion passes while the daemon holds the wrong policy. The absence arm (`controllerNoStore`) is what makes the presence arm mean anything. `RestrictAddressFamilies` already covers the store client; its own comment asks for the family to be added with the client, and AF_INET/AF_INET6 are present. Contributes to #3726
This commit is contained in:
parent
e263681f1d
commit
0d88ca5e7f
6 changed files with 252 additions and 14 deletions
|
|
@ -85,6 +85,11 @@ swarm-authelia-bridge-sock.workspace = true
|
|||
# not get to declare them privately.
|
||||
#
|
||||
swarm-queue-client = { workspace = true, features = ["kv"] }
|
||||
# `matrix_account.rs` writes the credential this daemon's route accepts. Same
|
||||
# crate the hive reads it back with, which is the point: the path, the field
|
||||
# name and the object's shape are agreements between the two ends, and a
|
||||
# second spelling here would be a store this hive could not read.
|
||||
swarm-secret-client.workspace = true
|
||||
# `otel_http_client.rs`'s `AuthenticatedHttpClient` — an
|
||||
# `opentelemetry_http::HttpClient` impl authenticated with this crate's own
|
||||
# `swarm-queue-client` identity. Lives in this crate rather than
|
||||
|
|
|
|||
Loading…
Reference in a new issue