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

nordisk/holger

xtask/src/downstream.rs

//! **A downstream's medium and its VM** (lane OX-WGDS3, 2026-10-28): the two verbs the ONE-box
//! gate (`gate holger` in private-monetize-ops) lays its downstreams with, beside the laid
//! estate — Holger's own, as `installtest` is.
//!
//! ```text
//! cargo xtask downstream-media --name <n> --upstream <up> --upstream-key <hex> --via front|wireguard
//!                              --mirror <url> [--ca <pem>] [--tls-name <name>] [--authority <name>] --bearer-file <f> --out <iso>
//! cargo xtask downstream-vm --iso <iso> --work <dir> --door <host-port> [--mem-mb N] [--install-min N]
//! ```
//!
//! * **`downstream-media`** renders the downstream's declaration (holger-recipe
//!   [`holger_recipe::downstream_ron`]) and composes `holger-downstream-installer` through the
//!   Build Thing as `installtest` composes its medium (no pairing seed: no installer pairs a
//!   downstream), the declaration on its release (`holger_ron`) and, `via: front`, the CA of
//!   the upstream's front (`downstream_ca`). The medium, its record (`<iso>.medium.json`) and
//!   the declaration (`<iso>.holger.ron`) are moved to `--out`, never left in `.iso-out` (a
//!   medium with an operator's bearer is never uploaded). Prints `MEDIUM <iso>`.
//! * **`downstream-vm`** lays the medium in KVM on a disk under `--work` once (SeaBIOS, the
//!   medium as an IDE cdrom, an 8 GiB boot disk and a 4 GiB data disk; `<work>/laid.ok` says
//!   it was laid), then boots the laid disks with ONE user-mode NIC — not restricted, so the
//!   guest reaches this host's loopback as `10.0.2.2` — forwarding `127.0.0.1:<door>` to the
//!   guest's HTTP door (8443). Once the door answers it prints `UP 127.0.0.1:<door>` and the
//!   VM stays up until the verb is stopped: by its process group (SIGTERM to `-<pgid>`
//!   reaches QEMU too), a file `<work>/stop`, or its parent ending. Run again, it boots the
//!   kept disks: a restart.

use std::io::Write as _;
use std::path::{Path, PathBuf};
use std::time::Duration;

use anyhow::{bail, Context, Result};
use holger_recipe::downstream_ron::{self, DownstreamRon, Via};
use holger_recipe::packaging as p;
use korp_installer::isoproof::serve::{Disk, InstallThenServe, MediumAttach, Probe};
use korp_installer::isoproof::SlirpNic;
use korp_installer::kvmproof::{self, Machine};
use nornir_build_thing::variant::ArtifactKind;

use crate::forge_door;

/// The medium both verbs are about.
pub const VARIANT: &str = p::DOWNSTREAM_INSTALLER;
/// The box's HTTP door (the declaration's `ron_http_url`).
pub const HTTP_DOOR: u16 = 8443;
/// The disks of a downstream VM: the boot disk the larger (korp lays the ESP on it).
pub const BOOT_DISK_GIB: u64 = 8;
pub const DATA_DISK_GIB: u64 = 4;
/// What says the disks under `--work` are laid.
pub const LAID: &str = "laid.ok";
/// What stops a running VM cleanly.
pub const STOP: &str = "stop";

/// `downstream-media`'s words.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MediaOpts {
    pub name: String,
    pub upstream: String,
    pub upstream_key: String,
    pub via: String,
    pub mirror: String,
    pub ca: Option<PathBuf>,
    pub tls_name: Option<String>,
    /// `--authority`: the calls' `:authority` when it is not the TLS name.
    pub authority: Option<String>,
    pub bearer_file: PathBuf,
    pub out: PathBuf,
}

/// `downstream-vm`'s words.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VmOpts {
    pub iso: PathBuf,
    pub work: PathBuf,
    pub door: u16,
    pub mem_mb: u32,
    pub install_min: u64,
}

/// Pure: `downstream-media`'s words; a stray or missing word is refused by name.
pub fn parse_media(args: &[String]) -> Result<MediaOpts> {
    let (
        mut name,
        mut upstream,
        mut key,
        mut via,
        mut mirror,
        mut ca,
        mut tls,
        mut bearer,
        mut out,
    ) = (None, None, None, None, None, None, None, None, None);
    let mut authority = None;
    let mut it = args.iter();
    while let Some(a) = it.next() {
        let mut val = |what: &str| {
            it.next()
                .cloned()
                .with_context(|| format!("downstream-media: {a} takes {what}"))
        };
        match a.as_str() {
            "--name" => name = Some(val("a name")?),
            "--upstream" => upstream = Some(val("the upstream's sync name")?),
            "--upstream-key" => key = Some(val("the upstream's sync key (hex)")?),
            "--via" => via = Some(val("front or wireguard")?),
            "--mirror" => mirror = Some(val("a URL")?),
            "--ca" => ca = Some(PathBuf::from(val("a PEM file")?)),
            "--tls-name" => tls = Some(val("a DNS name")?),
            "--authority" => authority = Some(val("a DNS name")?),
            "--bearer-file" => bearer = Some(PathBuf::from(val("a file")?)),
            "--out" => out = Some(PathBuf::from(val("the medium's path")?)),
            other => bail!("downstream-media: `{other}`: cargo xtask downstream-media --name <n> --upstream <up> --upstream-key <hex> --via front|wireguard --mirror <url> [--ca <pem>] [--tls-name <name>] [--authority <name>] --bearer-file <f> --out <iso>"),
        }
    }
    let need = |v: Option<String>, w: &str| {
        v.with_context(|| format!("downstream-media: {w} is required"))
    };
    let o = MediaOpts {
        name: need(name, "--name")?,
        upstream: need(upstream, "--upstream")?,
        upstream_key: need(key, "--upstream-key")?,
        via: need(via, "--via")?,
        mirror: need(mirror, "--mirror")?,
        ca,
        tls_name: tls,
        authority,
        bearer_file: bearer.context("downstream-media: --bearer-file is required")?,
        out: out.context("downstream-media: --out is required")?,
    };
    match (o.via.as_str(), o.ca.is_some(), o.tls_name.is_some() || o.authority.is_some()) {
        ("front", true, _) => {}
        ("front", false, _) => bail!("downstream-media: a `--via front` downstream verifies its upstream's front: --ca <pem> is required"),
        ("wireguard", false, false) => {}
        ("wireguard", _, _) => bail!("downstream-media: a `--via wireguard` downstream dials the front's downstream interface in the clear inside its tunnel: no --ca, no --tls-name, no --authority"),
        (other, _, _) => bail!("downstream-media: --via {other}: `front` or `wireguard`"),
    }
    Ok(o)
}

