Watch
0
0
Fork
You've already forked hyperhive
0

hive-agent: the swarm queue connection is required; retry, then exit

An agent's harness connects to the swarm queue with the agent's own
credential or not at all. The first connect retries after 5s, doubling
(7 attempts, about five minutes), with a warning per failed attempt;
when every attempt fails the harness logs an error and exits 1. Missing
coordinates (HIVE_AGENT_NATS_URL, or the secret store's BAO_ADDR /
HIVE_AGENT_NAME / certificate) fail the boot outright.

Previously the first connect's outcome was memoized for the life of the
process: a store 403 or queue refusal at boot left the agent on the
hive's shared client, or disconnected, until someone restarted it, and
an agent with no queue coordinates ran on without one. Both paths are
gone, with the hive-scoped subjects that only the shared client used.

The harness unit now restarts after 30s instead of 2s, so a swarm-wide
outage doesn't restart every agent every 2s, and has no start limit, so
it keeps coming back however long the outage lasts.

The hive's shared client credential is still imported into the harness
unit for swarm-logs; the two HIVE_AGENT_OIDC_*_FILE variables that
pointed the harness at it are dropped, as is the unset
HIVE_AGENT_OIDC_CA_FILE override.

With the fallback gone, the pieces #4944 added only for it go too:
swarm_input's skip-on-shared-client branch and swarm-queue-client's
connect_with_inbox_prefix. swarm-nats-auth's grant for the hive's shared
agent client is unchanged.

Closes #4909
This commit is contained in:
atlas 2026-10-04 19:05:48 +02:00 • committed by mara
commit 235bc481b3
16 changed files with 392 additions and 964 deletions

View file

@ -10,9 +10,7 @@
//! An agent with no icon deletes its key, so an icon removed from the config
//! also disappears from the swarm.
//!
//! **Only under the agent's own credential.** That credential is granted
//! exactly this agent's key. The hive's shared client is granted none of the
//! bucket, so under it this publishes nothing.
//! The agent's own queue credential is granted exactly this agent's key.
//!
//! The write is a `JetStream` publish straight to the key's subject, the same
//! message `kv::Store::put` sends, without opening the bucket first. That
@ -25,8 +23,6 @@ use std::time::Duration;
use async_nats::jetstream::context::PublishErrorKind;
use crate::swarm_queue::{Connection, Presented};
/// Where the agent's NixOS config puts the icon
/// (`services.hyperhive.agent.icon`), and the file `GET /icon` serves. Absent
/// when none is configured.
@ -63,20 +59,8 @@ fn read_icon(path: &Path) -> std::io::Result<Icon> {
}
}
/// The subject this agent's icon is written to, or `None` under a credential
/// that is not granted it.
fn subject(presented: &Presented, agent: &str) -> Option<String> {
match presented {
Presented::Agent => Some(swarm_queue_client::agent_icon::subject(agent)),
Presented::Hive { .. } => None,
}
}
/// Start the publish task, if this agent has a queue credential and a label.
/// Start the publish task, if this agent has a label.
pub fn spawn() {
if !crate::swarm_queue::configured() {
return;
}
let agent = crate::identity::label();
if agent.is_empty() {
tracing::warn!("this agent has no label; not publishing its icon upward");
@ -86,16 +70,8 @@ pub fn spawn() {
}
async fn run(agent: String) {
let Some(Connection { client, presented }) = crate::swarm_queue::client().await else {
return;
};
let Some(subject) = subject(&presented, &agent) else {
tracing::info!(
"connected with the hive's shared client, which may not write an agent's \
icon; not publishing it upward"
);
return;
};
let client = crate::swarm_queue::client().await;
let subject = swarm_queue_client::agent_icon::subject(&agent);
let icon = match read_icon(Path::new(ICON_PATH)) {
Ok(icon) => icon,
Err(e) => {
@ -168,25 +144,7 @@ async fn write(
#[cfg(test)]
mod tests {
use super::{Icon, Presented, read_icon, subject};
#[test]
fn under_its_own_credential_an_agent_writes_its_own_key() {
assert_eq!(
subject(&Presented::Agent, "atlas").as_deref(),
Some("$KV.agent-icons.atlas")
);
}
/// The hive's shared client is granted no icon key, so a publish under it
/// would be refused.
#[test]
fn under_the_hives_shared_client_nothing_is_written() {
let hive = Presented::Hive {
client_id: "hive-alpha-agent".to_owned(),
};
assert_eq!(subject(&hive, "atlas"), None);
}
use super::{Icon, read_icon};
#[test]
fn a_configured_icon_is_published_byte_for_byte() {