Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/gunnar

xtask/src/fast.rs

//! **`cargo xtask fast`**: Gunnar's estate on monetize's real code, its real box binaries in
//! rootless containers, in minutes, not the 45-minute KVM gate (monetize v2.0 P1, A24 §8.2;
//! A28 H14, lane N3). Holger's verb is the pattern; what is Gunnar's is prepared here and the
//! run is monetize's `monetize-fast` (through its binary, from `../monetize`):
//!
//! 1. Gunnar's two box rows, compiled in the toolchain container with their DECLARED argv
//!    (`gunnar-front-metered`, `gunnar-server-metered`; the profile dropped, as Holger's verb
//!    drops it), static musl binaries any image runs;
//! 2. Gunnar's CONTAINER MEDIA: what `gunnar-front-installer-metered` and
//!    `gunnar-installer-metered` boot (the program, the row's `korp.args=` and its `korp.env.*`,
//!    from the declaration, never typed here), as JSON;
//! 3. Gunnar's monetize GUEST (`gunnar-monetize-guest`, wasm32, unsigned: the run signs it under
//!    its own CA) and the ONE box's kit, built by monetize's `monetize-fast kit`;
//! 4. `monetize-fast --product gunnar --kit …` on the host: it re-runs itself inside podman's
//!    rootless network namespace and writes the receipt (`target/fast/receipt.txt`, `.json`).
//!    The estate's names and media kinds are the guest's facts (op 90), read by monetize-fast.
//!
//! `cargo xtask fast [--out DIR] [--budget-s N] [--twin-late-s N] [--no-build]`. Also
//! `cargo xtask fast --product gunnar` from monetize.

use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::process::Command;

use gunnar_recipe::nornir_build_thing::variant::{BuildVariant, BuildVariantSet};

/// The rows whose binaries the boxes run: (row, binary).
pub const ROWS: [(&str, &str); 2] = [
    ("gunnar-front-metered", "gunnar-front"),
    ("gunnar-server-metered", "gunnar-server"),
];
/// The target those rows declare.
pub const TARGET: &str = "x86_64-unknown-linux-musl";
/// The machines' hostname prefixes (the guest's `lifecycle.facts` shape) and the installer row
/// each boots (the twin boots the appliance's medium; the election makes it the twin).
pub const MEDIA: [(&str, &str); 3] = [
    ("gunnar-front-", gunnar_recipe::FRONT_METERED_INSTALLER),
    ("gunnar-appliance-", gunnar_recipe::METERED_INSTALLER),
    ("gunnar-twin-", gunnar_recipe::METERED_INSTALLER),
];
/// Gunnar's monetize guest: its manifest (a detached workspace) and its module.
pub const GUEST_MANIFEST: &str = "crates/gunnar-monetize-guest/Cargo.toml";
pub const GUEST_WASM: &str = "wasm32-unknown-unknown/release/gunnar_monetize_guest.wasm";
/// **What a container needs that the metal's PID 1 gives**: korp-start opens gunnar-server's
/// start-up disk test (`disktest::spawn`'s gate: PID 1 booted by korp-start); a container is not
/// that, so without its own switch a box answers the installer "the disk test has not finished"
/// for 300 s before it joins. Said here, the one place the media differ from the rows.
///
/// **The monetize key** (T-OWNERFLOW, 2026-10-26): the metered rows name the PRODUCTION box's
/// key (`GUNNAR_MONETIZE_PUBKEY=<hex>`), and the run's ONE box signs with its own, so its boxes
/// refused every order it queued (a paid sign-up's account never made). `fed`: the key the
/// run's door feeds, as the constant medium carries none (`gunnar_common::monetize_key::FED`).
pub const CONTAINER_ENV: [(&str, &str); 2] =
    [("GUNNAR_DISKTEST", "1"), ("GUNNAR_MONETIZE_PUBKEY", "fed")];
/// gunnar-ui's paid console row (trunk, on the host), which the metered front embeds: the
/// sale check reads its `/login`.
pub const CONSOLE_ROW: &str = "gunnar-ui-web-metered";
/// The twin boots this many seconds after the others by default (A12's order, as Holger's).
pub const DEFAULT_TWIN_LATE_S: u64 = 20;

/// A container medium (mock-upcloud's `container::Medium`): what a machine runs from `/opt/bin`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ContainerMedium {
    pub program: String,
    pub args: Vec<String>,
    pub env: BTreeMap<String, String>,
}

fn json_str(s: &str) -> String {
    let mut o = String::from("\"");
    for c in s.chars() {
        match c {
            '"' => o.push_str("\\\""),
            '\\' => o.push_str("\\\\"),
            '\n' => o.push_str("\\n"),
            c if (c as u32) < 0x20 => o.push_str(&format!("\\u{:04x}", c as u32)),
            c => o.push(c),
        }
    }
    o.push('"');
    o
}