/// Pure: `downstream-vm`'s words.
pub fn parse_vm(args: &[String]) -> Result<VmOpts> {
    let (mut iso, mut work, mut door, mut mem, mut install) = (None, None, None, 1024u32, 20u64);
    let mut it = args.iter();
    while let Some(a) = it.next() {
        let mut val = |what: &str| {
            it.next()
                .cloned()
                .with_context(|| format!("downstream-vm: {a} takes {what}"))
        };
        match a.as_str() {
            "--iso" => iso = Some(PathBuf::from(val("a medium")?)),
            "--work" => work = Some(PathBuf::from(val("a directory")?)),
            "--door" => door = Some(val("a port")?.parse::<u16>().context("downstream-vm: --door is a port")?),
            "--mem-mb" => mem = val("MiB")?.parse().context("downstream-vm: --mem-mb is MiB")?,
            "--install-min" => install = val("minutes")?.parse().context("downstream-vm: --install-min is minutes")?,
            other => bail!("downstream-vm: `{other}`: cargo xtask downstream-vm --iso <iso> --work <dir> --door <host-port> [--mem-mb N] [--install-min N]"),
        }
    }
    let door = door.context("downstream-vm: --door <host-port> is required")?;
    if door == 0 {
        bail!("downstream-vm: --door 0: name the host port the door is forwarded to");
    }
    Ok(VmOpts {
        iso: iso.context("downstream-vm: --iso is required")?,
        work: work.context("downstream-vm: --work is required")?,
        door,
        mem_mb: mem,
        install_min: install,
    })
}

/// Pure: the declaration of `o` with the bearer `bearer`.
pub fn declaration(o: &MediaOpts, bearer: &str) -> Result<DownstreamRon> {
    let via = match o.via.as_str() {
        "front" => Via::Front {
            server_name: o.tls_name.clone(),
            authority: o.authority.clone(),
        },
        _ => Via::Wireguard,
    };
    DownstreamRon::on_a_box(
        &o.name,
        &o.upstream,
        &o.upstream_key,
        via,
        &o.mirror,
        bearer.trim(),
    )
    .map_err(|e| anyhow::anyhow!(e))
}

/// Pure: the forge's parameters for the staged files (the declaration; the CA when there is one).
pub fn forge_params(ron: &Path, ca: Option<&Path>, korp_root: &Path) -> Vec<String> {
    let mut out = vec![
        format!("holger_ron=@{}", ron.display()),
        format!("plugin_trust_root=@{}", korp_root.display()),
    ];
    if let Some(ca) = ca {
        out.push(format!("downstream_ca=@{}", ca.display()));
    }
    out
}

pub fn run_media() -> Result<()> {
    let args: Vec<String> = std::env::args().skip(2).collect();
    media(&parse_media(&args)?, &crate::workspace_root())
}

fn media(o: &MediaOpts, root: &Path) -> Result<()> {
    let bearer = std::fs::read_to_string(&o.bearer_file)
        .with_context(|| format!("downstream-media: the bearer {}", o.bearer_file.display()))?;
    let d = declaration(o, &bearer)?;
    let ron = downstream_ron::render(&d);
    let out = if o.out.is_absolute() {
        o.out.clone()
    } else {
        std::env::current_dir()?.join(&o.out)
    };
    let dir = out
        .parent()
        .context("downstream-media: --out names no directory")?
        .to_path_buf();
    let stage = dir.join(format!(
        "{}.stage",
        out.file_name()
            .map(|n| n.to_string_lossy().into_owned())
            .unwrap_or_default()
    ));
    std::fs::create_dir_all(&stage)?;
    let staged_ron = stage.join("holger.ron");
    std::fs::write(&staged_ron, &ron)?;
    let korp_root = stage.join(crate::estate_media::KORP_ROOT_FILE);
    std::fs::write(&korp_root, holger_recipe::estate_ron::KORP_ROOT_PEM)?;
    let ca = match &o.ca {
        Some(c) => {
            let pem = std::fs::read_to_string(c)
                .with_context(|| format!("downstream-media: --ca {}", c.display()))?;
            if !pem.contains("-----BEGIN CERTIFICATE-----") {
                bail!(
                    "downstream-media: --ca {} holds no PEM certificate",
                    c.display()
                );
            }
            let to = stage.join(p::DOWNSTREAM_CA_STAGED);
            std::fs::write(&to, pem)?;
            Some(to)
        }
        None => None,
    };
    println!(
        "downstream-media: `{}` of `{}` via {} — mirror {}, {} → {}",
        d.name,
        d.upstream,
        d.via.word(),
        d.mirror,
        VARIANT,
        out.display()
    );
    let suite = forge_door::declared_medium(
        VARIANT,
        ArtifactKind::Installer,
        "cargo xtask downstream-media",
    )?;
    let ask = forge_door::Ask {
        variant: Some(VARIANT.to_string()),
        params: forge_params(&staged_ron, ca.as_deref(), &korp_root),
        builds: vec!["kernel".to_string()],
        ..Default::default()
    };
    let t0 = std::time::Instant::now();
    let iso = forge_door::run("downstream-media", root, &suite, VARIANT, &ask)?;
    // Out of `.iso-out`, with its record and declaration beside it.
    let (rec, app) = crate::estate_media::live_companions(&iso);
    let move_to = |from: &Path, to: &Path| -> Result<()> {
        std::fs::rename(from, to)
            .or_else(|_| {
                std::fs::copy(from, to)
                    .map(|_| ())
                    .and_then(|_| std::fs::remove_file(from))
            })
            .with_context(|| format!("move {} to {}", from.display(), to.display()))
    };
    move_to(&iso, &out)?;
    if iso.with_extension("medium.json").is_file() {
        move_to(
            &iso.with_extension("medium.json"),
            &out.with_extension("medium.json"),
        )?;
    }
    for f in [
        rec,
        app,
        iso.with_extension("live-release.bundle"),
        iso.with_extension("live-release.sig"),
    ] {
        let _ = std::fs::remove_file(f);
    }
    std::fs::write(out.with_extension("holger.ron"), &ron)?;
    let _ = std::fs::remove_dir_all(&stage);
    println!("downstream-media: composed in {} s", t0.elapsed().as_secs());
    println!("MEDIUM {}", out.display());
    Ok(())
}

