Watch
0
0
Fork
You've already forked hyperhive
0

agent permissions: hold hive-c0re's stored grant until the agent's own read

Until its first successful read of its key, the harness read its
permissions as the default set, `execution` included. So did the
subagent daemon. An agent the operator had narrowed ran wider than
granted after every restart while the queue was unreachable, and in the
gap before the first 10 s poll. During the meta-repo import, every
non-ruth key is still unset, and the agent read that as the default too,
for as long as the import stayed pending.

mara picked option (a) on #5293: "a, removal of the old path as a follow
up in 1w".

hive-c0re now writes each agent's entry from
swarm-agent-permissions.json into the agent's runtime dir as
swarm-permissions.json (/run/hive in the container), together with
whether the import is still pending. It writes the file at every start
(write_dropins) and after every 30 s pass of the permissions worker. The
harness and the subagent daemon hold that stored list until their own
first read succeeds. While the import is pending, an unset key keeps
the stored list rather than reverting it to the default. A queue that
is down never blocks a start: the agent runs on the stored grant.

The decision lives in swarm_queue_client::agent_permissions::HiveCopy,
shared by both readers.

Part of #5170.
This commit is contained in:
atlas 2026-10-15 13:39:51 +02:00 • committed by mara
commit 89013b5408
11 changed files with 329 additions and 51 deletions

View file

@ -1,7 +1,7 @@
//! Per-container host-side config: the nspawn conf rewrite (bind mounts,
//! network isolation, forwarded credentials), the systemd resource-limits
//! drop-in, the agent's copy of its memory cap, and the `write_dropins` verb
//! that re-applies them.
//! drop-in, the agent's copies of its memory cap and stored permissions, and
//! the `write_dropins` verb that re-applies them.
use std::path::{Path, PathBuf};
@ -17,14 +17,16 @@ use super::{
};
/// Re-apply the per-container host-side config: nspawn flags (bind
/// mounts etc.), the systemd resource-limits drop-in, the agent's copy of
/// its memory cap, and a daemon reload so all of it takes effect on the next
/// unit (re)start. Idempotent — the job queue's `WriteDropin` node, also
/// folded into every `Swap` (rebuild is the reconcile verb).
/// mounts etc.), the systemd resource-limits drop-in, the agent's copies of
/// its memory cap and stored permissions, and a daemon reload so all of it
/// takes effect on the next unit (re)start. Idempotent — the job queue's
/// `WriteDropin` node, also folded into every `Swap` (rebuild is the
/// reconcile verb).
pub async fn write_dropins(name: &str, hive: &HiveEnv, paths: &AgentPaths) -> Result<()> {
validate(name)?;
let container = container_name(name);
set_nspawn_flags(&container, &paths.agent, &paths.claude, &paths.notes).await?;
hand_permissions(name, &paths.agent);
if let Some((cpu_quota, memory_max)) = stage_limits(
name,
&crate::resource_limits::swarm_limits_path(),
@ -397,6 +399,22 @@ fn holds_manage_root_agent(agent_name: &str, path: &Path) -> bool {
})
}
/// Copy `agent`'s stored permissions into its runtime dir, which it holds
/// until its own first read of the swarm's key succeeds. A failure is logged
/// and the agent still starts, on the default.
pub fn hand_permissions(agent: &str, runtime_dir: &Path) {
let pending = crate::workers::permissions_import::pending();
if let Err(e) =
crate::permissions::hand_at(&crate::permissions::path(), pending, agent, runtime_dir)
{
tracing::error!(
agent = %agent,
error = ?e,
"could not hand the agent its stored permissions; it holds the default until it reads its key"
);
}
}
/// Env var naming the host directory `swarm-bao-queue-agent.service` lands
/// this hive's agent queue credential in. Set by the hive-c0re NixOS module
/// from `deploy.hive-controller.queue.agentCredentialDir`; absent means this

View file

@ -11,7 +11,7 @@ pub use git::{
git, git_authed, git_command, git_command_authed, git_is_ancestor, git_read_tree_reset,
git_rev_parse, git_tag, git_update_ref, git_update_ref_cas,
};
pub use host_config::write_dropins;
pub use host_config::{hand_permissions, write_dropins};
pub use setup::{
ensure_agent_state_subvolume, ensure_claude_dir, ensure_state_dir, initial_flake_nix,
setup_applied, setup_proposed,