impl ContainerMedium {
    /// The medium's JSON (`container_medium: 1`).
    pub fn to_json(&self) -> String {
        let args = self
            .args
            .iter()
            .map(|a| json_str(a))
            .collect::<Vec<_>>()
            .join(", ");
        let env = self
            .env
            .iter()
            .map(|(k, v)| format!("{}: {}", json_str(k), json_str(v)))
            .collect::<Vec<_>>()
            .join(", ");
        format!("{{\"container_medium\": 1, \"program\": {}, \"args\": [{args}], \"env\": {{{env}}}, \"files\": {{}}}}\n", json_str(&self.program))
    }
}

/// Pure: **the container medium an installer row boots**: the program is its variant's binary
/// (what `korp.entry` execs on the metal), the arguments its `korp.args=` token, the environment
/// its `korp.env.*` tokens (its declared `env` included), as `effective_boot_params` renders them.
pub fn medium_of(set: &BuildVariantSet, installer: &str) -> Result<ContainerMedium, String> {
    let row = set
        .installers
        .iter()
        .find(|i| i.name == installer)
        .ok_or_else(|| format!("[fast-media] the installer row {installer} is not declared"))?;
    let variant = set
        .variants
        .iter()
        .find(|v| v.name == row.variant)
        .ok_or_else(|| {
            format!(
                "[fast-media] {installer} boots {}, which is not declared",
                row.variant
            )
        })?;
    let program = variant
        .bin
        .clone()
        .ok_or_else(|| format!("[fast-media] {} names no binary", variant.name))?;
    let mut args = Vec::new();
    let mut env = BTreeMap::new();
    for p in row.effective_boot_params() {
        if let Some(a) = p.strip_prefix("korp.args=") {
            args = a
                .split(',')
                .filter(|s| !s.is_empty())
                .map(str::to_string)
                .collect();
        } else if let Some(kv) = p.strip_prefix("korp.env.") {
            if let Some((k, v)) = kv.split_once('=') {
                env.insert(k.to_string(), v.to_string());
            }
        }
    }
    env.entry("RUST_LOG".into())
        .or_insert_with(|| "info".into());
    if program == "gunnar-server" {
        for (k, v) in CONTAINER_ENV {
            env.insert(k.to_string(), v.to_string());
        }
    }
    Ok(ContainerMedium { program, args, env })
}

/// Pure: a row's declared build argv, the profile dropped (a debug build, as Holger's fast and
/// suite-check build), `--locked`.
pub fn row_args(v: &BuildVariant) -> Vec<String> {
    let mut a = v.cargo_args();
    if let Some(i) = a.iter().position(|x| x == "--profile") {
        a.drain(i..(i + 2).min(a.len()));
    }
    a.insert(1, "--locked".into());
    a
}

/// **The CI image** the toolchain container runs by default: pinned by DIGEST, the one dwarves'
/// `ci/runner.sh` and nornir-xtask-support's `ci::CI_IMAGE` pin (A KNOWN TWIN). ci4: the pinned
/// recipe plus OpenSSL headers, clippy, rustfmt, openssh-client, strace and git-lfs
/// (dwarves' `ci/ci-toolchain.Containerfile`); a box gets it by
/// `podman load -i ci4-53fec13a5c1c7f2c.oci.tar`, which keeps the digest.
pub const CI_IMAGE: &str = "localhost/nordisk-cargo-toolchain@sha256:875a7a325dfe7f6b838535d085a2a502e8ad250076c947f6d023b1e224f03334";

