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
|
|
@ -223,6 +223,21 @@ pub async fn run_forge_admin(args: &[&str]) -> Result<(String, String)> {
|
|||
check(call(&PrivRequest::RunForgeAdmin { args: owned }).await?)
|
||||
}
|
||||
|
||||
/// Create (`paused: true`) or remove (`paused: false`) the pause marker in
|
||||
/// `agent_name`'s harness dir via hive-priv (running as root).
|
||||
///
|
||||
/// hive-c0re cannot do this itself: the harness dir is chowned to the agent
|
||||
/// user on the container's first boot and left mode 0755, so this process
|
||||
/// can stat the marker (that's what `Coordinator::is_paused` does) but gets
|
||||
/// `EACCES` on create *and* unlink. Both directions are idempotent.
|
||||
pub async fn set_agent_paused(agent_name: &str, paused: bool) -> Result<()> {
|
||||
ok(call(&PrivRequest::SetAgentPaused {
|
||||
agent_name: agent_name.to_owned(),
|
||||
paused,
|
||||
})
|
||||
.await?)
|
||||
}
|
||||
|
||||
/// Write the Forgejo access token for `agent_name` to
|
||||
/// `<agent_state_root>/<agent_name>/state/forge-token` via hive-priv
|
||||
/// (running as root). The file is written 0600 and chowned to the agent
|
||||
|
|
|
|||
Loading…
Reference in a new issue