pub fn run_vm() -> Result<()> {
    let args: Vec<String> = std::env::args().skip(2).collect();
    vm(&parse_vm(&args)?)
}

/// The machine of a downstream VM.
pub fn proof(o: &VmOpts, qemu: PathBuf) -> InstallThenServe {
    let mut machine = Machine::seabios();
    machine.mem_mb = o.mem_mb;
    machine.smp = 2;
    InstallThenServe {
        product: "holger-downstream".into(),
        qemu,
        machine,
        iso: o.iso.clone(),
        attach: MediumAttach::Cdrom,
        disks: vec![
            Disk {
                path: o.work.join("sys.img"),
                bytes: BOOT_DISK_GIB << 30,
            },
            Disk {
                path: o.work.join("data.img"),
                bytes: DATA_DISK_GIB << 30,
            },
        ],
        workdir: o.work.clone(),
        install_budget: Duration::from_secs(o.install_min * 60),
        // Up until it is stopped: a week is "until stopped" for a gate.
        serve_budget: Duration::from_secs(7 * 24 * 3600),
        nics: vec![SlirpNic {
            net: None,
            hostfwds: vec![(o.door, HTTP_DOOR)],
        }],
        nic_model: "virtio-net-pci".into(),
        refuse_serial: Vec::new(),
        probe_every: Duration::from_secs(2),
    }
}

/// Whether the door at `127.0.0.1:<port>` answers HTTP at all.
fn door_answers(port: u16) -> bool {
    use std::io::Read as _;
    let Ok(mut s) = std::net::TcpStream::connect_timeout(
        &std::net::SocketAddr::from(([127, 0, 0, 1], port)),
        Duration::from_secs(2),
    ) else {
        return false;
    };
    let _ = s.set_read_timeout(Some(Duration::from_secs(5)));
    if s.write_all(b"GET /-/sync/key HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n")
        .is_err()
    {
        return false;
    }
    let mut head = [0u8; 12];
    s.read_exact(&mut head).is_ok() && head.starts_with(b"HTTP/1.")
}

fn vm(o: &VmOpts) -> Result<()> {
    let qemu = kvmproof::which("qemu-system-x86_64")
        .context("[downstream-vm-no-qemu] no qemu-system-x86_64 on PATH")?;
    if !Path::new("/dev/kvm").exists() {
        bail!("[downstream-vm-no-kvm] /dev/kvm is absent: a downstream VM runs on a KVM host");
    }
    std::fs::create_dir_all(&o.work)?;
    let _ = std::fs::remove_file(o.work.join(STOP));
    let proof = proof(o, qemu);
    if !o.work.join(LAID).is_file() {
        println!(
            "downstream-vm: laying {} on {} ({} min budget)",
            o.iso.display(),
            o.work.display(),
            o.install_min
        );
        let leg = proof.install();
        println!(
            "  {} install  {} ms  {}",
            if leg.pass { "PASS" } else { "FAIL" },
            leg.ms,
            leg.detail
        );
        if !leg.pass {
            if !leg.serial_tail.is_empty() {
                println!(
                    "  serial tail:\n{}",
                    leg.serial_tail
                        .lines()
                        .map(|l| format!("    | {l}"))
                        .collect::<Vec<_>>()
                        .join("\n")
                );
            }
            bail!("[downstream-vm-install] {}", leg.detail);
        }
        std::fs::write(o.work.join(LAID), format!("{}\n", o.iso.display()))?;
    } else {
        println!(
            "downstream-vm: the disks under {} are laid: booting them again",
            o.work.display()
        );
    }
    let parent = std::os::unix::process::parent_id();
    let mut up = false;
    let t0 = std::time::Instant::now();
    let leg = proof.serve(&mut |_serial| {
        if !up && door_answers(o.door) {
            up = true;
            println!("UP 127.0.0.1:{} ({} s)", o.door, t0.elapsed().as_secs());
            let _ = std::io::stdout().flush();
        }
        if o.work.join(STOP).exists() {
            return Probe::Done(Ok("stopped by its stop file".into()));
        }
        if std::os::unix::process::parent_id() != parent {
            return Probe::Done(Ok("its parent ended".into()));
        }
        Probe::Wait
    });
    println!(
        "downstream-vm: {} ({})",
        if up { "stopped" } else { "never served" },
        leg.detail
    );
    if !up {
        if !leg.serial_tail.is_empty() {
            println!(
                "  serial tail:\n{}",
                leg.serial_tail
                    .lines()
                    .map(|l| format!("    | {l}"))
                    .collect::<Vec<_>>()
                    .join("\n")
            );
        }
        bail!(
            "[downstream-vm-serve] the door 127.0.0.1:{} never answered: {}",
            o.door,
            leg.detail
        );
    }
    Ok(())
}

// ───────────────────────── the one-way appliance (lane HOL-CDS-5) ─────────────────────────

/// The one-way medium the proof composes.
pub const ONEWAY_VARIANT: &str = p::ONEWAY_INSTALLER;
/// The proof's security domain (`domain:`), checked on every answer.
pub const PROOF_DOMAIN: &str = "kvm-lab";
/// The size of the proof's USB stick.
pub const STICK_MIB: u64 = 64;

