diff --git a/hive-c0re/src/forge.rs b/hive-c0re/src/forge.rs index 06083cb3..d3220428 100644 --- a/hive-c0re/src/forge.rs +++ b/hive-c0re/src/forge.rs @@ -70,17 +70,14 @@ fn token_path(name: &str) -> PathBuf { } /// Probe whether `hive-forge` exists as a nixos-container. Cheap — -/// `nixos-container list` is just a directory scan in /etc. +/// `nixos-container list` is just a directory scan in /etc. Routed +/// through hive-priv: `nixos-container` needs root, and hive-c0re runs +/// unprivileged (privsep). pub async fn is_present() -> bool { - let Ok(out) = Command::new("nixos-container").arg("list").output().await else { + let Ok(stdout) = crate::priv_client::list_containers().await else { return false; }; - if !out.status.success() { - return false; - } - String::from_utf8_lossy(&out.stdout) - .lines() - .any(|l| l.trim() == FORGE_CONTAINER) + stdout.lines().any(|l| l.trim() == FORGE_CONTAINER) } /// Run `forgejo admin ` inside the hive-forge container as the diff --git a/hive-c0re/src/matrix.rs b/hive-c0re/src/matrix.rs index c6047be0..fb7a349a 100644 --- a/hive-c0re/src/matrix.rs +++ b/hive-c0re/src/matrix.rs @@ -11,7 +11,6 @@ use std::path::{Path, PathBuf}; use anyhow::{Context, Result}; use reqwest::StatusCode; -use tokio::process::Command; use crate::coordinator::Coordinator; @@ -55,17 +54,13 @@ fn password_path(name: &str) -> PathBuf { /// Probe whether `hive-matrix` exists as a nixos-container. Cheap — /// `nixos-container list` is just a directory scan in /etc. Same shape -/// as `forge::is_present`. +/// as `forge::is_present` — routed through hive-priv since +/// `nixos-container` needs root and hive-c0re runs unprivileged. pub async fn is_present() -> bool { - let Ok(out) = Command::new("nixos-container").arg("list").output().await else { + let Ok(stdout) = crate::priv_client::list_containers().await else { return false; }; - if !out.status.success() { - return false; - } - String::from_utf8_lossy(&out.stdout) - .lines() - .any(|l| l.trim() == MATRIX_CONTAINER) + stdout.lines().any(|l| l.trim() == MATRIX_CONTAINER) } /// Read `n` cryptographic-quality bytes from `/dev/urandom` and return diff --git a/nix/modules/hive-c0re.nix b/nix/modules/hive-c0re.nix index 5d8fe6bc..9ff67e4c 100644 --- a/nix/modules/hive-c0re.nix +++ b/nix/modules/hive-c0re.nix @@ -507,6 +507,20 @@ in # entry point; systemd starts this service on first connect. after = [ "hive-priv.socket" ]; requires = [ "hive-priv.socket" ]; + # `nixos-container` is a perl script that shells out by bare name to + # nix / nix-env / nix-instantiate (create + update), machinectl + + # systemctl (start/stop), and find / rm / umount / chattr (destroy); + # only nsenter + su are hardcoded. Give the helper exactly those — + # not the whole system profile — on top of the systemd/coreutils/ + # findutils already in the default unit PATH. Without `nixos-container` + # on PATH every container op fails ENOENT, which `build_all` silently + # swallows into an empty list ("no managed containers"). + path = [ + pkgs.nixos-container + pkgs.nix # nix, nix-env, nix-instantiate — create + update + pkgs.util-linux # umount (nsenter is hardcoded in the script) + pkgs.e2fsprogs # chattr + ]; serviceConfig = { ExecStart = "${cfg.package}/bin/hive-priv"; Type = "simple";