diff --git a/hive-c0re/src/forge.rs b/hive-c0re/src/forge.rs index b08440c8..4fe27d5d 100644 --- a/hive-c0re/src/forge.rs +++ b/hive-c0re/src/forge.rs @@ -335,7 +335,6 @@ async fn mint_and_persist_token(name: &str, path: &Path, scopes: &str) -> Result std::fs::write(path, format!("{token}\n")) .with_context(|| format!("write token to {}", path.display()))?; let _ = std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o600)); - crate::lifecycle::chown_to_agent(name, path, "forge"); tracing::info!(%name, path = %path.display(), "forge: persisted access token"); Ok(()) } diff --git a/hive-c0re/src/lifecycle.rs b/hive-c0re/src/lifecycle.rs index e3051517..c81217a5 100644 --- a/hive-c0re/src/lifecycle.rs +++ b/hive-c0re/src/lifecycle.rs @@ -92,59 +92,6 @@ pub fn is_manager(name: &str) -> bool { name == MANAGER_NAME } -/// Read the agent user's `(uid, gid)` from the container's nixos-managed -/// `/etc/passwd`. Returns `None` when the container hasn't been built -/// yet, the passwd file is unparseable, or the agent user is missing -/// (e.g. legacy pre-#658 container that still runs as root). -/// -/// Used by `forge` + `matrix` after writing per-agent state files so -/// the bind-mounted host file ends up readable by the agent user -/// without waiting for the next container activation to run the chown -/// fixup (#673). -/// -/// Notes: -/// - Reads the *container-local* passwd at -/// `/var/lib/nixos-containers//etc/passwd`, not the host's. -/// The container's user-namespace shares uids with the host (no -/// `PrivateUsers`), so the uid is directly usable in host-side -/// `chown(2)`. -/// - Best-effort: caller treats `None` as "skip the chown". -#[must_use] -pub fn agent_uid_gid(agent_name: &str) -> Option<(u32, u32)> { - let container = container_name(agent_name); - let passwd_path = format!("/var/lib/nixos-containers/{container}/etc/passwd"); - let content = std::fs::read_to_string(&passwd_path).ok()?; - for line in content.lines() { - let mut parts = line.split(':'); - let user = parts.next()?; - if user != agent_name { - continue; - } - let _ = parts.next()?; // x (password placeholder) - let uid: u32 = parts.next()?.parse().ok()?; - let gid: u32 = parts.next()?.parse().ok()?; - return Some((uid, gid)); - } - None -} - -/// Best-effort `chown(path, agent_uid, agent_gid)`. Resolves the agent's -/// uid/gid via [`agent_uid_gid`] and shells out to `std::os::unix::fs::chown`. -/// Silently no-ops when the container isn't built yet (`None` from -/// [`agent_uid_gid`]) and logs at debug on chown syscall failure — the -/// activation script in `harness-base.nix` is the steady-state safety -/// net. Used by per-agent state writers in `forge` + `matrix` so the -/// agent can read the file without waiting for the next container -/// rebuild (#673). -pub fn chown_to_agent(name: &str, path: &Path, subsystem: &str) { - let Some((uid, gid)) = agent_uid_gid(name) else { - return; - }; - if let Err(e) = std::os::unix::fs::chown(path, Some(uid), Some(gid)) { - tracing::debug!(%name, %subsystem, path = %path.display(), error = %e, "chown to agent failed"); - } -} - fn validate(name: &str) -> Result<()> { if name.is_empty() { bail!("agent name must not be empty"); diff --git a/hive-c0re/src/matrix.rs b/hive-c0re/src/matrix.rs index 0e0099b6..c695e17c 100644 --- a/hive-c0re/src/matrix.rs +++ b/hive-c0re/src/matrix.rs @@ -274,7 +274,6 @@ pub async fn ensure_user_for( std::fs::write(&path, format!("{access_token}\n")) .with_context(|| format!("matrix: write token to {}", path.display()))?; let _ = std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o600)); - crate::lifecycle::chown_to_agent(name, &path, "matrix"); tracing::info!(%name, path = %path.display(), "matrix: registered user + persisted access token"); Ok(()) }