/// Pure: **the toolchain container's argv** for `cargo args` in this checkout, the same shape as
/// nornir-xtask-support's `ci::Container` (A KNOWN TWIN: gunnar's xtask links no
/// nornir-xtask-support): the CI image (`NORNIR_CI_IMAGE`), the constellation at `/src`, the
/// target at `/target`, the shared registry, sccache and mold (`NORNIR_CI_REGISTRY`,
/// `NORNIR_CI_FAST`, `NORNIR_CI_SCCACHE`), as the invoking user.
pub fn toolchain_argv(
    root: &Path,
    target: &Path,
    env: &dyn Fn(&str) -> Option<String>,
    args: &[String],
    console: Option<&Path>,
) -> Vec<String> {
    let parent = root.parent().unwrap_or(root);
    let repo = root
        .file_name()
        .map(|n| n.to_string_lossy().into_owned())
        .unwrap_or_default();
    let mut v: Vec<String> = [
        "podman",
        "run",
        "--rm",
        "--init",
        "--pids-limit=-1",
        "--userns=keep-id",
        "--passwd-entry=$USERNAME:*:$UID:$GID:$NAME:/tmp:/bin/sh",
    ]
    .iter()
    .map(|s| s.to_string())
    .collect();
    let mut bind = |host: &str, at: &str, ro: bool| {
        v.push("-v".into());
        v.push(format!("{host}:{at}{}", if ro { ":ro" } else { "" }));
    };
    bind(&parent.display().to_string(), "/src", false);
    bind(&target.display().to_string(), "/target", false);
    // NORNIR_CI_REGISTRY, else the caller's own `$CARGO_HOME/registry` (else
    // `~/.cargo/registry`) when it exists: `ci::Container`'s rule (GHUNT-5). Without a mount
    // cargo writes into the image's registry, which the keep-id user may not.
    let registry = env("NORNIR_CI_REGISTRY")
        .filter(|r| !r.is_empty())
        .or_else(|| {
            env("CARGO_HOME")
                .filter(|h| !h.is_empty())
                .or_else(|| {
                    env("HOME")
                        .filter(|h| !h.is_empty())
                        .map(|h| format!("{h}/.cargo"))
                })
                .map(|h| format!("{h}/registry"))
                .filter(|p| Path::new(p).is_dir())
        });
    if let Some(r) = registry {
        bind(&r, "/usr/local/cargo/registry", false);
    }
    if let Some(f) = env("NORNIR_CI_FAST") {
        bind(&f, "/tools", true);
    }
    if let Some(s) = env("NORNIR_CI_SCCACHE") {
        bind(&s, "/sccache", false);
    }
    if let Some(f) = env("NORNIR_CI_FAST") {
        v.push("--env-file".into());
        v.push(format!("{f}/env.sh"));
    }
    // The run's console, read-only at its own path, named to the front's build script.
    if let Some(c) = console {
        v.push("-v".into());
        v.push(format!("{}:{}:ro", c.display(), c.display()));
        v.push("-e".into());
        v.push(format!("GUNNAR_FRONT_CONSOLE_DIST={}", c.display()));
    }
    for (k, val) in [
        ("CARGO_TARGET_DIR", "/target"),
        ("NORNIR_CI_IN_CONTAINER", "1"),
        ("GIT_CONFIG_COUNT", "1"),
        ("GIT_CONFIG_KEY_0", "safe.directory"),
        ("GIT_CONFIG_VALUE_0", "*"),
    ] {
        v.push("-e".into());
        v.push(format!("{k}={val}"));
    }
    if let Some(j) = env("CARGO_BUILD_JOBS") {
        v.push("-e".into());
        v.push(format!("CARGO_BUILD_JOBS={j}"));
    }
    v.push("-w".into());
    v.push(format!("/src/{repo}"));
    v.push(
        env("NORNIR_CI_IMAGE")
            .filter(|i| !i.is_empty())
            .unwrap_or_else(|| CI_IMAGE.into()),
    );
    v.push("cargo".into());
    v.extend(args.iter().cloned());
    v
}

/// The toolchain container's target: `$CARGO_TARGET_DIR/toolchain` (as `ci::Container`), else
/// `<root>/target/toolchain`.
pub fn toolchain_target(root: &Path) -> PathBuf {
    std::env::var("NORNIR_CI_TOOLCHAIN_TARGET")
        .ok()
        .filter(|v| !v.is_empty())
        .map(PathBuf::from)
        .unwrap_or_else(|| {
            std::env::var("CARGO_TARGET_DIR")
                .ok()
                .filter(|v| !v.is_empty())
                .map(PathBuf::from)
                .unwrap_or_else(|| root.join("target"))
                .join("toolchain")
        })
}

/// **Where this run's console is**: `GUNNAR_FRONT_CONSOLE_DIST` when the caller names one, else
/// the run's OWN directory, `<out>/console/dist-metered`. Never gunnar-ui's source tree: two runs
/// of one checkout that start together would both build into it (FAST-REL, 2026-10-25).
pub fn console_dist(out: &Path, env: Option<&str>) -> PathBuf {
    match env.filter(|v| !v.is_empty()) {
        Some(d) => PathBuf::from(d),
        None => out.join("console").join("dist-metered"),
    }
}

/// **The lock beside trunk's intermediates**: trunk writes `wasm-bindgen/` and `wasm-opt/` into
/// the cargo target the console's build uses (`CARGO_TARGET_DIR`, else gunnar-ui's `target`), so
/// two console builds over one target take turns there; each still writes its own bundle.
pub fn console_lock_path(ui: &Path, cargo_target: Option<&str>) -> PathBuf {
    cargo_target
        .filter(|v| !v.is_empty())
        .map(PathBuf::from)
        .unwrap_or_else(|| ui.join("target"))
        .join("gunnar-console-build.lock")
}

/// Held while this process builds a console over one target; removed on drop.
struct ConsoleLock(PathBuf);

impl Drop for ConsoleLock {
    fn drop(&mut self) {
        let _ = std::fs::remove_file(&self.0);
    }
}

/// Pure: is the lock's holder (`pid` as written in the lock) gone? An unreadable pid is gone.
pub fn holder_gone(pid: &str, alive: &dyn Fn(u32) -> bool) -> bool {
    pid.trim().parse::<u32>().map(|p| !alive(p)).unwrap_or(true)
}

