hive-c0re: back agent state dirs with btrfs subvolumes

Progressive enhancement: a brand-new agent's state root under
/var/lib/hyperhive/agents is created as a btrfs subvolume when the host
filesystem is btrfs, otherwise it falls back to a plain directory. No
existing agent is auto-migrated — the new path only fires when the root
does not yet exist, so plain-dir agents are left untouched until an
explicit opt-in upgrade.

Two new privileged ops (subvolume create/delete are root-only):
EnsureAgentSubvolume statfs-gates on btrfs, creates the subvolume, and
chowns it to the hive-core user so the normal state/claude/harness
mkdirs succeed inside it; DeleteAgentSubvolume btrfs-subvolume-deletes
the root iff it is actually a subvolume. hive-c0re calls Ensure before
the per-agent dirs are created (spawn/rebuild/InitConfig) and Delete on
the purge path only — destroy keeps the subvolume for revival, matching
plain-dir semantics. btrfs-progs added to the hive-priv unit PATH.

Per-subvolume usage accounting + optional quota is a separate
follow-up.
This commit is contained in:
atlas 2026-06-19 12:28:17 +02:00 committed by mara
commit 1f602d5fda
9 changed files with 242 additions and 0 deletions

View file

@ -315,6 +315,7 @@ async fn run_approval_init_config(
) -> Result<()> {
let result: Result<()> = async {
lifecycle::setup_proposed(&proposed_dir, &approval.agent).await?;
lifecycle::ensure_agent_state_subvolume(&approval.agent).await?;
lifecycle::ensure_claude_dir(&claude_dir)?;
lifecycle::ensure_state_dir(&notes_dir)?;
Ok(())
@ -659,6 +660,13 @@ pub async fn destroy(coord: &Arc<Coordinator>, name: &str, purge: bool) -> Resul
let _ = std::fs::remove_dir_all(&runtime);
}
if purge {
// The state root may be a btrfs subvolume (#1762): a subvolume root
// can't be removed with rmdir/`remove_dir_all`, so delete it via
// hive-priv (root) first. No-op for plain-dir agents — the loop below
// then handles the plain-dir state root plus the applied dir.
if let Err(e) = crate::priv_client::delete_agent_subvolume(name).await {
tracing::warn!(error = ?e, %name, "purge: delete state subvolume failed");
}
for dir in [
Coordinator::agent_state_root(name),
Coordinator::agent_applied_dir(name),