fix(#2733): write the agent pause marker via hive-priv
`Coordinator::set_paused` wrote the marker directly with `std::fs::write`
from hive-c0re, which runs as the unprivileged `hive-core` user. The
agent's harness dir is chowned to the agent user on every container boot
(`user.nix`'s activation chown), mode 0755 — so hive-core can stat the
marker but gets EACCES creating or unlinking it. Pause therefore only
ever worked on an agent that had never booted; the read side works
because a stat needs traverse, not write, which is why the paused pill
and `is_paused` looked healthy.
Route both directions through hive-priv, the root helper that already
owns the other writes into agent-owned directories:
- `PrivRequest::SetAgentPaused { agent_name, paused }`, with the marker
filename constant moved to hive-priv-sock. That is the narrowest crate
all three sides share (hive-priv deliberately does not depend on
hive-sh4re, which re-exports it for the in-container resolver). A
private copy on any one side would break pause silently, since every
reader just sees "no marker".
- `write_agent_state_file` generalised to `write_agent_dir_file`, taking
the target directory: `state/` and `harness/` are both agent-owned,
which is the same reason both need root.
- resume unlinks via `remove_file`, which acts on the leaf and never
follows a symlink — an agent could otherwise plant a link at the
marker path and have root delete an arbitrary file.
`Coordinator::set_paused` becomes an async round-trip; its three call
sites were already async. Both directions stay idempotent because the
dashboard toggle and `hivectl pause|resume` fire without reading the
current state first.
This commit is contained in:
parent
270d3aafa2
commit
de09628c7c
9 changed files with 193 additions and 36 deletions
|
|
@ -15,6 +15,16 @@ use serde::{Deserialize, Serialize};
|
|||
/// Default socket path for the privileged helper.
|
||||
pub const PRIV_SOCK: &str = "/run/hive/priv.sock";
|
||||
|
||||
/// File name of the pause marker inside an agent's harness dir. Defined
|
||||
/// here — the narrowest crate all three sides already share — because the
|
||||
/// marker is a two-sided contract with no protocol behind it: hive-priv
|
||||
/// creates and unlinks it as root, hive-c0re stats it to render the paused
|
||||
/// indicator, and the in-container harness stats it to gate its turn loop
|
||||
/// (via the `hive-sh4re::paths` re-export). A private copy on any one side
|
||||
/// would break pause *silently*, since every reader just sees "no marker" —
|
||||
/// exactly the failure mode a shared constant exists to prevent.
|
||||
pub const PAUSED_MARKER_FILE: &str = "paused";
|
||||
|
||||
/// Manager logical agent name. The manager's system container name is
|
||||
/// `h-ruth` (same `h-` prefix convention as every other agent).
|
||||
pub const MANAGER_NAME: &str = "ruth";
|
||||
|
|
@ -385,6 +395,26 @@ pub enum PrivRequest {
|
|||
args: Vec<String>,
|
||||
},
|
||||
|
||||
// --- Agent turn-loop pause ---
|
||||
// (marker filename: `PAUSED_MARKER_FILE`, defined at the crate root)
|
||||
/// Create (`paused: true`) or remove (`paused: false`) the pause marker
|
||||
/// at `AGENT_STATE_ROOT/<agent_name>/harness/paused`. Its presence parks
|
||||
/// the agent's turn loop; the harness stats it in-container through the
|
||||
/// harness bind-mount.
|
||||
///
|
||||
/// Required because hive-c0re runs unprivileged: the harness dir is
|
||||
/// chowned to the agent user on first container boot (mode 0755), so
|
||||
/// hive-core can stat the marker but cannot create or unlink it. Both
|
||||
/// directions are idempotent — pausing an already-paused agent rewrites
|
||||
/// an empty file, and resuming a running one treats `NotFound` as
|
||||
/// success.
|
||||
SetAgentPaused {
|
||||
/// Logical agent name (validated by `validate_agent_name`).
|
||||
agent_name: String,
|
||||
/// `true` creates the marker, `false` removes it.
|
||||
paused: bool,
|
||||
},
|
||||
|
||||
// --- Agent credential writes ---
|
||||
/// Write `forge-token` into `AGENT_STATE_ROOT/<agent_name>/state/forge-token`.
|
||||
///
|
||||
|
|
|
|||
Loading…
Reference in a new issue