/// `downstream-oneway-proof`'s words.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProofOpts {
    /// The fixture (`cargo test -p holger --test one_way_downstream -- --ignored
    /// write_the_one_way_appliance_fixture`): the bundle, `trust-policy.ron`,
    /// `upstream-root.pem`, `upstream-signer.pem`, `fixture.json`.
    pub fixture: PathBuf,
    pub work: PathBuf,
    pub door: u16,
    pub mem_mb: u32,
    pub install_min: u64,
    /// The medium's network (`holger_net`); `None`: the medium's default `eth0:dhcp`.
    pub net: Option<String>,
    /// A medium composed before (skip the compose).
    pub iso: Option<PathBuf>,
    /// Compose the medium and stop (no KVM: it needs no turn on the KVM lock).
    pub compose_only: bool,
}

/// Pure: `downstream-oneway-proof`'s words; a stray word is refused by name.
pub fn parse_proof(args: &[String]) -> Result<ProofOpts> {
    let mut o = ProofOpts {
        fixture: PathBuf::new(),
        work: PathBuf::new(),
        door: 0,
        mem_mb: 1024,
        install_min: 20,
        net: None,
        iso: None,
        compose_only: false,
    };
    let mut it = args.iter();
    while let Some(a) = it.next() {
        let mut v = || {
            it.next()
                .cloned()
                .with_context(|| format!("[downstream-oneway-proof-args] {a} takes a value"))
        };
        match a.as_str() {
            "--fixture" => o.fixture = PathBuf::from(v()?),
            "--work" => o.work = PathBuf::from(v()?),
            "--door" => o.door = v()?.parse().context("[downstream-oneway-proof-args] --door is a port")?,
            "--mem-mb" => o.mem_mb = v()?.parse().context("[downstream-oneway-proof-args] --mem-mb is a number")?,
            "--install-min" => o.install_min = v()?.parse().context("[downstream-oneway-proof-args] --install-min is a number")?,
            "--net" => o.net = Some(v()?),
            "--iso" => o.iso = Some(PathBuf::from(v()?)),
            "--compose-only" => o.compose_only = true,
            other => bail!("[downstream-oneway-proof-args] `{other}` is not a word of downstream-oneway-proof (--fixture --work --door [--mem-mb] [--install-min] [--net] [--iso])"),
        }
    }
    if o.fixture.as_os_str().is_empty()
        || o.work.as_os_str().is_empty()
        || (o.door == 0 && !o.compose_only)
    {
        bail!("[downstream-oneway-proof-args] --fixture, --work and --door are required");
    }
    Ok(o)
}

/// The fixture, read and checked.
#[derive(Debug, Clone)]
pub struct Fixture {
    pub dir: PathBuf,
    pub bundle: String,
    pub vendor: String,
    pub product: String,
    pub sequence: u64,
    pub project: String,
    pub wheel: String,
    pub wheel_sha256: String,
}

pub fn read_fixture(dir: &Path) -> Result<Fixture> {
    let j: serde_json::Value =
        serde_json::from_slice(&std::fs::read(dir.join("fixture.json")).with_context(|| {
            format!(
                "[downstream-oneway-proof-fixture] {}/fixture.json",
                dir.display()
            )
        })?)?;
    let s = |k: &str| {
        j[k].as_str().map(str::to_string).with_context(|| {
            format!("[downstream-oneway-proof-fixture] fixture.json names no `{k}`")
        })
    };
    let f = Fixture {
        dir: dir.to_path_buf(),
        bundle: s("bundle")?,
        vendor: s("vendor")?,
        product: s("product")?,
        sequence: j["sequence"]
            .as_u64()
            .context("[downstream-oneway-proof-fixture] fixture.json names no `sequence`")?,
        project: s("project")?,
        wheel: s("wheel")?,
        wheel_sha256: s("wheel_sha256")?,
    };
    for need in [
        f.bundle.as_str(),
        "trust-policy.ron",
        "upstream-root.pem",
        "upstream-signer.pem",
    ] {
        if !dir.join(need).is_file() {
            bail!(
                "[downstream-oneway-proof-fixture] {} has no {need}",
                dir.display()
            );
        }
    }
    Ok(f)
}

/// Pure: the forge's parameters of the one-way medium (every staged file by its declared name).
pub fn oneway_forge_params(stage: &Path, net: bool) -> Vec<String> {
    let mut out = vec![
        format!("holger_ron=@{}", stage.join("holger.ron").display()),
        format!(
            "trust_policy=@{}",
            stage.join(p::ONEWAY_TRUST_POLICY_STAGED).display()
        ),
        format!(
            "upstream_root=@{}",
            stage.join(p::ONEWAY_ROOT_STAGED).display()
        ),
        format!(
            "upstream_signer=@{}",
            stage.join(p::ONEWAY_SIGNER_STAGED).display()
        ),
        format!(
            "plugin_trust_root=@{}",
            stage.join(crate::estate_media::KORP_ROOT_FILE).display()
        ),
    ];
    if net {
        out.push(format!(
            "holger_net=@{}",
            stage.join(p::ONEWAY_NET_STAGED).display()
        ));
    }
    out
}