/// Take the lock at `path`, waiting while a live process holds it; a dead holder's lock is taken over.
fn take_console_lock(path: &Path) -> Result<ConsoleLock, String> {
    if let Some(d) = path.parent() {
        std::fs::create_dir_all(d).map_err(|e| format!("{}: {e}", d.display()))?;
    }
    let mut said = false;
    loop {
        match std::fs::OpenOptions::new()
            .write(true)
            .create_new(true)
            .open(path)
        {
            Ok(mut f) => {
                use std::io::Write as _;
                f.write_all(std::process::id().to_string().as_bytes())
                    .map_err(|e| format!("{}: {e}", path.display()))?;
                return Ok(ConsoleLock(path.to_path_buf()));
            }
            Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {
                let pid = std::fs::read_to_string(path).unwrap_or_default();
                if holder_gone(&pid, &|p| Path::new(&format!("/proc/{p}")).exists())
                    && !pid.is_empty()
                {
                    let _ = std::fs::remove_file(path);
                    continue;
                }
                if !said {
                    println!(
                        "fast: another console build holds {} (pid {}); waiting for it",
                        path.display(),
                        pid.trim()
                    );
                    said = true;
                }
                std::thread::sleep(std::time::Duration::from_millis(500));
            }
            Err(e) => return Err(format!("{}: {e}", path.display())),
        }
    }
}

/// A front without its console serves `[console-not-on-medium]` at `/login`, and the ONE box's
/// sale check refuses the estate: refused here, by name, before anything is laid.
pub fn refuse_front_without_console(dist: &Path) -> Result<(), String> {
    if dist.join("index.html").is_file() {
        Ok(())
    } else {
        Err(format!("[fast-console] {} has no index.html: the metered front would serve no console and the sale check would refuse it; build it with `cargo xtask build --variant {CONSOLE_ROW}` in ../gunnar-ui", dist.display()))
    }
}

/// **The run's console**: gunnar-ui's paid console, built (trunk, on the host) into the run's own
/// directory ([`console_dist`]) when it is not there. Its directory is returned (absolute).
pub fn build_console(root: &Path, out: &Path) -> Result<PathBuf, String> {
    let out = std::path::absolute(out).map_err(|e| format!("{}: {e}", out.display()))?;
    let dist = console_dist(
        &out,
        std::env::var("GUNNAR_FRONT_CONSOLE_DIST").ok().as_deref(),
    );
    if refuse_front_without_console(&dist).is_err() {
        let ui = root.parent().unwrap_or(root).join("gunnar-ui");
        let _held = take_console_lock(&console_lock_path(
            &ui,
            std::env::var("CARGO_TARGET_DIR").ok().as_deref(),
        ))?;
        println!("fast: build gunnar-ui's console: cargo xtask build --variant {CONSOLE_ROW} in {}, into {}", ui.display(), dist.display());
        let s = Command::new(cargo())
            .current_dir(&ui)
            .args(["xtask", "build", "--variant", CONSOLE_ROW])
            .env("GUNNAR_UI_BUNDLE_DIST", &dist)
            .status()
            .map_err(|e| format!("cargo xtask build in {}: {e}", ui.display()))?;
        if !s.success() {
            return Err(format!(
                "[fast-build] gunnar-ui's {CONSOLE_ROW}: cargo xtask build exited {s}"
            ));
        }
    }
    refuse_front_without_console(&dist)?;
    Ok(dist)
}

fn in_a_container() -> bool {
    std::env::var("NORNIR_CI_IN_CONTAINER").is_ok_and(|v| v == "1")
        || Path::new("/run/.containerenv").exists()
        || Path::new("/.dockerenv").exists()
}

fn value(args: &[String], flag: &str) -> Option<String> {
    args.iter()
        .position(|a| a == flag)
        .and_then(|i| args.get(i + 1))
        .cloned()
}

fn cargo() -> String {
    std::env::var("CARGO").unwrap_or_else(|_| "cargo".into())
}

fn in_toolchain(
    root: &Path,
    target: &Path,
    what: &str,
    args: &[String],
    console: Option<&Path>,
) -> Result<(), String> {
    let argv = toolchain_argv(
        root,
        target,
        &|k| std::env::var(k).ok().filter(|v| !v.is_empty()),
        args,
        console,
    );
    println!("fast: build {what}: cargo {}", args.join(" "));
    let s = Command::new(&argv[0])
        .args(&argv[1..])
        .status()
        .map_err(|e| format!("{}: {e}", argv[0]))?;
    if s.success() {
        Ok(())
    } else {
        Err(format!("[fast-build] {what}: cargo exited {s}"))
    }
}

