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:
parent
4f9e9bd680
commit
89013b5408
11 changed files with 329 additions and 51 deletions
|
|
@ -20,6 +20,7 @@
|
|||
//! [`Read::Failed`]: the caller checks against `HIVE_AVAILABLE_MODELS` and
|
||||
//! applies no cap.
|
||||
|
||||
use std::path::Path;
|
||||
#[cfg(test)]
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::{Arc, OnceLock};
|
||||
|
|
@ -27,7 +28,7 @@ use std::time::Duration;
|
|||
|
||||
use hive_sh4re::permissions::Permission;
|
||||
use swarm_queue_client::agent_inference::{self, AgentInference, read_own};
|
||||
use swarm_queue_client::agent_permissions;
|
||||
use swarm_queue_client::agent_permissions::{self, HIVE_COPY_PATH, HiveCopy};
|
||||
|
||||
use crate::swarm_term::{Target, connect};
|
||||
|
||||
|
|
@ -36,14 +37,16 @@ use crate::swarm_term::{Target, connect};
|
|||
static LAST_PERMISSIONS: std::sync::RwLock<Option<Vec<Permission>>> = std::sync::RwLock::new(None);
|
||||
|
||||
/// The permissions this agent holds now, for the subagent being spawned: the
|
||||
/// last set read, or [`Permission::DEFAULT`] before any read succeeds.
|
||||
/// last set read, or what hive-c0re stored for the agent before any read
|
||||
/// succeeds ([`HiveCopy::before_read`]).
|
||||
#[must_use]
|
||||
pub fn permissions_now() -> Vec<Permission> {
|
||||
LAST_PERMISSIONS
|
||||
let last = LAST_PERMISSIONS
|
||||
.read()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.clone()
|
||||
.unwrap_or_else(|| Permission::DEFAULT.to_vec())
|
||||
.clone();
|
||||
let agent = std::env::var(crate::swarm_term::ENV_AGENT_NAME).unwrap_or_default();
|
||||
last.unwrap_or_else(|| HiveCopy::read_at(Path::new(HIVE_COPY_PATH)).before_read(&agent))
|
||||
}
|
||||
|
||||
/// How long after a failed connect the task tries again.
|
||||
|
|
@ -147,8 +150,8 @@ impl Reader {
|
|||
}
|
||||
|
||||
/// Read the permissions the swarm grants this agent and keep them for
|
||||
/// [`permissions_now`]: its list, or the default for an unset key. A read
|
||||
/// that fails keeps the last value.
|
||||
/// [`permissions_now`], judged by [`HiveCopy::after_read`]. A read that
|
||||
/// fails keeps the last value.
|
||||
pub async fn refresh_permissions(&self) {
|
||||
let read = match &self.source {
|
||||
Source::Queue { agent, client } => read_permissions(agent, client).await,
|
||||
|
|
@ -170,11 +173,9 @@ async fn read_permissions(
|
|||
let client = client.get()?;
|
||||
swarm_queue_client::ensure_connected(client).ok()?;
|
||||
match tokio::time::timeout(READ_TIMEOUT, agent_permissions::read(client, agent)).await {
|
||||
Ok(Ok(agent_permissions::Read::Current(record))) => Some(
|
||||
record
|
||||
.permissions
|
||||
.unwrap_or_else(|| Permission::default_for(agent)),
|
||||
),
|
||||
Ok(Ok(agent_permissions::Read::Current(record))) => {
|
||||
Some(HiveCopy::read_at(Path::new(HIVE_COPY_PATH)).after_read(agent, &record))
|
||||
}
|
||||
Ok(Ok(agent_permissions::Read::NoBucket)) => None,
|
||||
Ok(Err(e)) => {
|
||||
tracing::warn!(error = %e, "subagent permissions: reading this agent's key failed");
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ use tokio::sync::mpsc;
|
|||
|
||||
/// Names the agent. The store role, the store path, the queue token and the
|
||||
/// subjects are all built from it.
|
||||
const ENV_AGENT_NAME: &str = "HIVE_AGENT_NAME";
|
||||
pub(crate) const ENV_AGENT_NAME: &str = "HIVE_AGENT_NAME";
|
||||
|
||||
/// Where the swarm queue listens, as this container reaches it.
|
||||
const ENV_NATS_URL: &str = "HIVE_AGENT_NATS_URL";
|
||||
|
|
|
|||
Loading…
Reference in a new issue