Watch
0
0
Fork
You've already forked hyperhive
0

hive-agent: read the per-agent queue secret from bao in process

The harness now reads swarm/agents/<agent>/queue from the store itself,
under the agent's own store certificate, and holds it in memory only.
It reads once before the first connect and again on every reconnect
attempt (async-nats `ConnectOptions::with_auth_callback`), so an agent
whose secret was re-minted reconnects with the new value instead of
being refused until the container restarts.

hive-agent-queue-credential.service, the /run file it wrote, and
HIVE_AGENT_QUEUE_AGENT_SECRET_FILE are gone; queue-identity.nix now
hands hive-agent.service the store address, its certificate paths and
the agent name.

A failed or empty read before the first connect still falls back to
the hive's shared client. Each read is bounded by a 10s timeout, and
retries wait out the existing reconnect backoff (500ms doubling, capped
at 60s).

Closes #4783
This commit is contained in:
atlas 2026-09-29 09:40:48 +02:00 • committed by mara
commit ccb5bd3b38
10 changed files with 770 additions and 529 deletions

View file

@ -5,15 +5,14 @@
# to `swarm/agents/<agent>/forge-token`
# (`swarm_secret_client::forge::agent_token_path`); no hive is in that chain.
# This unit logs in to the store with the certificate ./bao.nix already proves
# it can log in with, and reads its own path. The shape is ./queue-identity.nix's,
# for the same reason: a hive handing the token over would be the hive reading
# a secret on the agent's behalf.
# it can log in with, and reads its own path. A hive handing the token over
# would be the hive reading a secret on the agent's behalf.
#
# Unlike the queue credential this one rotates: the controller replaces the
# token when the forge's copy stops matching the stored one. So the unit is
# re-run by a timer, and it swaps the file in by rename, only when the value
# changed, so a reader never sees half a token and a watcher on the file
# (forge-avatar-sync.path) fires only on a real change.
# The token rotates: the controller replaces it when the forge's copy stops
# matching the stored one. So the unit is re-run by a timer, and it swaps the
# file in by rename, only when the value changed, so a reader never sees half
# a token and a watcher on the file (forge-avatar-sync.path) fires only on a
# real change.
#
# Consumers read `services.hyperhive.agent.forge.tokenFile` first and fall back
# to `<state>/forge-token`, the file the hive wrote before this existed.
@ -79,7 +78,9 @@ in
description = "fetch this agent's own forge token from the secret store";
after = [
"network.target"
# Ordering only, for the reason ./queue-identity.nix gives.
# Ordering only: that unit reports a store this agent cannot reach as
# itself, and should get to say so before this one reports a path it
# could not read.
"hive-agent-bao-identity.service"
];
before = [ "hive-forge-notify.service" ];
@ -93,10 +94,10 @@ in
startLimitIntervalSec = 300;
serviceConfig = {
Type = "oneshot";
# Not `RemainAfterExit`, unlike the queue fetch: the timer below has to
# be able to start this unit again, and an active unit cannot be
# started. `RuntimeDirectoryPreserve` is what keeps the directory, and
# the token in it, alive between runs instead.
# Not `RemainAfterExit`: the timer below has to be able to start this
# unit again, and an active unit cannot be started.
# `RuntimeDirectoryPreserve` is what keeps the directory, and the token
# in it, alive between runs instead.
RemainAfterExit = false;
TimeoutStartSec = 30;
Restart = "on-failure";
@ -167,8 +168,8 @@ in
fi
export BAO_TOKEN
# 🩸 Degrades rather than fails, for the reason ./queue-identity.nix
# gives: the policy stanza that let the login read `bao-mtls` covers
# 🩸 Degrades where ./bao.nix's check fails, because one policy stanza
# governs both reads: the one that let the login read `bao-mtls` covers
# this path too, so a refusal here is a token not minted yet. The
# file already in place, if any, is kept: a store that is briefly
# unreachable must not take a working token away.