xtask/src/forge_door.rs
//! **The media door** — `installer-iso` and `appliance-iso` are thin callers of
//! `dwarves buildthing <this checkout> --variant <medium>`.
//!
//! Owner, 2026-09-27: "BUILD thing !! all must go trough build thing". Every
//! holger medium declares its kernel (`tunnr/tunnr-kernel`), so there is nothing
//! left for this door to compose in-process: the forge builds the payload in the
//! pinned container, reads the donor kernel off its pin, and runs the MEDIA STEPS
//! (dwarves `media_steps`: the root-of-trust decision, the worktree refusal, the
//! plugins the payload consumes — built, signed, held to this commit, staged — and
//! the medium record). Nothing of that lives here; this door owns no build
//! knowledge (the LAW: xtask is a thin door onto the build thing).
//!
//! What the door still does, and why:
//! * resolves `--variant` against the RUST declaration and refuses the wrong KIND
//! by name, before a forge is spawned (a medium for a machine with no shell is
//! one keystroke from the wrong one);
//! * refuses a committed `.nornir/build-variants.toml` that is not the code's
//! emission (the forge discovers the file by path);
//! * reads SUCCESS off the ISO on disk, never off an exit code.
//!
//! The forge binary: `$DWARVES_BIN`, else `cargo run --release -p dwarves
//! --features sign-step` in the sibling dwarves checkout (`$DWARVES_ROOT`, else
//! `../dwarves`). `sign-step` because a medium whose payload consumes a plugin has
//! the forge SIGN it.
use std::path::{Path, PathBuf};
use std::process::Command;
use anyhow::{bail, Context, Result};
use nornir_build_thing::variant::ArtifactKind;
/// The env that composes an open medium unenrolled ON PURPOSE (dwarves
/// `media_steps::UNENROLLED_ENV`); `--unenrolled <why>` sets it.
pub const UNENROLLED_ENV: &str = "DWARVES_UNENROLLED";
/// What the operator asked for.
#[derive(Debug, Default, PartialEq, Eq)]
pub struct Ask {
pub variant: Option<String>,
pub params: Vec<String>,
pub opts: Vec<String>,
/// `--build <edge>` — build a consumed build thing (the kernel) as a step.
pub builds: Vec<String>,
pub unenrolled: Option<String>,
}
/// Parse the passthrough; an unknown flag is refused, never dropped.
pub fn parse(verb: &str, args: impl IntoIterator<Item = String>) -> Result<Ask> {
let mut ask = Ask::default();
let mut args = args.into_iter();
while let Some(a) = args.next() {
let mut val = |flag: &str| args.next().with_context(|| format!("{flag} takes a value"));
match a.as_str() {
"--variant" => ask.variant = Some(val("--variant")?),
"--param" => ask.params.push(val("--param")?),
"--opt" => ask.opts.push(val("--opt")?),
"--build" => ask.builds.push(val("--build")?),
"--unenrolled" => ask.unenrolled = Some(val("--unenrolled")?),
"--kernel" | "--allow-host-kernel" | "--app" => bail!(
"{verb}: `{a}` is gone — every holger medium DECLARES its kernel (tunnr/tunnr-kernel) \
and its payload, and the Build Thing builds both. Build the kernel with `--build \
kernel` (or `dwarves buildthing tunnr --variant tunnr-kernel`)."
),
other => bail!(
"unknown argument '{other}' ({verb} takes --variant <name>, --param k=v, --opt k=v, \
--build <edge>, --unenrolled <why>)"
),
}
}
Ok(ask)
}
/// Resolve `name` to a declared variant of `kind`, refusing another kind BY NAME.
/// Returns the DELIVERY (suite) the forge builds it in — the variant's first
/// declared suite, read off the declaration (one, MVP), so the forge never has to
/// guess (`say which one with --suite`).
pub fn declared_medium(name: &str, kind: ArtifactKind, door_of_other: &str) -> Result<String> {
let set = holger_recipe::declared();
let v = set.require(name)?;
if v.kind != kind {
let of_kind: Vec<&str> = set
.variants
.iter()
.filter(|v| v.kind == kind)
.map(|v| v.name.as_str())
.collect();
bail!(
"variant '{name}' is kind '{}', not '{}'{} (declared: {})",
String::from(v.kind),
String::from(kind),
if matches!(v.kind, ArtifactKind::Installer | ArtifactKind::Iso) {
format!(" — '{name}' is built by `{door_of_other} --variant {name}`")
} else {
String::new()
},
of_kind.join(", ")
);
}
let has_row = match kind {
ArtifactKind::Installer => set.installers.iter().any(|i| i.name == name),
ArtifactKind::Iso => set.isos.iter().any(|i| i.name == name),
_ => false,
};
if !has_row {
bail!(
"variant '{name}' has no [[build.{}]] row of the same name",
String::from(kind)
);
}
v.suites.first().cloned().with_context(|| {
format!("variant '{name}' declares no suite — the forge cannot select it by delivery")
})
}
/// **The sandbox entries a row packs are staged first** (lane HOLGER-MEDIA): a
/// `[[build.installer]]` row whose `payload_entries` name `.sandbox-out/` gets
/// `cargo xtask scan-worker stage` run here when the staging is absent or does not
/// match its manifest, so the forge never refuses "payload entry not there" after
/// a kernel build, and a stale staging never rides a medium. A row that names
/// nothing stages nothing (the front).
pub fn ensure_payload_entries_staged(repo_root: &Path, variant: &str) -> Result<()> {
if !stages_the_sandbox(variant) {
return Ok(());
}
let out = repo_root.join(crate::scan_worker::STAGE_OUT);
// The tree's worker, built now (incremental: quick when it is up to date), so a staging
// from another tree never rides the medium (HOL-NCSC).
let worker = crate::scan_worker::build(repo_root)?;
let worker_bytes = std::fs::read(&worker).with_context(|| worker.display().to_string())?;
match crate::scan_worker::staged_ok(&out)
.and_then(|m| crate::scan_worker::worker_is_current(&m, &worker_bytes).map(|()| m))
.and_then(|m| crate::scan_worker::heimdall_is_current(repo_root, &m).map(|()| m))
{
Ok(m) => eprintln!(
"sandbox entries staged in {} ({} entries, as the manifest says, the tree's worker)",
out.display(),
m.entries.len()
),
Err(why) => {
eprintln!("sandbox entries: {why:#}; staging now (cargo xtask scan-worker stage)");
crate::scan_worker::stage(repo_root, &out, Some(worker), None)?;
}
}
// HOL-OCI-EVIDENCE: the release's signed Sigstore trust root rides beside the sandbox.
match crate::sigstore_trust::staged_ok(&out) {
Ok(()) => {
eprintln!(
"sigstore trust root staged in {} (the pinned root)",
out.display()
);
Ok(())
}
Err(why) => {
eprintln!(
"sigstore trust root: {why:#}; staging now (cargo xtask sigstore-trust stage)"
);
crate::sigstore_trust::stage(
repo_root,
&out,
None,
&crate::sigstore_trust::signer_dir(),
730,
)
}
}
}
/// Pure: **whether the forge stages the sandbox for `variant`**: the medium names a payload
/// entry under `.sandbox-out/`.
pub fn stages_the_sandbox(variant: &str) -> bool {
use holger_recipe::packaging as p;
holger_recipe::declared()
.installers
.into_iter()
.find(|i| i.name == variant)
.is_some_and(|i| {
i.payload_entries
.values()
.any(|src| src.starts_with(p::SANDBOX_STAGE_DIR))
})
}
/// Where the forge leaves the medium.
pub fn iso_out(repo_root: &Path, variant: &str) -> PathBuf {
repo_root.join(".iso-out").join(format!("{variant}.iso"))
}
/// The argv after the forge binary: `buildthing <root> --variant <v> …`.
pub fn forge_args(repo_root: &Path, suite: &str, variant: &str, ask: &Ask) -> Vec<String> {
let mut a = vec![
"buildthing".to_string(),
repo_root.display().to_string(),
"--suite".to_string(),
suite.to_string(),
"--variant".to_string(),
variant.to_string(),
];
for (flag, vals) in [
("--param", &ask.params),
("--opt", &ask.opts),
("--build", &ask.builds),
] {
for v in vals {
a.push(flag.to_string());
a.push(v.clone());
}
}
a
}
/// The variable naming a prebuilt forge binary.
pub const DWARVES_BIN_ENV: &str = "DWARVES_BIN";
/// The variable naming the dwarves checkout the forge is run from.
pub const DWARVES_ROOT_ENV: &str = "DWARVES_ROOT";
/// The forge: `$DWARVES_BIN`, else `cargo run` in the sibling dwarves checkout.
fn forge(repo_root: &Path) -> Result<Command> {
if let Some(bin) = std::env::var_os(DWARVES_BIN_ENV).filter(|v| !v.is_empty()) {
return Ok(Command::new(bin));
}
let root = std::env::var_os(DWARVES_ROOT_ENV)
.filter(|v| !v.is_empty())
.map(PathBuf::from)
.unwrap_or_else(|| {
repo_root
.parent()
.map(|p| p.join("dwarves"))
.unwrap_or_default()
});
if !root.join("Cargo.toml").is_file() {
bail!(
"no forge: set $DWARVES_BIN to a dwarves built with `--features sign-step`, or put the \
dwarves checkout at {} (or point $DWARVES_ROOT at it)",
root.display()
);
}
let mut c = Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()));
c.current_dir(root)
.args([
"run",
"--release",
"-p",
"dwarves",
"--features",
"sign-step",
"--bin",
"dwarves",
"--",
])
// `cargo run` exports CARGO_MANIFEST_DIR to its child; a grandchild
// inheriting it makes ring & co rebuild (gunnar, measured 2026-09-23).
.env_remove("CARGO_MANIFEST_DIR");
Ok(c)
}
/// **Run the door**: declaration checks, the forge, and the ISO read off disk.
pub fn run(verb: &str, repo_root: &Path, suite: &str, variant: &str, ask: &Ask) -> Result<PathBuf> {
holger_recipe::require_committed_is_emitted(repo_root)?;
ensure_payload_entries_staged(repo_root, variant)?;
let mut cmd = forge(repo_root)?;
cmd.args(forge_args(repo_root, suite, variant, ask));
if let Some(why) = &ask.unenrolled {
cmd.env(UNENROLLED_ENV, why);
}
eprintln!("{verb}: `{variant}` through the Build Thing: {:?}", cmd);
let status = cmd.status().context("spawn the forge")?;
if !status.success() {
bail!("the Build Thing exited {status} composing `{variant}`");
}
let iso = iso_out(repo_root, variant);
let bytes = std::fs::metadata(&iso)
.with_context(|| {
format!(
"the forge reported success but {} is absent — an exit code is not an artifact",
iso.display()
)
})?
.len();
if bytes == 0 {
bail!("{} exists but is EMPTY", iso.display());
}
let record = iso.with_extension("medium.json");
let sha = std::fs::read_to_string(&record)
.ok()
.and_then(|s| serde_json::from_str::<serde_json::Value>(&s).ok())
.and_then(|v| v["iso_sha256"].as_str().map(str::to_string));
println!(
"✅ {verb}: {} ({bytes} bytes){}",
iso.display(),
sha.map(|s| format!(" sha256 {s} · record {}", record.display()))
.unwrap_or_default()
);
Ok(iso)
}
#[cfg(test)]
mod tests {
use super::*;
fn s(v: &[&str]) -> Vec<String> {
v.iter().map(|s| s.to_string()).collect()
}
/// Lane HOL-CDS-5: every declared medium that carries the sandbox (the downstream media
/// too: their bundles are verified in jobs) gets it staged, the sigstore trust with it;
/// a medium that carries none (the front) stages nothing.
#[test]
fn every_medium_that_carries_the_sandbox_has_it_staged() {
let set = holger_recipe::declared();
let mut carrying = 0;
for i in &set.installers {
let carries = i
.payload_entries
.values()
.any(|src| src.starts_with(holger_recipe::packaging::SANDBOX_STAGE_DIR));
carrying += usize::from(carries);
assert_eq!(stages_the_sandbox(&i.name), carries, "{}", i.name);
}
assert!(
carrying >= 5,
"the main media and both downstream media carry it"
);
assert!(stages_the_sandbox("holger-downstream-oneway-installer"));
assert!(!stages_the_sandbox("no-such-medium"));
}
#[test]
fn the_passthrough_reaches_the_forge_in_order() {
let ask = parse(
"installer-iso",
s(&[
"--variant",
"holger-installer-metered",
"--param",
"holger_ron=@x.ron",
"--opt",
"kernel.preset=nuked",
"--build",
"kernel",
"--param",
"site=a",
]),
)
.unwrap();
assert_eq!(
forge_args(
Path::new("/w/holger"),
"MVP",
"holger-installer-metered",
&ask
),
s(&[
"buildthing",
"/w/holger",
"--suite",
"MVP",
"--variant",
"holger-installer-metered",
"--param",
"holger_ron=@x.ron",
"--param",
"site=a",
"--opt",
"kernel.preset=nuked",
"--build",
"kernel"
])
);
}
#[test]
fn the_operator_kernel_is_gone_and_says_where_it_went() {
for flag in ["--kernel", "--allow-host-kernel", "--app"] {
let e = parse("installer-iso", s(&[flag, "/boot/vmlinuz"]))
.unwrap_err()
.to_string();
assert!(
e.contains("DECLARES its kernel") && e.contains("--build kernel"),
"{e}"
);
}
let e = parse("installer-iso", s(&["--atom", "x"]))
.unwrap_err()
.to_string();
assert!(e.contains("unknown argument '--atom'"), "{e}");
}
#[test]
fn the_wrong_kind_is_refused_and_pointed_at_its_door() {
assert_eq!(
declared_medium("holger-installer-metered", ArtifactKind::Installer, "x").unwrap(),
"MVP"
);
assert!(declared_medium("holger-appliance-iso", ArtifactKind::Iso, "x").is_ok());
let e = declared_medium(
"holger-appliance-iso",
ArtifactKind::Installer,
"cargo xtask appliance-iso",
)
.unwrap_err()
.to_string();
assert!(
e.contains("not 'installer'") && e.contains("cargo xtask appliance-iso"),
"{e}"
);
let e = declared_medium("mvp-server-musl", ArtifactKind::Installer, "x")
.unwrap_err()
.to_string();
assert!(
e.contains("holger-installer-metered"),
"the refusal lists the declared media: {e}"
);
}
}