/// Compose the one-way medium from the fixture's trust (what an admin provisions), into `out`.
fn oneway_media(
    f: &Fixture,
    net: Option<&str>,
    bearer: &str,
    out: &Path,
    root: &Path,
) -> Result<()> {
    let r = downstream_ron::OneWayRon::on_a_box("kvm-proof", PROOF_DOMAIN, bearer)
        .map_err(|e| anyhow::anyhow!(e))?;
    let ron = downstream_ron::render_one_way(&r);
    let stage = out.with_extension("stage");
    std::fs::create_dir_all(&stage)?;
    std::fs::write(stage.join("holger.ron"), &ron)?;
    std::fs::write(
        stage.join(crate::estate_media::KORP_ROOT_FILE),
        holger_recipe::estate_ron::KORP_ROOT_PEM,
    )?;
    for (from, to) in [
        ("trust-policy.ron", p::ONEWAY_TRUST_POLICY_STAGED),
        ("upstream-root.pem", p::ONEWAY_ROOT_STAGED),
        ("upstream-signer.pem", p::ONEWAY_SIGNER_STAGED),
    ] {
        std::fs::copy(f.dir.join(from), stage.join(to))?;
    }
    if let Some(n) = net {
        std::fs::write(stage.join(p::ONEWAY_NET_STAGED), n)?;
    }
    let suite = forge_door::declared_medium(
        ONEWAY_VARIANT,
        ArtifactKind::Installer,
        "cargo xtask downstream-oneway-proof",
    )?;
    let ask = forge_door::Ask {
        variant: Some(ONEWAY_VARIANT.to_string()),
        params: oneway_forge_params(&stage, net.is_some()),
        builds: vec!["kernel".to_string()],
        ..Default::default()
    };
    let t0 = std::time::Instant::now();
    let iso = forge_door::run(
        "downstream-oneway-proof",
        root,
        &suite,
        ONEWAY_VARIANT,
        &ask,
    )?;
    std::fs::rename(&iso, out).or_else(|_| {
        std::fs::copy(&iso, out)
            .map(|_| ())
            .and_then(|_| std::fs::remove_file(&iso))
    })?;
    std::fs::write(out.with_extension("holger.ron"), &ron)?;
    let _ = std::fs::remove_dir_all(&stage);
    println!(
        "downstream-oneway-proof: the one-way medium composed in {} s: {}",
        t0.elapsed().as_secs(),
        out.display()
    );
    Ok(())
}

/// The USB stick: a FAT32 image labelled `HOLGERINBOX` with the fixture's bundle at its top
/// level (korp-installer's FAT writer, no root and no mkfs needed; the appliance's kernel reads
/// FAT, its ESP is one).
fn make_stick(f: &Fixture, img: &Path) -> Result<()> {
    let bundle = std::fs::read(f.dir.join(&f.bundle))?;
    let mut image = korp_installer::esp::build_esp_image(
        STICK_MIB << 20,
        &[korp_installer::esp::EspFile::new(f.bundle.clone(), bundle)],
    )
    .context("[downstream-oneway-proof-stick] format the FAT stick")?;
    // The boot sector's FAT32 volume label (offset 71, 11 bytes): the stick's name.
    if !(image[66] == 0x29 && &image[82..87] == b"FAT32") {
        bail!("[downstream-oneway-proof-stick] the stick is not FAT32");
    }
    image[71..82].copy_from_slice(b"HOLGERINBOX");
    std::fs::write(img, image)?;
    Ok(())
}

/// One HTTP/1.1 call to `127.0.0.1:<port>`: `(status, lower-cased headers, body)`.
pub fn http(
    port: u16,
    method: &str,
    path: &str,
    headers: &[(&str, &str)],
    body: &[u8],
) -> Result<(u16, std::collections::BTreeMap<String, String>, Vec<u8>)> {
    use std::io::Read as _;
    let mut s = std::net::TcpStream::connect_timeout(
        &std::net::SocketAddr::from(([127, 0, 0, 1], port)),
        Duration::from_secs(3),
    )?;
    s.set_read_timeout(Some(Duration::from_secs(20)))?;
    let mut head = format!("{method} {path} HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\nContent-Length: {}\r\n", body.len());
    for (k, v) in headers {
        head.push_str(&format!("{k}: {v}\r\n"));
    }
    head.push_str("\r\n");
    s.write_all(head.as_bytes())?;
    s.write_all(body)?;
    let mut raw = Vec::new();
    s.read_to_end(&mut raw)?;
    let split = raw
        .windows(4)
        .position(|w| w == b"\r\n\r\n")
        .context("no HTTP answer")?;
    let text = String::from_utf8_lossy(&raw[..split]).to_string();
    let mut lines = text.lines();
    let status = lines
        .next()
        .and_then(|l| l.split_whitespace().nth(1))
        .and_then(|c| c.parse().ok())
        .context("no status")?;
    let h: std::collections::BTreeMap<String, String> = lines
        .filter_map(|l| l.split_once(':'))
        .map(|(k, v)| (k.trim().to_ascii_lowercase(), v.trim().to_string()))
        .collect();
    let mut b = raw[split + 4..].to_vec();
    if h.get("transfer-encoding")
        .is_some_and(|v| v.eq_ignore_ascii_case("chunked"))
    {
        let mut out = Vec::new();
        let mut rest = &b[..];
        while let Some(nl) = rest.windows(2).position(|w| w == b"\r\n") {
            let n =
                usize::from_str_radix(String::from_utf8_lossy(&rest[..nl]).trim(), 16).unwrap_or(0);
            if n == 0 || rest.len() < nl + 2 + n {
                break;
            }
            out.extend_from_slice(&rest[nl + 2..nl + 2 + n]);
            rest = &rest[(nl + 2 + n + 2).min(rest.len())..];
        }
        b = out;
    }
    Ok((status, h, b))
}

/// One QMP command to the VM's monitor socket.
fn qmp(sock: &Path, command: &serde_json::Value) -> Result<String> {
    use std::io::{BufRead as _, BufReader};
    let s = std::os::unix::net::UnixStream::connect(sock)
        .with_context(|| format!("[downstream-oneway-proof-qmp] {}", sock.display()))?;
    s.set_read_timeout(Some(Duration::from_secs(10)))?;
    let mut r = BufReader::new(s.try_clone()?);
    let mut line = String::new();
    r.read_line(&mut line)?; // the greeting
    let mut w = s;
    w.write_all(b"{\"execute\":\"qmp_capabilities\"}\n")?;
    line.clear();
    r.read_line(&mut line)?;
    w.write_all(format!("{command}\n").as_bytes())?;
    let mut answer = String::new();
    // Events may come first; the command's answer has `return` or `error`.
    for _ in 0..20 {
        answer.clear();
        r.read_line(&mut answer)?;
        if answer.contains("\"return\"") || answer.contains("\"error\"") {
            break;
        }
    }
    if answer.contains("\"error\"") {
        bail!("[downstream-oneway-proof-qmp] {command}: {answer}");
    }
    Ok(answer)
}

