supermachine 0.2.0

Run any OCI/Docker image as a hardware-isolated microVM on macOS HVF (Linux KVM and Windows WHP in progress). Single library API, zero flags for the common case, sub-100 ms cold-restore from snapshot.
//! First-run codesign autopilot.
//!
//! macOS HVF requires the `com.apple.security.hypervisor`
//! entitlement on whatever process calls `hv_vm_create`. In our
//! architecture that's the `supermachine-worker` binary, not the
//! `supermachine` CLI itself.
//!
//! `cargo install supermachine` builds an unsigned worker on the
//! user's machine. To make `cargo install … && supermachine run X`
//! Just Work with no manual setup, the CLI signs the worker with
//! the bundled entitlements plist on its first invocation —
//! transparently, in ~30–50 ms — and writes a sentinel so
//! subsequent invocations skip the signing in ~1 ms.
//!
//! The same path covers the dev-tree case where `cargo build`
//! strips the entitlement on every rebuild: the next CLI launch
//! re-signs automatically.

use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::OnceLock;

/// Sign `worker_path` with the bundled HVF entitlement (ad-hoc),
/// idempotent. Caches the result both in-process (one signing
/// attempt per CLI invocation) and on disk (one across CLI
/// invocations until the worker binary changes).
///
/// Returns `Ok(())` on success or no-op skip; `Err` on codesign
/// failure with a message suitable for the user. Callers should
/// not panic on `Err` — we'd rather let `hv_vm_create` surface
/// its own error than block on a codesign issue.
pub fn ensure_worker_signed(worker_path: &Path) -> Result<(), String> {
    static IN_PROCESS_DONE: OnceLock<()> = OnceLock::new();
    if IN_PROCESS_DONE.get().is_some() {
        return Ok(());
    }

    let meta = std::fs::metadata(worker_path)
        .map_err(|e| format!("stat {}: {e}", worker_path.display()))?;
    let worker_size = meta.len();
    // mtime gives us a cheap "did the binary change?" check.
    // Combined with the path string, this catches both "rebuilt
    // since last sign" and "different binary at a new path."
    let worker_mtime = meta
        .modified()
        .ok()
        .and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
        .map(|d| d.as_secs())
        .unwrap_or(0);

    let sentinel_marker = format!(
        "size={}\nmtime={}\npath={}\n",
        worker_size,
        worker_mtime,
        worker_path.display()
    );

    if let Some(sentinel) = sentinel_path() {
        if let Ok(existing) = std::fs::read_to_string(&sentinel) {
            if existing == sentinel_marker {
                IN_PROCESS_DONE.set(()).ok();
                return Ok(());
            }
        }
    }

    // Drop the entitlements plist (compile-time include_str) to a
    // temp file; codesign needs it on disk. Unique-per-pid so two
    // concurrent CLI invocations don't race on the same temp path.
    let plist = std::env::temp_dir().join(format!(
        "supermachine-entitlements-{}.plist",
        std::process::id()
    ));
    std::fs::write(&plist, crate::assets::ENTITLEMENTS_PLIST)
        .map_err(|e| format!("write entitlements plist: {e}"))?;

    let status = Command::new("codesign")
        .args(["-s", "-", "--entitlements"])
        .arg(&plist)
        .arg("--force")
        .arg(worker_path)
        .stdout(Stdio::null())
        .stderr(Stdio::piped())
        .status();
    let _ = std::fs::remove_file(&plist);

    match status {
        Ok(s) if s.success() => {
            // Update sentinel — best-effort, swallow IO errors.
            if let Some(sentinel) = sentinel_path() {
                if let Some(parent) = sentinel.parent() {
                    let _ = std::fs::create_dir_all(parent);
                }
                let _ = std::fs::write(&sentinel, sentinel_marker);
            }
            IN_PROCESS_DONE.set(()).ok();
            Ok(())
        }
        Ok(s) => Err(format!(
            "codesign exited with {:?} for {}",
            s.code(),
            worker_path.display()
        )),
        Err(e) => Err(format!(
            "failed to spawn codesign for {}: {e}\n\
             (codesign ships with macOS by default; if missing, \
             reinstall Xcode Command Line Tools)",
            worker_path.display()
        )),
    }
}

/// Locate `supermachine-worker` relative to the currently running
/// binary. Tries the immediate sibling first (the layout produced
/// by `cargo install`, where both binaries land in the same
/// directory), then walks ancestors of the running exe looking
/// for a `target/release/supermachine-worker` (the cargo dev tree).
pub fn locate_worker_bin() -> Option<PathBuf> {
    let exe = std::env::current_exe().ok()?;
    if let Some(dir) = exe.parent() {
        let sibling = dir.join("supermachine-worker");
        if sibling.is_file() {
            return Some(sibling);
        }
    }
    for ancestor in exe.ancestors() {
        let p = ancestor.join("target/release/supermachine-worker");
        if p.is_file() {
            return Some(p);
        }
    }
    None
}

/// `$XDG_DATA_HOME/supermachine/v{VERSION}/.worker-signed` (or the
/// `$HOME/.local/share/...` fallback). Versioned so a supermachine
/// upgrade doesn't reuse the prior version's sentinel.
fn sentinel_path() -> Option<PathBuf> {
    let base = if let Some(d) = std::env::var_os("XDG_DATA_HOME") {
        PathBuf::from(d)
    } else if let Some(h) = std::env::var_os("HOME") {
        PathBuf::from(h).join(".local/share")
    } else {
        return None;
    };
    Some(
        base.join("supermachine")
            .join(format!("v{}", env!("CARGO_PKG_VERSION")))
            .join(".worker-signed"),
    )
}