/// The verb.
pub fn run(root: &Path, args: &[String]) -> Result<(), String> {
    if in_a_container() && !std::env::var("NORNIR_CI_HOST").is_ok_and(|v| v == "1") {
        return Err("[fast-on-the-host] cargo xtask fast starts containers: run it on the host, not inside the toolchain container".into());
    }
    let out = value(args, "--out")
        .map(PathBuf::from)
        .unwrap_or_else(|| root.join("target/fast"));
    let budget = value(args, "--budget-s").unwrap_or_else(|| "180".into());
    let twin_late = value(args, "--twin-late-s").unwrap_or_else(|| DEFAULT_TWIN_LATE_S.to_string());
    let build = !args.iter().any(|a| a == "--no-build");
    std::fs::create_dir_all(&out).map_err(|e| format!("{}: {e}", out.display()))?;
    let parent = root.parent().ok_or("gunnar's parent")?;
    let monetize = parent.join("monetize");
    if !monetize.join("crates/monetize-fast/Cargo.toml").is_file() {
        return Err(
            "[fast-no-monetize] ../monetize/crates/monetize-fast is not checked out beside gunnar"
                .into(),
        );
    }
    let set = gunnar_recipe::emit_recipe::declared();
    let target = toolchain_target(root);
    std::fs::create_dir_all(&target).map_err(|e| format!("{}: {e}", target.display()))?;
    let bin_dir = target.join(TARGET).join("debug");

    // 1. the box binaries, from the declared rows, in the toolchain container; the front embeds
    // gunnar-ui's console, built first when it is not there (trunk on the host, as Holger's).
    let dist = if build {
        build_console(root, &out)?
    } else {
        console_dist(
            &std::path::absolute(&out).map_err(|e| e.to_string())?,
            std::env::var("GUNNAR_FRONT_CONSOLE_DIST").ok().as_deref(),
        )
    };
    refuse_front_without_console(&dist)?;
    if build {
        for (row, _) in ROWS {
            let v = set
                .variants
                .iter()
                .find(|v| v.name == row)
                .ok_or_else(|| format!("[fast-row] {row} is not declared"))?;
            // The front embeds THIS run's console: its directory, at the same path inside.
            let console = (row == ROWS[0].0).then_some(dist.as_path());
            in_toolchain(root, &target, row, &row_args(v), console)?;
        }
    }
    for (_, bin) in ROWS {
        if !bin_dir.join(bin).is_file() {
            return Err(format!(
                "[fast-build] {} is not built",
                bin_dir.join(bin).display()
            ));
        }
    }
    // 3. Gunnar's guest, then the ONE box's kit (monetize's).
    let guest = target.join(GUEST_WASM);
    let kit_dir = out.join("kit");
    if build {
        let a: Vec<String> = [
            "build",
            "--locked",
            "--manifest-path",
            GUEST_MANIFEST,
            "--release",
            "--target",
            "wasm32-unknown-unknown",
        ]
        .iter()
        .map(|s| s.to_string())
        .collect();
        in_toolchain(root, &target, "gunnar-monetize-guest", &a, None)?;
        if !guest.is_file() {
            return Err(format!("[fast-build] {} is not built", guest.display()));
        }
        let s = Command::new(cargo())
            .current_dir(&monetize)
            .args([
                "run",
                "--quiet",
                "--manifest-path",
                "crates/monetize-fast/Cargo.toml",
                "--bin",
                "monetize-fast",
                "--",
                "kit",
                "--out",
            ])
            .arg(&kit_dir)
            .arg("--parent")
            .arg(parent)
            .arg("--guest")
            .arg(format!("gunnar={}", guest.display()))
            .status()
            .map_err(|e| format!("monetize-fast kit: {e}"))?;
        if !s.success() {
            return Err(format!("[fast-kit] monetize-fast kit exited {s}"));
        }
    }

    // 2. Gunnar's container media, from the declared installer rows.
    let media_dir = out.join("media");
    std::fs::create_dir_all(&media_dir).map_err(|e| e.to_string())?;
    let mut run = Command::new(cargo());
    run.current_dir(&monetize)
        .args([
            "run",
            "--quiet",
            "--manifest-path",
            "crates/monetize-fast/Cargo.toml",
            "--bin",
            "monetize-fast",
            "--",
        ])
        .args(["--product", "gunnar", "--kit"])
        .arg(&kit_dir)
        .arg("--bin-dir")
        .arg(&bin_dir)
        .arg("--out")
        .arg(&out)
        .args(["--budget-s", &budget, "--twin-late-s", &twin_late]);
    for (prefix, installer) in MEDIA {
        let m = medium_of(&set, installer)?;
        let f = media_dir.join(format!("{prefix}json"));
        std::fs::write(&f, m.to_json()).map_err(|e| format!("{}: {e}", f.display()))?;
        run.arg("--medium").arg(format!("{prefix}={}", f.display()));
    }
    for tree in ["gunnar", "gunnar-ui", "monetize", "Skidbladnir", "edda"] {
        run.arg("--tree")
            .arg(format!("{tree}={}", parent.join(tree).display()));
    }
    // 4. the run, monetize's.
    println!(
        "fast: monetize-fast --product gunnar (the receipt goes to {})",
        out.display()
    );
    let s = run
        .status()
        .map_err(|e| format!("cargo run monetize-fast: {e}"))?;
    if !s.success() {
        return Err(format!(
            "[fast-red] the fast test is RED; see {}",
            out.join("receipt.txt").display()
        ));
    }
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn gunnars_media_boot_what_its_metered_installer_rows_boot() {
        let set = gunnar_recipe::emit_recipe::declared();
        let server = medium_of(&set, gunnar_recipe::METERED_INSTALLER).unwrap();
        assert_eq!(
            server.program, "gunnar-server",
            "korp.entry execs the server variant's binary"
        );
        assert_eq!(
            server.args,
            ["serve", "--grpc-bind", "utility:50051"],
            "the row's korp.args="
        );
        assert_eq!(
            server.env.get("GUNNAR_APPLIANCE").map(String::as_str),
            Some("1")
        );
        assert_eq!(
            server.env.get("GUNNAR_DATA_ROOT").map(String::as_str),
            Some("/data")
        );
        assert!(
            server.env.contains_key("GUNNAR_MONETIZE_PUBKEY"),
            "the row's declared env rides too: {:?}",
            server.env
        );
        // ★ T-OWNERFLOW (2026-10-26, the owner's walk on the real boxes): the row names the
        // PRODUCTION box's monetize key; the run's ONE box signs with its own, so the boxes
        // refused every order it queued ("does not verify under this box's monetize key") and a
        // paid sign-up stayed pending. The run's door feeds its key, as the constant medium's is.
        assert_eq!(server.env.get("GUNNAR_MONETIZE_PUBKEY").map(String::as_str), Some("fed"), "the boxes take (gunnar_common::monetize_key::FED) the run's own monetize key from the door: {:?}", server.env);
        assert_eq!(
            server.env.get("GUNNAR_DISKTEST").map(String::as_str),
            Some("1"),
            "the disk test korp-start's PID 1 runs on the metal"
        );
        let front = medium_of(&set, gunnar_recipe::FRONT_METERED_INSTALLER).unwrap();
        assert_eq!(front.program, "gunnar-front");
        assert!(front.args.is_empty(), "gunnar-front takes no korp.args");
        assert!(
            !front.env.contains_key("GUNNAR_APPLIANCE"),
            "no appliance gate on the front"
        );
        assert!(
            !front.env.contains_key("GUNNAR_DISKTEST"),
            "the front runs no disk test"
        );
        assert!(medium_of(&set, "no-such-installer")
            .unwrap_err()
            .contains("[fast-media]"));
    }

    #[test]
    fn a_medium_is_monetize_fasts_container_medium_json() {
        let m = ContainerMedium {
            program: "gunnar-server".into(),
            args: vec!["serve".into()],
            env: BTreeMap::from([("K".to_string(), "a \"b\"".to_string())]),
        };
        assert_eq!(m.to_json(), "{\"container_medium\": 1, \"program\": \"gunnar-server\", \"args\": [\"serve\"], \"env\": {\"K\": \"a \\\"b\\\"\"}, \"files\": {}}\n");
        let parsed = crate::json::Json::parse(&m.to_json()).unwrap();
        assert_eq!(
            parsed
                .get("env")
                .and_then(|e| e.get("K"))
                .and_then(|v| v.str()),
            Some("a \"b\"")
        );
    }

    #[test]
    fn the_box_rows_are_declared_musl_and_built_without_their_profile() {
        let set = gunnar_recipe::emit_recipe::declared();
        for (row, bin) in ROWS {
            let v = set
                .variants
                .iter()
                .find(|v| v.name == row)
                .unwrap_or_else(|| panic!("{row}"));
            assert_eq!(v.bin.as_deref(), Some(bin));
            assert_eq!(v.target.as_deref(), Some(TARGET));
            let a = row_args(v);
            assert_eq!(&a[..2], ["build", "--locked"]);
            assert!(!a.iter().any(|x| x == "--profile"), "{a:?}");
        }
        let front = set
            .variants
            .iter()
            .find(|v| v.name == "gunnar-front-metered")
            .unwrap();
        assert!(
            row_args(front)
                .windows(2)
                .any(|w| w[0] == "--manifest-path"
                    && w[1].ends_with("crates/gunnar-front/Cargo.toml")),
            "the front is a detached workspace"
        );
    }

    #[test]
    fn the_media_prefixes_are_the_guests_own_hostnames() {
        // The names are Gunnar's facts (its guest's `lifecycle.facts` shape): a prefix here that
        // the guest does not lay would boot no machine.
        let shape = include_str!("../../crates/gunnar-monetize-guest/src/lifecycle.rs");
        for (prefix, _) in MEDIA {
            assert!(
                shape.contains(&format!("\"{prefix}{{company}}\"")),
                "{prefix} is not a hostname the guest lays"
            );
        }
    }

    #[test]
    fn a_front_without_its_console_is_refused_by_name() {
        let d = std::env::temp_dir().join(format!("gunnar-fast-console-{}", std::process::id()));
        let _ = std::fs::remove_dir_all(&d);
        std::fs::create_dir_all(&d).unwrap();
        let e = refuse_front_without_console(&d).unwrap_err();
        assert!(
            e.contains("[fast-console]") && e.contains(CONSOLE_ROW),
            "{e}"
        );
        std::fs::write(d.join("index.html"), "<html>").unwrap();
        assert!(refuse_front_without_console(&d).is_ok());
        let _ = std::fs::remove_dir_all(&d);
        assert_eq!(
            console_dist(Path::new("/runs/a"), None),
            Path::new("/runs/a/console/dist-metered"),
            "the run's own, never gunnar-ui's tree"
        );
        assert_ne!(
            console_dist(Path::new("/runs/a"), None),
            console_dist(Path::new("/runs/b"), None),
            "two runs, two consoles"
        );
        assert_eq!(
            console_dist(Path::new("/runs/a"), Some("/x")),
            Path::new("/x"),
            "the caller's"
        );
        assert_eq!(
            console_dist(Path::new("/runs/a"), Some("")),
            Path::new("/runs/a/console/dist-metered"),
            "an empty value is unset"
        );
    }

    #[test]
    fn the_toolchain_argv_is_the_ci_containers_shape() {
        let env = |k: &str| match k {
            "NORNIR_CI_IMAGE" => Some("img:1".to_string()),
            "NORNIR_CI_FAST" => Some("/tools-host".to_string()),
            _ => None,
        };
        let a = toolchain_argv(
            Path::new("/c/gunnar"),
            Path::new("/t/toolchain"),
            &env,
            &["build".into()],
            None,
        );
        let s = a.join(" ");
        assert!(
            s.starts_with("podman run --rm --init --pids-limit=-1 --userns=keep-id"),
            "{s}"
        );
        assert!(s.contains("-v /c:/src -v /t/toolchain:/target -v /tools-host:/tools:ro --env-file /tools-host/env.sh"), "{s}");
        assert!(
            !s.contains("--network host"),
            "never host networking beside env.sh: {s}"
        );
        assert!(s.ends_with("-w /src/gunnar img:1 cargo build"), "{s}");
    }

    /// ★ GHUNT-5 (lane OX-GUNHUNT, 2026-10-28): without NORNIR_CI_REGISTRY the container
    /// mounts the caller's own `$CARGO_HOME/registry` (else `~/.cargo/registry`), as the
    /// known twin `nornir_xtask_support::ci::Container` does. It mounted none, so a plain
    /// `cargo xtask fast` (and `cargo xtask idp`) on a dev box ran cargo against the image's
    /// own registry, which the keep-id user cannot write: "failed to create directory
    /// `/usr/local/cargo/registry/cache/...`: Permission denied", before any build.
    #[test]
    fn without_nornir_ci_registry_the_callers_cargo_home_registry_is_mounted() {
        let home = std::env::temp_dir().join(format!("ghunt5-{}", std::process::id()));
        std::fs::create_dir_all(home.join("cargo/registry")).unwrap();
        std::fs::create_dir_all(home.join("user/.cargo/registry")).unwrap();
        let cargo_home = home.join("cargo").display().to_string();
        let user = home.join("user").display().to_string();
        let with_cargo_home = |k: &str| match k {
            "CARGO_HOME" => Some(cargo_home.clone()),
            "HOME" => Some(user.clone()),
            _ => None,
        };
        let s = toolchain_argv(
            Path::new("/c/gunnar"),
            Path::new("/t"),
            &with_cargo_home,
            &["build".into()],
            None,
        )
        .join(" ");
        assert!(
            s.contains(&format!(
                "-v {cargo_home}/registry:/usr/local/cargo/registry"
            )),
            "{s}"
        );
        let only_home = |k: &str| (k == "HOME").then(|| user.clone());
        let s = toolchain_argv(
            Path::new("/c/gunnar"),
            Path::new("/t"),
            &only_home,
            &["build".into()],
            None,
        )
        .join(" ");
        assert!(
            s.contains(&format!(
                "-v {user}/.cargo/registry:/usr/local/cargo/registry"
            )),
            "{s}"
        );
        let named = |k: &str| match k {
            "NORNIR_CI_REGISTRY" => Some("/reg".to_string()),
            "CARGO_HOME" => Some(cargo_home.clone()),
            _ => None,
        };
        let s = toolchain_argv(
            Path::new("/c/gunnar"),
            Path::new("/t"),
            &named,
            &["build".into()],
            None,
        )
        .join(" ");
        assert!(
            s.contains("-v /reg:/usr/local/cargo/registry"),
            "the named one wins: {s}"
        );
        assert_eq!(
            s.matches(":/usr/local/cargo/registry").count(),
            1,
            "one registry: {s}"
        );
        let _ = std::fs::remove_dir_all(&home);
    }

    /// ★ Without NORNIR_CI_IMAGE the toolchain container is the CI image pinned by DIGEST, the
    /// one dwarves' ci/runner.sh and nornir-xtask-support's `ci::CI_IMAGE` pin (ci4: ssh client,
    /// strace, git-lfs; `:live` had none and names another image on each box).
    #[test]
    fn the_toolchain_image_is_the_ci_image_pinned_by_digest() {
        let env = |_: &str| None;
        let a = toolchain_argv(
            Path::new("/c/gunnar"),
            Path::new("/t"),
            &env,
            &["build".into()],
            None,
        );
        let s = a.join(" ");
        assert!(
            s.ends_with(&format!("-w /src/gunnar {CI_IMAGE} cargo build")),
            "{s}"
        );
        let (name, digest) = CI_IMAGE
            .split_once("@sha256:")
            .expect("CI_IMAGE names a digest");
        assert_eq!(name, "localhost/nordisk-cargo-toolchain");
        assert_eq!(digest.len(), 64, "{digest}");
        assert!(
            digest
                .bytes()
                .all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase()),
            "{digest}"
        );
        let empty = |k: &str| (k == "NORNIR_CI_IMAGE").then(String::new);
        assert!(
            toolchain_argv(
                Path::new("/c/gunnar"),
                Path::new("/t"),
                &empty,
                &["build".into()],
                None
            )
            .join(" ")
            .contains(CI_IMAGE),
            "an empty NORNIR_CI_IMAGE is unset"
        );
        // The runner's pin, when the dwarves sibling is checked out beside this repo.
        let runner = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../dwarves/ci/runner.sh");
        if let Ok(r) = std::fs::read_to_string(&runner) {
            let line = r
                .lines()
                .find(|l| l.starts_with("CI_IMAGE="))
                .expect("runner.sh sets CI_IMAGE");
            assert_eq!(
                line.trim_start_matches("CI_IMAGE=")
                    .trim()
                    .trim_matches('"'),
                CI_IMAGE,
                "{}",
                runner.display()
            );
        }
    }

    /// ★ The front row embeds THE RUN's console: mounted read-only at its own path and named to
    /// its build script; the other rows see none.
    #[test]
    fn the_front_row_reads_the_runs_own_console() {
        let env = |_: &str| None;
        let s = toolchain_argv(
            Path::new("/c/gunnar"),
            Path::new("/t"),
            &env,
            &["build".into()],
            Some(Path::new("/runs/a/console/dist-metered")),
        )
        .join(" ");
        assert!(s.contains("-v /runs/a/console/dist-metered:/runs/a/console/dist-metered:ro -e GUNNAR_FRONT_CONSOLE_DIST=/runs/a/console/dist-metered"), "{s}");
        let s = toolchain_argv(
            Path::new("/c/gunnar"),
            Path::new("/t"),
            &env,
            &["build".into()],
            None,
        )
        .join(" ");
        assert!(!s.contains("GUNNAR_FRONT_CONSOLE_DIST"), "{s}");
    }

    /// ★ Console builds over one target take turns at one lock beside trunk's intermediates; a
    /// lock whose holder died is taken over, a live holder's is waited for.
    #[test]
    fn console_builds_over_one_target_share_one_lock() {
        assert_eq!(
            console_lock_path(Path::new("/c/gunnar-ui"), Some("/t/fast-gunnar")),
            Path::new("/t/fast-gunnar/gunnar-console-build.lock")
        );
        assert_eq!(
            console_lock_path(Path::new("/c/gunnar-ui"), None),
            Path::new("/c/gunnar-ui/target/gunnar-console-build.lock")
        );
        assert!(holder_gone("4242", &|_| false), "a dead holder");
        assert!(!holder_gone("4242\n", &|p| p == 4242), "a live holder");
        assert!(holder_gone("not a pid", &|_| true), "an unreadable lock");
        let d = std::env::temp_dir().join(format!("gunnar-console-lock-{}", std::process::id()));
        let _ = std::fs::remove_dir_all(&d);
        let p = d.join("l.lock");
        std::fs::create_dir_all(&d).unwrap();
        std::fs::write(&p, "999999999").unwrap();
        let held = take_console_lock(&p).expect("a dead holder's lock is taken over");
        assert_eq!(
            std::fs::read_to_string(&p).unwrap(),
            std::process::id().to_string()
        );
        drop(held);
        assert!(!p.exists(), "released on drop");
        let _ = std::fs::remove_dir_all(&d);
    }
}