pub fn run_oneway_proof() -> Result<()> {
    let args: Vec<String> = std::env::args().skip(2).collect();
    oneway_proof(&parse_proof(&args)?, &crate::workspace_root())
}

/// ★ **The one-way appliance, proven in KVM**: the medium composed with the fixture's trust,
/// laid once, booted with ONE user-mode NIC in `restrict=on` (the guest reaches nothing but the
/// door forwarded to it); the admin authorizes the first import over the door; a USB stick with
/// the signed bundle is plugged in (QMP hot-plug); the box copies it into its inbox, verifies and
/// lands it, and serves the wheel to pip with `X-Holger-Domain`; then the power button stops it
/// cleanly. Prints `PROOF-OK` and writes `<work>/proof.json`.
fn oneway_proof(o: &ProofOpts, root: &Path) -> Result<()> {
    let f = read_fixture(&o.fixture)?;
    std::fs::create_dir_all(&o.work)?;
    let bearer = format!(
        "proof-admin-{}",
        blake3::hash(format!("{}{}", std::process::id(), now_ms()).as_bytes()).to_hex()
    );
    let iso = match &o.iso {
        Some(i) => i.clone(),
        None => {
            let iso = o.work.join("oneway.iso");
            oneway_media(&f, o.net.as_deref(), &bearer, &iso, root)?;
            iso
        }
    };
    if o.compose_only {
        println!("MEDIUM {}", iso.display());
        return Ok(());
    }
    let qemu = kvmproof::which("qemu-system-x86_64")
        .context("[downstream-oneway-proof-no-qemu] no qemu-system-x86_64 on PATH")?;
    if !Path::new("/dev/kvm").exists() {
        bail!("[downstream-oneway-proof-no-kvm] /dev/kvm is absent");
    }
    // The bearer the medium was composed with (a kept medium's declaration says it).
    let bearer = std::fs::read_to_string(iso.with_extension("holger.ron"))
        .ok()
        .and_then(|t| {
            t.split("token: Some(\"")
                .nth(1)
                .and_then(|r| r.split('"').next())
                .map(str::to_string)
        })
        .unwrap_or(bearer);
    let vm = VmOpts {
        iso: iso.clone(),
        work: o.work.join("vm"),
        door: o.door,
        mem_mb: o.mem_mb,
        install_min: o.install_min,
    };
    std::fs::create_dir_all(&vm.work)?;
    let laying = proof(&vm, qemu.clone());
    let t0 = std::time::Instant::now();
    let leg = laying.install();
    println!(
        "  {} install  {} ms  {}",
        if leg.pass { "PASS" } else { "FAIL" },
        leg.ms,
        leg.detail
    );
    if !leg.pass {
        println!("  serial tail:\n{}", leg.serial_tail);
        bail!("[downstream-oneway-proof-install] {}", leg.detail);
    }
    let stick = o.work.join("stick.img");
    make_stick(&f, &stick)?;
    // The serve leg, by hand: the laid disks, ONE restricted user-mode NIC, the monitor. Boot 1
    // hot-plugs the stick after the admin's grant (the site's real order: the box serves, the
    // admin authorizes, the stick goes in). A guest that takes no hot-plugged disk is stopped
    // cleanly and booted again with the stick in (the grant is kept on its data disk).
    let sock = o.work.join("qmp.sock");
    let page = format!("/production/simple/{}/", f.project);
    let auth = format!("Bearer {bearer}");
    let grant = format!(
        "/-/bundle/first-import?vendor={}&product={}&sequence={}",
        f.vendor, f.product, f.sequence
    );
    let mut steps: Vec<(String, bool, String)> = Vec::new();
    let mut served_at = None;
    let mut serial_all = String::new();
    let mut clean_stops = 0u32;
    for boot in 1..=2u8 {
        let stick_at_boot = boot == 2;
        let _ = std::fs::remove_file(&sock);
        let mut args = Vec::new();
        for d in &laying.disks {
            args.push("-drive".to_string());
            args.push(format!("file={},if=virtio,format=raw", d.path.display()));
        }
        if stick_at_boot {
            args.push("-drive".to_string());
            args.push(format!(
                "file={},if=virtio,format=raw,readonly=on",
                stick.display()
            ));
        }
        args.extend([
            "-netdev".to_string(),
            format!(
                "user,id=n0,restrict=on,hostfwd=tcp:127.0.0.1:{}-:{HTTP_DOOR}",
                o.door
            ),
            "-device".to_string(),
            "virtio-net-pci,netdev=n0".to_string(),
        ]);
        args.extend([
            "-qmp".to_string(),
            format!("unix:{},server=on,wait=off", sock.display()),
            "-boot".to_string(),
            "c".to_string(),
        ]);
        // 0 door, 1 grant (+ plug), 2 served?, 3 stopping, 4 stopped, 8 hot-plug gave nothing, 9 red
        let mut stage = 0u8;
        let mut plugged_at: Option<std::time::Instant> = None;
        let mut said = String::new();
        let dir = o.work.join(format!("serve-{boot}"));
        let run = kvmproof::boot_hold_opts(
            &qemu,
            &laying.machine,
            &args,
            &dir,
            Duration::from_secs(o.install_min * 60),
            false,
            |serial| {
                said = serial.to_string();
                match stage {
                    0 if door_answers(o.door) => {
                        steps.push((
                            format!("boot {boot}: door"),
                            true,
                            format!("UP 127.0.0.1:{} after {} s", o.door, t0.elapsed().as_secs()),
                        ));
                        stage = if boot == 1 { 1 } else { 2 };
                    }
                    1 => {
                        let r = http(o.door, "POST", &grant, &[("Authorization", &auth)], b"");
                        let ok = matches!(&r, Ok((200, _, _)));
                        steps.push((
                            "the admin's first-import grant".into(),
                            ok,
                            format!(
                                "{:?}",
                                r.as_ref()
                                    .map(|(c, _, b)| (c, String::from_utf8_lossy(b).to_string()))
                            ),
                        ));
                        if !ok {
                            stage = 9;
                        } else {
                            let plug = qmp(&sock, &serde_json::json!({"execute": "blockdev-add", "arguments": {"driver": "raw", "node-name": "stick", "read-only": true, "file": {"driver": "file", "filename": stick.display().to_string()}}}))
                            .and_then(|_| qmp(&sock, &serde_json::json!({"execute": "device_add", "arguments": {"driver": "virtio-blk-pci", "drive": "stick", "id": "stick0"}})));
                            steps.push(("the stick hot-plugged".into(), true, format!("{plug:?}")));
                            plugged_at = Some(std::time::Instant::now());
                            stage = 2;
                        }
                    }
                    2 => {
                        if let Ok((200, h, b)) = http(o.door, "GET", &page, &[], b"") {
                            if String::from_utf8_lossy(&b)
                                .contains(&format!("#sha256={}", f.wheel_sha256))
                            {
                                let domain = h.get("x-holger-domain").cloned().unwrap_or_default();
                                steps.push((
                                    "pip index".into(),
                                    true,
                                    format!(
                                        "{page} lists {} sha256 {} (boot {boot})",
                                        f.wheel, f.wheel_sha256
                                    ),
                                ));
                                steps.push((
                                    "domain".into(),
                                    domain == PROOF_DOMAIN,
                                    format!("X-Holger-Domain: {domain}"),
                                ));
                                let file = http(
                                    o.door,
                                    "GET",
                                    &format!("/production/{}", f.wheel),
                                    &[],
                                    b"",
                                );
                                let bytes_ok = file
                                    .as_ref()
                                    .map(|(c, _, b)| *c == 200 && sha256_hex(b) == f.wheel_sha256)
                                    .unwrap_or(false);
                                steps.push((
                                    "pip download".into(),
                                    bytes_ok,
                                    format!("/production/{} byte for byte", f.wheel),
                                ));
                                served_at = Some(t0.elapsed().as_secs());
                                let down =
                                    qmp(&sock, &serde_json::json!({"execute": "system_powerdown"}));
                                steps.push((
                                    format!("boot {boot}: power button"),
                                    down.is_ok(),
                                    format!("{down:?}"),
                                ));
                                stage = 3;
                            }
                        }
                        if stage == 2
                            && boot == 1
                            && plugged_at.is_some_and(|t| t.elapsed() > Duration::from_secs(120))
                            && !serial.contains("(HOLGERINBOX):")
                        {
                            steps.push(("hot-plug".into(), true, "the guest took no hot-plugged disk in 120 s: a clean stop, then boot 2 with the stick in".into()));
                            let down =
                                qmp(&sock, &serde_json::json!({"execute": "system_powerdown"}));
                            steps.push((
                                "boot 1: power button".into(),
                                down.is_ok(),
                                format!("{down:?}"),
                            ));
                            stage = 8;
                        }
                    }
                    9 => return true,
                    _ => {}
                }
                false
            },
        );
        let serial = if said.is_empty() {
            run.serial.clone()
        } else {
            said
        };
        let stopped = run.exited.is_some() && serial.contains("stopped cleanly");
        clean_stops += u32::from(stopped);
        steps.push((
            format!("boot {boot}: clean stop"),
            stopped,
            format!("exited {:?}", run.exited),
        ));
        serial_all.push_str(&format!("\n===== boot {boot} =====\n{serial}"));
        if stage != 8 {
            break;
        }
    }
    let serial = serial_all;
    let has = |needle: &str| serial.contains(needle);
    steps.push((
        "no pairing".into(),
        has("pid1: one-way, no pairing") && !has("pairing seed"),
        "the init said it is one-way and paired nothing".into(),
    ));
    steps.push((
        "stick into the inbox".into(),
        has("(HOLGERINBOX): 1 signed bundle(s) into the inbox"),
        "the box copied the stick's bundle into bundle.inbox".into(),
    ));
    steps.push((
        "clean stops".into(),
        clean_stops >= 1,
        format!("{clean_stops} clean stop(s)"),
    ));
    let pass = served_at.is_some() && steps.iter().all(|(_, ok, _)| *ok);
    for (name, ok, detail) in &steps {
        println!("  {} {name}  {detail}", if *ok { "PASS" } else { "FAIL" });
    }
    let receipt = serde_json::json!({
        "proof": "downstream-oneway-proof", "pass": pass, "medium": iso.display().to_string(),
        "fixture": f.dir.display().to_string(), "domain": PROOF_DOMAIN, "served_after_s": served_at,
        "steps": steps.iter().map(|(n, ok, d)| serde_json::json!({"step": n, "pass": ok, "detail": d})).collect::<Vec<_>>(),
        "serial_tail": korp_installer::isoproof::serve::tail(&serial, 60),
    });
    std::fs::write(
        o.work.join("proof.json"),
        serde_json::to_vec_pretty(&receipt)?,
    )?;
    if !pass {
        println!(
            "  serial tail:\n{}",
            korp_installer::isoproof::serve::tail(&serial, 60)
        );
        bail!(
            "[downstream-oneway-proof] RED: see {}",
            o.work.join("proof.json").display()
        );
    }
    println!("PROOF-OK {}", o.work.join("proof.json").display());
    Ok(())
}

