supermachine 0.7.108

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.
Documentation
//! Bake the slice's base snapshot: boot the CURRENT-kernel node_24-bookworm
//! snapshot (its kernel carries af-tsi patch 0013 — the getsockopt NULL-deref
//! guard the old node24-playwright bake predates), install Playwright's
//! chromium inside the guest, start it once and PROVE the kernel survives
//! (no oops in dmesg), then capture a full live snapshot to use as the
//! prefix-tree slice base.
//!
//! Env: SUPERMACHINE_BAKE_SRC (snapshot dir), SUPERMACHINE_BAKE_DEST (out dir),
//!      SUPERMACHINE_BAKE_MEM_MIB (default 8192).

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn main() {
    use std::time::Duration;
    use supermachine::{Image, VmConfig};

    let src = std::env::var("SUPERMACHINE_BAKE_SRC").expect("SUPERMACHINE_BAKE_SRC");
    let dest = std::env::var("SUPERMACHINE_BAKE_DEST").expect("SUPERMACHINE_BAKE_DEST");
    let mem: u32 = std::env::var("SUPERMACHINE_BAKE_MEM_MIB")
        .ok()
        .and_then(|v| v.parse().ok())
        .unwrap_or(8192);
    let _ = std::fs::remove_dir_all(&dest);

    let cfg = VmConfig::new().with_memory_mib(mem);
    let vm = Image::from_snapshot(&src)
        .expect("from_snapshot")
        .start(&cfg)
        .expect("start");
    for _ in 0..120 {
        if let Ok(o) = vm
            .exec_builder()
            .argv(["/bin/sh", "-c", "echo up"])
            .output()
        {
            if o.success() {
                break;
            }
        }
        std::thread::sleep(Duration::from_millis(1000));
    }
    let run = |cmd: &str| -> String {
        // The bookworm-vintage agent intermittently drops an exec connection
        // with a zero-frame EOF (recurrence of the vm#78 wedge, now
        // fail-fast per PR #89) — retry idempotent steps a few times.
        let mut last_err = String::new();
        let mut out = None;
        for attempt in 0..4 {
            match vm.exec_builder().argv(["/bin/sh", "-c", cmd]).output() {
                Ok(o) => {
                    out = Some(o);
                    break;
                }
                Err(e) => {
                    last_err = format!("{e:?}");
                    eprintln!("BAKE exec retry {attempt} after: {last_err}");
                    std::thread::sleep(std::time::Duration::from_secs(3));
                }
            }
        }
        let o = out.unwrap_or_else(|| panic!("exec failed after retries: {last_err}"));
        if !o.success() {
            panic!(
                "guest cmd failed: {cmd}\nstdout: {}\nstderr: {}",
                String::from_utf8_lossy(&o.stdout),
                String::from_utf8_lossy(&o.stderr)
            );
        }
        String::from_utf8_lossy(&o.stdout).to_string()
    };

    // The PW version env var lives on the HOST; the guest shell never sees
    // it, so it must be formatted into the command here, not expanded there.
    // Default 1.57: the 1.48 installer fails silently in this guest while
    // 1.57 was proven working by in-guest recon (chromium-1200 present).
    let pw = std::env::var("SUPERMACHINE_BAKE_PW_VERSION").unwrap_or_else(|_| "1.57".into());
    eprintln!("BAKE kernel={}", run("uname -r").trim());
    eprintln!("BAKE step=install_chromium (in-guest download, playwright@{pw})");
    let out = run(&format!(
        "cd /root && npx --yes playwright@{pw} install chromium chromium-headless-shell > /root/bake-install.log 2>&1; \
         echo INSTALL_EXIT=$?; tail -3 /root/bake-install.log; \
         ls /root/.cache/ms-playwright/ /.cache/ms-playwright/ 2>/dev/null | head -6"
    ));
    eprintln!("BAKE step=install_system_deps");
    let deps = run(&format!("cd /root && (npx --yes playwright@{pw} install-deps chromium 2>&1 | tail -2) || (apt-get update -qq && apt-get install -y -qq libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libasound2 libpango-1.0-0 libcairo2 2>&1 | tail -2)"));
    eprintln!("BAKE deps_out={}", deps.trim());
    eprintln!("BAKE install_out={}", out.trim());
    assert!(
        out.contains("INSTALL_EXIT=0"),
        "playwright install failed inside guest:\n{out}"
    );
    // Retry the find: a wedged exec (vm#93, per-connection) can return an
    // empty result with a clean exit, which is indistinguishable from
    // "not installed" without the sentinel.
    let mut chrome = String::new();
    for attempt in 0..4 {
        let found = run("echo FIND_BEGIN; find /root/.cache/ms-playwright /.cache/ms-playwright -maxdepth 4 -name chrome -path '*chrome-linux*' 2>/dev/null | head -1; echo FIND_END");
        if !found.contains("FIND_BEGIN") || !found.contains("FIND_END") {
            eprintln!("BAKE find retry {attempt}: sentinel missing (exec wedge)");
            std::thread::sleep(Duration::from_secs(3));
            continue;
        }
        chrome = found
            .split("FIND_BEGIN")
            .nth(1)
            .unwrap_or("")
            .split("FIND_END")
            .next()
            .unwrap_or("")
            .trim()
            .to_string();
        break;
    }
    if chrome.is_empty() {
        eprintln!(
            "BAKE find_diag={}",
            run("ls -laR /root/.cache/ms-playwright /.cache/ms-playwright 2>&1 | head -40").trim()
        );
    }
    assert!(
        !chrome.is_empty(),
        "chromium binary not found after install"
    );
    eprintln!("BAKE chrome={chrome}");
    // Bare `chromium` on PATH: fleet's check_playwright_snapshot_cdp (and any
    // guest script written against the v1 snapshot) invokes it by name.
    run(&format!("ln -sf {chrome} /usr/local/bin/chromium"));

    eprintln!("BAKE step=chrome_smoke (oops check)");
    run("dmesg -C || true");
    // QUIC/background networking off: chromium's startup QUIC probes create
    // AF_INET6 UDP tsi sockets whose getsockopt path oopses this kernel
    // (vm#113). Runtime launches must use the same flags until the kernel fix.
    run(&format!(
        "nohup {chrome} --headless --no-sandbox --disable-gpu --disable-dev-shm-usage \
         --disable-quic --disable-background-networking --disable-component-update \
         --disable-sync --disable-default-apps --no-first-run \
         --remote-debugging-port=9224 --remote-debugging-address=127.0.0.1 \
         --user-data-dir=/root/cdp-profile-smoke about:blank > /root/chrome-smoke.log 2>&1 & sleep 6; true"
    ));
    let ready = run("node -e \"require('http').get('http://127.0.0.1:9224/json/version',r=>{console.log('cdp ok',r.statusCode);process.exit(0)}).on('error',e=>{console.log('cdp ERR',e.message);process.exit(1)})\" || echo NOT_READY");
    eprintln!("BAKE cdp_smoke={}", ready.trim());
    if !ready.contains("cdp ok") {
        eprintln!(
            "BAKE chrome_log={}",
            run("tail -6 /root/chrome-smoke.log 2>/dev/null").trim()
        );
    }
    let oops = run("dmesg | grep -cE 'BUG|Oops|RIP: 0010:0x0' || true");
    eprintln!("BAKE oops_lines={}", oops.trim());
    if oops.trim() != "0" {
        eprintln!(
            "BAKE oops_dump={}",
            run("dmesg | grep -B2 -A14 -E 'BUG|Oops' | head -80 || true").trim()
        );
    }
    assert!(
        ready.contains("cdp ok"),
        "chromium devtools did not come up"
    );
    assert_eq!(
        oops.trim(),
        "0",
        "kernel oops during chromium start — kernel too old?"
    );

    // Kill the smoke chromium; the slice starts its own with a clean
    // profile. BEST-EFFORT: leftover smoke files in the capture are
    // harmless, and the per-connection ExecEof wedge (vm#93) can eat this
    // exec — never fail the bake over cosmetics.
    if let Ok(o) = vm.exec_builder().argv(["/bin/sh", "-c", "pkill -f 'cdp-profile-smok[e]' || true; sleep 1; rm -rf /root/cdp-profile-smoke /root/chrome-smoke.log; true"]).output() {
        let _ = o;
    } else {
        eprintln!("BAKE cleanup skipped (exec wedge) — capturing with smoke leftovers");
    }

    eprintln!("BAKE step=capture_full_snapshot");
    let t = std::time::Instant::now();
    vm.snapshot_live(&dest).expect("snapshot_live");
    eprintln!("BAKE captured in {:?} -> {dest}", t.elapsed());
    vm.stop().ok();
    eprintln!("BAKE result=PASS chrome={chrome}");
}

#[cfg(not(all(target_os = "linux", target_arch = "x86_64")))]
fn main() {
    eprintln!("linux/x86_64 only");
}