fn now_ms() -> u128 {
    std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map(|d| d.as_millis())
        .unwrap_or(0)
}

fn sha256_hex(b: &[u8]) -> String {
    use sha2::Digest as _;
    sha2::Sha256::digest(b)
        .iter()
        .map(|x| format!("{x:02x}"))
        .collect()
}

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

    fn s(v: &str) -> Vec<String> {
        v.split_whitespace().map(str::to_string).collect()
    }

    const KEY: &str = "ab12ab12ab12ab12ab12ab12ab12ab12ab12ab12ab12ab12ab12ab12ab12ab12";

    #[test]
    fn downstream_media_takes_its_words_and_refuses_strays() {
        let o = parse_media(&s(&format!("--name lab-1 --upstream test3.holger.rs --upstream-key {KEY} --via front --mirror https://10.0.2.2:40443 --ca /c.pem --tls-name repo.test3.holger.rs --bearer-file /b --out /o/lab-1.iso"))).unwrap();
        assert_eq!(
            (
                o.name.as_str(),
                o.via.as_str(),
                o.tls_name.as_deref(),
                o.out.as_path()
            ),
            (
                "lab-1",
                "front",
                Some("repo.test3.holger.rs"),
                Path::new("/o/lab-1.iso")
            )
        );
        // The certificate's name and the route's `:authority` apart (lane OX-WGDS3).
        let apart = parse_media(&s(&format!("--name lab-1 --upstream test3.holger.rs --upstream-key {KEY} --via front --mirror https://10.0.2.2:40443 --ca /c.pem --tls-name test3.holger.rs --authority repo.test3.holger.rs --bearer-file /b --out /o/lab-1.iso"))).unwrap();
        assert_eq!(
            declaration(&apart, "gate-bearer-of-sufficient-length-0123456789")
                .unwrap()
                .via,
            Via::Front {
                server_name: Some("test3.holger.rs".into()),
                authority: Some("repo.test3.holger.rs".into())
            }
        );
        assert!(parse_media(&s(&format!("--name lab-2 --upstream u --upstream-key {KEY} --via wireguard --mirror http://10.46.0.1:50070 --authority x --bearer-file /b --out /o/i"))).is_err());
        let d = declaration(&o, "gate-bearer-of-sufficient-length-0123456789\n").unwrap();
        assert_eq!(
            d.via,
            Via::Front {
                server_name: Some("repo.test3.holger.rs".into()),
                authority: None
            }
        );
        let w = parse_media(&s(&format!("--name lab-2 --upstream up --upstream-key {KEY} --via wireguard --mirror http://10.46.0.1:50070 --bearer-file /b --out /o/lab-2.iso"))).unwrap();
        assert_eq!(
            declaration(&w, &"x".repeat(40)).unwrap().via,
            Via::Wireguard
        );
        for bad in [
            format!("--name lab-1 --upstream up --upstream-key {KEY} --via front --mirror https://x --bearer-file /b --out /o"),
            format!("--name lab-2 --upstream up --upstream-key {KEY} --via wireguard --mirror http://x --ca /c --bearer-file /b --out /o"),
            format!("--name lab-2 --upstream up --upstream-key {KEY} --via tailscale --mirror http://x --bearer-file /b --out /o"),
            format!("--name lab-2 --upstream up --upstream-key {KEY} --via wireguard --mirror http://x --out /o"),
            "--wat".to_string(),
        ] {
            assert!(parse_media(&s(&bad)).is_err(), "accepted: {bad}");
        }
    }

    #[test]
    fn downstream_vm_takes_its_words_and_lays_one_unrestricted_nic() {
        let o = parse_vm(&s("--iso /m.iso --work /w --door 40811 --mem-mb 768")).unwrap();
        assert_eq!((o.door, o.mem_mb, o.install_min), (40811, 768, 20));
        assert!(parse_vm(&s("--iso /m.iso --work /w")).is_err(), "no door");
        assert!(parse_vm(&s("--iso /m.iso --work /w --door 0")).is_err());
        assert!(parse_vm(&s("--iso /m.iso --door 1 --wat")).is_err());
        let p = proof(&o, PathBuf::from("/q"));
        assert_eq!(p.nics.len(), 1, "ONE NIC");
        assert_eq!(
            (p.nics[0].net.as_deref(), &p.nics[0].hostfwds[..]),
            (None, &[(40811, HTTP_DOOR)][..]),
            "user-mode, unrestricted, the door forwarded"
        );
        assert!(
            p.disks[0].bytes > p.disks[1].bytes,
            "the boot disk is the larger"
        );
    }

    #[test]
    fn the_one_way_proof_takes_its_words_and_stages_every_trust_file_by_its_declared_param() {
        let o = parse_proof(&s(
            "--fixture /f --work /w --door 40900 --net eth0:10.0.2.15/24:10.0.2.2",
        ))
        .unwrap();
        assert_eq!(
            (o.door, o.net.as_deref(), o.mem_mb),
            (40900, Some("eth0:10.0.2.15/24:10.0.2.2"), 1024)
        );
        assert!(
            parse_proof(&s("--fixture /f --work /w")).is_err(),
            "no door"
        );
        assert!(parse_proof(&s("--fixture /f --work /w --door 1 --wat")).is_err());
        let got = oneway_forge_params(Path::new("/s"), true);
        let set = holger_recipe::declared();
        let m = set
            .installers
            .iter()
            .find(|i| i.name == ONEWAY_VARIANT)
            .expect("the one-way medium");
        for g in &got {
            let name = g.split('=').next().unwrap();
            assert!(
                m.params.iter().any(|p| p.name == name),
                "{ONEWAY_VARIANT} declares `{name}`"
            );
        }
        for need in [
            "holger_ron",
            "trust_policy",
            "upstream_root",
            "upstream_signer",
        ] {
            assert!(
                got.iter().any(|g| g.starts_with(&format!("{need}="))),
                "{need}: {got:?}"
            );
        }
        assert!(!oneway_forge_params(Path::new("/s"), false)
            .iter()
            .any(|g| g.starts_with("holger_net=")));
    }

    #[test]
    fn the_staged_files_ride_the_medium_by_its_declared_params() {
        let got = forge_params(
            Path::new("/s/holger.ron"),
            Some(Path::new("/s/downstream-ca.pem")),
            Path::new("/s/korp.pem"),
        );
        assert_eq!(
            got,
            [
                "holger_ron=@/s/holger.ron",
                "plugin_trust_root=@/s/korp.pem",
                "downstream_ca=@/s/downstream-ca.pem"
            ]
        );
        let set = holger_recipe::declared();
        let m = set
            .installers
            .iter()
            .find(|i| i.name == VARIANT)
            .expect("the medium");
        for g in &got {
            let name = g.split('=').next().unwrap();
            assert!(
                m.params.iter().any(|p| p.name == name),
                "{VARIANT} declares `{name}`"
            );
        }
    }
}