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

nordisk/holger

xtask/src/installtest.rs

//! **`cargo xtask installtest`** — Holger installed in KVM from its own DEV medium, booted from
//! the disk it laid, and proven to SERVE by a push and a pull of one artifact for each of cargo,
//! pip and npm (A9 gap 2, lane B2, 2026-10-21).
//!
//! ```text
//! cargo xtask installtest [--iso <medium>] [--work <dir>] [--install-min N] [--serve-min N]
//!                         [--keep-disks] [--plan]
//! ```
//!
//! What it does, in order, and every step that misses makes the verb RED (exit 1):
//!
//! 1. **the DEV medium** — the main's medium (`holger-installer-metered`), composed through the
//!    Build Thing as `estate-media` composes it (`--opt dev-narrate=on`, `--build kernel`, the korp
//!    root, `live_app=on`, and an intel trust policy in the `intel_trust_policy` slot as an airgap
//!    estate's operator stages one: here the policy of signers minted for the run), with two
//!    differences. It carries NO pairing seed marker: no metadata
//!    service and no installer exist here, and a box whose marker finds no metadata refuses to
//!    start by name (`REFUSED [pairing-seed-metadata]`, measured on the R10 medium 2026-10-21) —
//!    the paired path is the ONE box gate's (`gate holger`). And its `holger_ron` is [`declaration`] — the estate's own declaration (`estate_ron`, the sold, online
//!    tier, estate `installtest.invalid`) with the console's bearer and two approvers written in,
//!    because no installer feeds this box. The composed medium is moved out of `.iso-out` into
//!    the run's directory, so no test medium is ever uploaded by `estate-media --composed`.
//!    `--iso <medium>` installs a medium composed elsewhere instead (its record must name this
//!    checkout's HEAD for a green receipt);
//! 2. **the install** — SeaBIOS (UpCloud's firmware), the medium as an IDE cdrom, a 30 GiB boot
//!    disk and a 20 GiB data disk, `-no-reboot`. The leg ends when the GUEST ends it (the
//!    installer's own reboot), never at `INSTALL-OK`: the data set is laid after that marker;
//! 3. **the boot of the laid disks** — no medium, two NICs (UpCloud's public and utility; the
//!    medium names `eth1`), the HTTP door (8443) forwarded on the first;
//! 4. **serve** — the leg waits for the door to answer at all, then:
//! 5. **push and pull**, each compared byte for byte by sha256:
//!    * **cargo** — `cargo package` of a one-file crate, the publish call `cargo publish` makes
//!      (`PUT /crates/api/v1/crates/new`) to the estate's cargo door, walked through the intake
//!      chain to AVAILABLE (the coordinate `crates/<name>/<name>-<version>.crate`, lane
//!      HOL-CARGO-PUBLISH-DOOR), then the sparse index line (its `cksum`) and the download;
//!    * **pip** — a wheel uploaded the way twine uploads it (`POST /incoming/legacy/`), walked
//!      through the estate's intake chain to AVAILABLE (`POST /-/intake/advance`: the console
//!      imports and seals, two approvers approve), then the PEP 503 page
//!      `/production/simple/<project>/` and the file it links;
//!    * **npm** — the document `npm publish` sends (`PUT /npm/<package>`) to the estate's npm
//!      door, walked through the intake chain to AVAILABLE as the wheel is (the coordinate is
//!      the tarball's registry path `<package>/-/<file>.tgz`), then the packument and the
//!      tarball it names.
//!
//!    One red fault runs first: a pull of a crate nobody pushed must NOT answer 200, so the pull
//!    check is proven able to say no (an empty registry is never a pass).
//! 6. **the scan sandbox** (lane HOLGER-MEDIA): the pip walk must have passed through SCANNED (the
//!    wheel's INGEST walk and its SCANNED scanners ran in the sandbox the medium carried: crun over
//!    the laid worker rootfs, `rules: (sandbox: required)`), and `GET /-/sandbox/probe` (the
//!    worker's `probe` mode through that same sandbox) must answer what `holger-sandbox`'s live test
//!    asserts: no network, no name resolves, a read-only root, the scratch cap, uid 65532, no
//!    capability, no new privileges, a seccomp filter, and a run count above zero.
//!
//! The receipt `.installtest-receipts/holger-installtest-<holger12>.json` names the commits
//! (holger, tunnr, edda, Skidbladnir, dwarves), the medium's sha256, every leg and every probe.
//! It is GREEN only when every leg passed and the medium is this checkout's. The ONE box kit's
//! `cargo xtask gate holger` (private-monetize-ops) reads it: no green gate without a serving main.
//!
//! Gunnar's `installtest` is the pattern (install in KVM, boot the laid disk, push and clone with
//! the product's own protocol, refuse an empty graph); the code is Holger's own. The KVM half is
//! the shared `korp_installer::isoproof::serve` (tunnr).
//!
//! Runs on the machine with `/dev/kvm` and `qemu-system-x86_64`, at nice 19, one at a time
//! (`~/locks/holger-installtest.lock`).

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

use anyhow::{bail, Context, Result};
use korp_installer::isoproof::serve::{Disk, InstallThenServe, Leg, MediumAttach, Probe};
use korp_installer::isoproof::SlirpNic;
use korp_installer::kvmproof::{self, Machine};
use nornir_build_thing::variant::ArtifactKind;
use sha2::Digest as _;

use crate::{estate_media, forge_door};

/// The medium under test: the main's (and the twin's) DEV medium.
pub const VARIANT: &str = holger_recipe::packaging::METERED_INSTALLER;
/// The estate name the test declaration carries (never a real estate).
pub const ESTATE: &str = "installtest.invalid";
/// The box's HTTP door (`estate_ron::EstateRon::on_a_box`'s `http_bind`).
pub const HTTP_DOOR: u16 = 8443;
/// The receipts' directory in the holger checkout.
pub const RECEIPTS: &str = ".installtest-receipts";
/// The repositories a receipt names (siblings of the holger checkout).
pub const NAMED_BY: [&str; 5] = ["holger", "tunnr", "edda", "Skidbladnir", "dwarves"];
/// The two approvers written into the test declaration.
pub const APPROVERS: [&str; 2] = ["installtest-approver-a", "installtest-approver-b"];
/// The probe artifacts.
pub const CRATE: &str = "holger-installtest-probe";
pub const CRATE_VERSION: &str = "0.1.0";
pub const PIP_PROJECT: &str = "holger-installtest-probe";
pub const PIP_VERSION: &str = "0.1.0";
pub const NPM_PACKAGE: &str = "holger-installtest-probe";
pub const NPM_VERSION: &str = "0.1.0";
/// The disks of the machine (R10's main shape).
pub const BOOT_DISK_GIB: u64 = 30;
pub const DATA_DISK_GIB: u64 = 20;
/// The trust policy's path on the box: the shipped declaration's `intel.trust_policy`, which
/// the medium's `intel_trust_policy` slot fills on the release (lane P6). The tests' own.
#[cfg(test)]
pub fn intel_trust_on_box() -> String {
    format!(
        "{}/{}",
        holger_recipe::estate_ron::RELEASE_ROOT,
        holger_recipe::estate_ron::INTEL_TRUST_STAGED
    )
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Opts {
    pub iso: Option<PathBuf>,
    pub work: Option<PathBuf>,
    pub install_min: u64,
    pub serve_min: u64,
    pub keep_disks: bool,
    pub plan: bool,
}

/// Pure: the verb's words; a stray word is refused, never dropped.
pub fn parse(args: &[String]) -> Result<Opts> {
    let mut o = Opts {
        iso: None,
        work: None,
        install_min: 20,
        serve_min: 20,
        keep_disks: false,
        plan: false,
    };
    let mut it = args.iter();
    while let Some(a) = it.next() {
        let mut val = |what: &str| {
            it.next()
                .cloned()
                .with_context(|| format!("installtest: {a} takes {what}"))
        };
        match a.as_str() {
            "--iso" => o.iso = Some(PathBuf::from(val("a medium")?)),
            "--work" => o.work = Some(PathBuf::from(val("a directory")?)),
            "--install-min" => o.install_min = val("minutes")?.parse().context("--install-min is minutes")?,
            "--serve-min" => o.serve_min = val("minutes")?.parse().context("--serve-min is minutes")?,
            "--keep-disks" => o.keep_disks = true,
            "--plan" => o.plan = true,
            other => bail!("installtest: `{other}`: cargo xtask installtest [--iso <medium>] [--work <dir>] [--install-min N] [--serve-min N] [--keep-disks] [--plan]"),
        }
    }
    Ok(o)
}

// ── the declaration (pure) ──────────────────────────────────────────────────

/// The principals' bearers of one run (never printed, never in the receipt).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Bearers {
    pub console: String,
    pub approvers: [String; 2],
}

impl Bearers {
    /// Fresh random bearers.
    pub fn mint() -> Result<Self> {
        let mut f = std::fs::File::open("/dev/urandom").context("/dev/urandom")?;
        let mut one = || -> Result<String> {
            let mut b = [0u8; 24];
            std::io::Read::read_exact(&mut f, &mut b)?;
            Ok(format!("installtest-{}", hex(&b)))
        };
        Ok(Self {
            console: one()?,
            approvers: [one()?, one()?],
        })
    }
}

fn hex(b: &[u8]) -> String {
    b.iter().map(|x| format!("{x:02x}")).collect()
}

/// Replace `from` by `to` in `s`, exactly once, or refuse by name: the estate declaration
/// changed under the test and the test must be taught the new shape, never guess.
fn once(s: &str, from: &str, to: &str) -> Result<String> {
    match s.matches(from).count() {
        1 => Ok(s.replacen(from, to, 1)),
        n => bail!("[installtest-declaration-drift] the estate declaration carries `{from}` {n} times, the test expects it once: teach installtest::declaration the new shape"),
    }
}

/// Pure: **the test medium's declaration** — the estate's own (`estate_ron`, the sold main's
/// online form, for
/// [`ESTATE`]) with what the installer would feed written in: the console's bearer (the shop
/// derives from the front's console key), and two approvers (people an admin would invite). Nothing
/// else differs from what a sold main boots.
pub fn declaration(b: &Bearers) -> Result<String> {
    use holger_recipe::estate_ron as er;
    // The airgap tier: no egress. Its scan blocks every artifact until an advisory database
    // is installed (`no advisory database is installed`, measured on B1's run of e42850ea,
    // 2026-10-21), and an airgap box takes one only as a signed bundle verified against
    // `intel.trust_policy`. The shipped declaration names it on the release, and the test fills
    // that slot as an airgap estate's operator does: its medium is composed with the policy of
    // signers minted for the run (`intel_trust_policy`, [`compose`]), and it imports a bundle
    // signed by them ([`IntelFeed`]). The intel line is the estate's own, and the scan is
    // untouched. (The online form was tried on 2026-10-21: the box's first `intel.refresh` of
    // the public feeds had not landed after 40 minutes.)
    let base = er::render(&er::EstateRon::on_a_box(Some(ESTATE)).airgapped());
    let file = format!(
        "StaticToken(subject: \"{}\", token_file: Some(\"{}/{}\"))",
        er::CONSOLE_PRINCIPAL,
        er::BOX_APPLIANCE_ROOT,
        er::CONSOLE_TOKEN_FILE
    );
    let mut methods = vec![format!(
        "StaticToken(subject: \"{}\", token: Some(\"{}\"))",
        er::CONSOLE_PRINCIPAL,
        b.console
    )];
    methods.extend(
        APPROVERS
            .iter()
            .zip(&b.approvers)
            .map(|(who, t)| format!("StaticToken(subject: \"{who}\", token: Some(\"{t}\"))")),
    );
    let s = once(
        &base,
        &format!("methods: [{file}]"),
        &format!("methods: [{}]", methods.join(", ")),
    )?;
    let roles = APPROVERS
        .iter()
        .map(|a| format!(", \"{a}\": Reader"))
        .collect::<String>();
    let s = once(
        &s,
        &format!("roles: {{\"{}\": Admin}}", er::CONSOLE_PRINCIPAL),
        &format!("roles: {{\"{}\": Admin{roles}}}", er::CONSOLE_PRINCIPAL),
    )?;
    let console_duties = format!(
        "\"{}\": [{}]",
        er::CONSOLE_PRINCIPAL,
        er::CONSOLE_DUTIES.join(", ")
    );
    let approver_duties = APPROVERS
        .iter()
        .map(|a| format!(", \"{a}\": [Developer, SecurityApprover]"))
        .collect::<String>();
    once(
        &s,
        &format!("duties: {{{console_duties}}}"),
        &format!("duties: {{{console_duties}{approver_duties}}}"),
    )
}

// ── the artifacts (pure, but cargo's package) ───────────────────────────────

/// The publish body `cargo publish` sends: `u32le json | json | u32le crate | crate`.
pub fn cargo_publish_body(name: &str, vers: &str, crate_bytes: &[u8]) -> Vec<u8> {
    let meta = serde_json::json!({
        "name": name, "vers": vers, "deps": [], "features": {}, "authors": ["holger installtest"],
        "description": "Holger's install test probe", "license": "AGPL-3.0-only", "readme": null,
        "keywords": [], "categories": [], "links": null,
    })
    .to_string();
    let mut body = Vec::new();
    body.extend_from_slice(&(meta.len() as u32).to_le_bytes());
    body.extend_from_slice(meta.as_bytes());
    body.extend_from_slice(&(crate_bytes.len() as u32).to_le_bytes());
    body.extend_from_slice(crate_bytes);
    body
}

/// The sparse index path of a crate name (cargo's layout).
pub fn sparse_path(name: &str) -> String {
    let n = name.to_ascii_lowercase();
    match n.len() {
        1 => format!("1/{n}"),
        2 => format!("2/{n}"),
        3 => format!("3/{}/{n}", &n[..1]),
        _ => format!("{}/{}/{n}", &n[..2], &n[2..4]),
    }
}

/// Pure: the `cksum` of version `vers` in a sparse index file.
pub fn index_cksum(index: &str, vers: &str) -> Option<String> {
    index
        .lines()
        .filter_map(|l| serde_json::from_str::<serde_json::Value>(l).ok())
        .find(|v| v["vers"] == vers)
        .and_then(|v| v["cksum"].as_str().map(str::to_string))
}

/// Package a one-file crate with stock `cargo package` and return the `.crate` bytes.
pub fn package_crate(dir: &Path) -> Result<Vec<u8>> {
    let src = dir.join("crate");
    std::fs::create_dir_all(src.join("src"))?;
    std::fs::write(
        src.join("Cargo.toml"),
        format!("[package]\nname = \"{CRATE}\"\nversion = \"{CRATE_VERSION}\"\nedition = \"2021\"\nlicense = \"AGPL-3.0-only\"\ndescription = \"Holger's install test probe\"\n\n[workspace]\n"),
    )?;
    std::fs::write(src.join("src/lib.rs"), "//! Holger's install test probe.\npub fn probe() -> &'static str { \"holger-installtest\" }\n")?;
    let cargo = std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into());
    let target = dir.join("crate-target");
    let st = Command::new(&cargo)
        .args(["package", "--no-verify", "--allow-dirty", "--offline"])
        .env("CARGO_TARGET_DIR", &target)
        .current_dir(&src)
        .status()
        .context("spawn cargo package")?;
    if !st.success() {
        bail!("[installtest-cargo-package] `cargo package` exited {st}");
    }
    let file = target
        .join("package")
        .join(format!("{CRATE}-{CRATE_VERSION}.crate"));
    std::fs::read(&file).with_context(|| format!("[installtest-cargo-package] {}", file.display()))
}

fn crc32(data: &[u8]) -> u32 {
    let mut crc = 0xFFFF_FFFFu32;
    for b in data {
        crc ^= *b as u32;
        for _ in 0..8 {
            crc = if crc & 1 != 0 {
                (crc >> 1) ^ 0xEDB8_8320
            } else {
                crc >> 1
            };
        }
    }
    !crc
}

fn b64(data: &[u8], url: bool) -> String {
    let abc: &[u8; 64] = if url {
        b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
    } else {
        b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
    };
    let mut out = String::new();
    for c in data.chunks(3) {
        let n = (c[0] as u32) << 16
            | (*c.get(1).unwrap_or(&0) as u32) << 8
            | *c.get(2).unwrap_or(&0) as u32;
        for i in 0..4 {
            if i <= c.len() {
                out.push(abc[(n >> (18 - 6 * i) & 63) as usize] as char);
            } else if !url {
                out.push('=');
            }
        }
    }
    out
}

/// A stored (uncompressed) zip of `members`.
pub fn zip_stored(members: &[(String, Vec<u8>)]) -> Vec<u8> {
    let (mut out, mut central) = (Vec::new(), Vec::new());
    for (name, body) in members {
        let crc = crc32(body);
        let offset = out.len() as u32;
        out.extend_from_slice(&0x0403_4b50u32.to_le_bytes());
        out.extend_from_slice(&[20, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
        out.extend_from_slice(&crc.to_le_bytes());
        out.extend_from_slice(&(body.len() as u32).to_le_bytes());
        out.extend_from_slice(&(body.len() as u32).to_le_bytes());
        out.extend_from_slice(&(name.len() as u16).to_le_bytes());
        out.extend_from_slice(&0u16.to_le_bytes());
        out.extend_from_slice(name.as_bytes());
        out.extend_from_slice(body);
        central.extend_from_slice(&0x0201_4b50u32.to_le_bytes());
        central.extend_from_slice(&[20, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
        central.extend_from_slice(&crc.to_le_bytes());
        central.extend_from_slice(&(body.len() as u32).to_le_bytes());
        central.extend_from_slice(&(body.len() as u32).to_le_bytes());
        central.extend_from_slice(&(name.len() as u16).to_le_bytes());
        central.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
        central.extend_from_slice(&offset.to_le_bytes());
        central.extend_from_slice(name.as_bytes());
    }
    let (cd_at, cd_len, n) = (out.len() as u32, central.len() as u32, members.len() as u16);
    out.extend_from_slice(&central);
    out.extend_from_slice(&0x0605_4b50u32.to_le_bytes());
    out.extend_from_slice(&[0, 0, 0, 0]);
    out.extend_from_slice(&n.to_le_bytes());
    out.extend_from_slice(&n.to_le_bytes());
    out.extend_from_slice(&cd_len.to_le_bytes());
    out.extend_from_slice(&cd_at.to_le_bytes());
    out.extend_from_slice(&0u16.to_le_bytes());
    out
}

/// The wheel file name of the pip probe.
pub fn wheel_name() -> String {
    format!(
        "{}-{PIP_VERSION}-py3-none-any.whl",
        PIP_PROJECT.replace('-', "_")
    )
}

/// Pure: a valid pure-Python wheel (METADATA, WHEEL, RECORD).
pub fn wheel_bytes() -> Vec<u8> {
    let pkg = PIP_PROJECT.replace('-', "_");
    let info = format!("{pkg}-{PIP_VERSION}.dist-info");
    let mut m: Vec<(String, Vec<u8>)> = vec![
        (format!("{pkg}/__init__.py"), format!("__version__ = \"{PIP_VERSION}\"\n").into_bytes()),
        (format!("{info}/METADATA"), format!("Metadata-Version: 2.1\nName: {PIP_PROJECT}\nVersion: {PIP_VERSION}\nLicense-Expression: MIT\n").into_bytes()),
        (format!("{info}/WHEEL"), b"Wheel-Version: 1.0\nGenerator: holger-installtest\nRoot-Is-Purelib: true\nTag: py3-none-any\n".to_vec()),
    ];
    let mut record = String::new();
    for (name, body) in &m {
        record.push_str(&format!(
            "{name},sha256={},{}\n",
            b64(&sha2::Sha256::digest(body), true),
            body.len()
        ));
    }
    record.push_str(&format!("{info}/RECORD,,\n"));
    m.push((format!("{info}/RECORD"), record.into_bytes()));
    zip_stored(&m)
}

/// Pure: the multipart body twine sends to `/legacy/`.
pub fn twine_form(file: &str, content: &[u8]) -> (String, Vec<u8>) {
    let boundary = "----holger-installtest-twine";
    let mut body = Vec::new();
    let mut field = |name: &str, value: &str| {
        body.extend_from_slice(format!("--{boundary}\r\nContent-Disposition: form-data; name=\"{name}\"\r\n\r\n{value}\r\n").as_bytes())
    };
    for (k, v) in [
        (":action", "file_upload"),
        ("protocol_version", "1"),
        ("metadata_version", "2.1"),
        ("name", PIP_PROJECT),
        ("version", PIP_VERSION),
        ("filetype", "bdist_wheel"),
        ("pyversion", "py3"),
    ] {
        field(k, v);
    }
    field("sha256_digest", &sha256_hex(content));
    body.extend_from_slice(format!("--{boundary}\r\nContent-Disposition: form-data; name=\"content\"; filename=\"{file}\"\r\nContent-Type: application/octet-stream\r\n\r\n").as_bytes());
    body.extend_from_slice(content);
    body.extend_from_slice(format!("\r\n--{boundary}--\r\n").as_bytes());
    (format!("multipart/form-data; boundary={boundary}"), body)
}

/// Pure: an npm package tarball (`package/package.json`, `package/index.js`), gzip-stored.
pub fn npm_tarball() -> Vec<u8> {
    fn header(name: &str, size: usize) -> [u8; 512] {
        let mut h = [0u8; 512];
        h[..name.len()].copy_from_slice(name.as_bytes());
        h[100..107].copy_from_slice(b"0000644");
        h[108..115].copy_from_slice(b"0000000");
        h[116..123].copy_from_slice(b"0000000");
        h[124..135].copy_from_slice(format!("{size:011o}").as_bytes());
        h[136..147].copy_from_slice(b"00000000000");
        h[156] = b'0';
        h[257..263].copy_from_slice(b"ustar\0");
        h[263..265].copy_from_slice(b"00");
        h[148..156].copy_from_slice(b"        ");
        let sum: u32 = h.iter().map(|b| *b as u32).sum();
        h[148..155].copy_from_slice(format!("{sum:06o}\0").as_bytes());
        h
    }
    let files = [
        ("package/package.json", format!("{{\"name\":\"{NPM_PACKAGE}\",\"version\":\"{NPM_VERSION}\",\"main\":\"index.js\",\"license\":\"MIT\"}}\n").into_bytes()),
        ("package/index.js", b"module.exports = 'holger-installtest';\n".to_vec()),
    ];
    let mut tar = Vec::new();
    for (name, body) in &files {
        tar.extend_from_slice(&header(name, body.len()));
        tar.extend_from_slice(body);
        tar.resize(tar.len().div_ceil(512) * 512, 0);
    }
    tar.resize(tar.len() + 1024, 0);
    // gzip with stored deflate blocks (no compressor needed, any gunzip reads it).
    let mut gz = vec![0x1f, 0x8b, 8, 0, 0, 0, 0, 0, 0, 255];
    for (i, c) in tar.chunks(65535).enumerate() {
        let last = (i + 1) * 65535 >= tar.len();
        gz.push(u8::from(last));
        gz.extend_from_slice(&(c.len() as u16).to_le_bytes());
        gz.extend_from_slice(&(!(c.len() as u16)).to_le_bytes());
        gz.extend_from_slice(c);
    }
    gz.extend_from_slice(&crc32(&tar).to_le_bytes());
    gz.extend_from_slice(&(tar.len() as u32).to_le_bytes());
    gz
}

/// Pure: the document `npm publish` PUTs.
pub fn npm_publish_doc(tgz: &[u8]) -> String {
    let file = format!("{NPM_PACKAGE}-{NPM_VERSION}.tgz");
    let sha1 = sha1_hex(tgz);
    let integrity = format!("sha512-{}", b64(&sha2::Sha512::digest(tgz), false));
    serde_json::json!({
        "_id": NPM_PACKAGE, "name": NPM_PACKAGE, "dist-tags": {"latest": NPM_VERSION},
        "versions": {NPM_VERSION: {"name": NPM_PACKAGE, "version": NPM_VERSION, "main": "index.js", "license": "MIT",
            "dist": {"shasum": sha1, "integrity": integrity, "tarball": format!("/npm/{NPM_PACKAGE}/-/{file}")}}},
        "_attachments": {file: {"content_type": "application/octet-stream", "data": b64(tgz, false), "length": tgz.len()}},
    })
    .to_string()
}

/// SHA-1 (npm's `shasum`), small and self-contained.
pub fn sha1_hex(data: &[u8]) -> String {
    let mut h: [u32; 5] = [
        0x6745_2301,
        0xEFCD_AB89,
        0x98BA_DCFE,
        0x1032_5476,
        0xC3D2_E1F0,
    ];
    let mut m = data.to_vec();
    let bits = (data.len() as u64) * 8;
    m.push(0x80);
    while m.len() % 64 != 56 {
        m.push(0);
    }
    m.extend_from_slice(&bits.to_be_bytes());
    for block in m.chunks(64) {
        let mut w = [0u32; 80];
        for i in 0..16 {
            w[i] = u32::from_be_bytes([
                block[4 * i],
                block[4 * i + 1],
                block[4 * i + 2],
                block[4 * i + 3],
            ]);
        }
        for i in 16..80 {
            w[i] = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]).rotate_left(1);
        }
        let [mut a, mut b, mut c, mut d, mut e] = h;
        for (i, wi) in w.iter().enumerate() {
            let (f, k) = match i {
                0..=19 => ((b & c) | (!b & d), 0x5A82_7999),
                20..=39 => (b ^ c ^ d, 0x6ED9_EBA1),
                40..=59 => ((b & c) | (b & d) | (c & d), 0x8F1B_BCDC),
                _ => (b ^ c ^ d, 0xCA62_C1D6),
            };
            let t = a
                .rotate_left(5)
                .wrapping_add(f)
                .wrapping_add(e)
                .wrapping_add(k)
                .wrapping_add(*wi);
            e = d;
            d = c;
            c = b.rotate_left(30);
            b = a;
            a = t;
        }
        for (x, y) in h.iter_mut().zip([a, b, c, d, e]) {
            *x = x.wrapping_add(y);
        }
    }
    h.iter().map(|x| format!("{x:08x}")).collect()
}

pub fn sha256_hex(b: &[u8]) -> String {
    hex(&sha2::Sha256::digest(b))
}

// ── the probe (HTTP against the forwarded door) ─────────────────────────────

/// One HTTP answer: status and body. `Err` is a transport failure (nothing answered).
type Answer = std::result::Result<(u16, Vec<u8>), String>;

struct Door {
    base: String,
    agent: ureq::Agent,
}

impl Door {
    fn new(port: u16) -> Self {
        let agent: ureq::Agent = ureq::Agent::config_builder()
            .http_status_as_error(false)
            .timeout_global(Some(Duration::from_secs(60)))
            .build()
            .into();
        Self {
            base: format!("http://127.0.0.1:{port}"),
            agent,
        }
    }

    fn call(
        &self,
        method: &str,
        path: &str,
        bearer: Option<&str>,
        ctype: Option<&str>,
        body: Option<&[u8]>,
    ) -> Answer {
        let url = format!("{}{path}", self.base);
        let read =
            |r: std::result::Result<ureq::http::Response<ureq::Body>, ureq::Error>| -> Answer {
                let mut r = r.map_err(|e| e.to_string())?;
                let st = r.status().as_u16();
                let b = r
                    .body_mut()
                    .with_config()
                    .limit(64 << 20)
                    .read_to_vec()
                    .map_err(|e| e.to_string())?;
                Ok((st, b))
            };
        match method {
            "GET" => {
                let mut q = self.agent.get(&url);
                if let Some(t) = bearer {
                    q = q.header("Authorization", format!("Bearer {t}"));
                }
                read(q.call())
            }
            _ => {
                let mut q = if method == "PUT" {
                    self.agent.put(&url)
                } else {
                    self.agent.post(&url)
                };
                if let Some(t) = bearer {
                    q = q.header("Authorization", format!("Bearer {t}"));
                }
                if let Some(c) = ctype {
                    q = q.header("Content-Type", c);
                }
                read(q.send(body.unwrap_or_default()))
            }
        }
    }
}

fn said(a: &Answer) -> String {
    match a {
        Ok((st, b)) => format!(
            "http {st}: {}",
            String::from_utf8_lossy(&b[..b.len().min(200)]).trim()
        ),
        Err(e) => format!("no answer: {e}"),
    }
}

/// What the probe carries in: the artifacts and the bearers.
pub struct Artifacts {
    pub crate_bytes: Vec<u8>,
    pub wheel: Vec<u8>,
    pub tgz: Vec<u8>,
}

/// One probe's result.
#[derive(Debug, Clone)]
pub struct ProbeRow {
    pub name: String,
    pub pass: bool,
    pub detail: String,
}

fn row(name: &str, r: std::result::Result<String, String>) -> ProbeRow {
    match r {
        Ok(d) => ProbeRow {
            name: name.into(),
            pass: true,
            detail: d,
        },
        Err(e) => ProbeRow {
            name: name.into(),
            pass: false,
            detail: e,
        },
    }
}

/// **The estate board on the laid box** (lane E3; A18 §4 #8): `GET /-/estate.json` is admin-read
/// (anonymous 401, an approver — a Reader with duties — 403), and the console's bearer is handed
/// the board's rows (data; holger-ui draws them, H2 #1): this box first, as the main, up, and the
/// consumers counted (the metered row carries `admission`, so the book is kept). The install
/// test is ONE main: the twin and a downstream on the board are proven in `server/lib/tests`
/// (the twin follow, the A13 board test).
fn board_probe(d: &Door, b: &Bearers) -> std::result::Result<String, String> {
    let anon = d.call("GET", BOARD_PATH, None, None, None);
    if !matches!(anon, Ok((401, _))) {
        return Err(format!(
            "[installtest-board-anonymous] anonymous GET {BOARD_PATH} must be 401, got {}",
            said(&anon)
        ));
    }
    let approver = d.call("GET", BOARD_PATH, Some(&b.approvers[0]), None, None);
    if !matches!(approver, Ok((403, _))) {
        return Err(format!(
            "[installtest-board-approver] an approver (Reader) on {BOARD_PATH} must be 403, got {}",
            said(&approver)
        ));
    }
    let (st, body) = d.call("GET", BOARD_PATH, Some(&b.console), None, None)?;
    if st != 200 {
        return Err(format!(
            "[installtest-board-console] the console's GET {BOARD_PATH}: http {st}: {}",
            String::from_utf8_lossy(&body[..body.len().min(200)])
        ));
    }
    let text = String::from_utf8_lossy(&body);
    let v: serde_json::Value = serde_json::from_slice(&body).map_err(|e| {
        format!(
            "[installtest-board-data] the console's board is not JSON ({e}): {}",
            first_chars(&text, 200)
        )
    })?;
    let me = &v["boxes"][0];
    if me["this_box"].as_bool() != Some(true) {
        return Err(format!(
            "[installtest-board-data] the board's first row is not this box: {}",
            first_chars(&text, 300)
        ));
    }
    if me["state"].as_str() != Some("up") {
        return Err(format!(
            "[installtest-board-state] this box is not up: {}",
            first_chars(&me.to_string(), 300)
        ));
    }
    if v["consumers"]["counted"].as_bool() != Some(true) {
        return Err(format!("[installtest-board-consumers] the metered main keeps the admission book, and its board does not count consumers: {}", v["consumers"]));
    }
    let summary = v["summary"].as_str().unwrap_or_default();
    let consumers = summary
        .find("consumers: ")
        .map(|at| &summary[at..])
        .unwrap_or(summary);
    Ok(format!(
        "401 anonymous, 403 approver, 200 console: {} bytes of JSON, this box up, {}",
        body.len(),
        consumers.trim()
    ))
}

/// The estate board's HTTP path (server-lib `exposed::http::ESTATE_JSON_PATH`).
const BOARD_PATH: &str = "/-/estate.json";

/// The first `n` characters of `s`, on a character boundary (the board's words carry `·`).
fn first_chars(s: &str, n: usize) -> &str {
    match s.char_indices().nth(n) {
        Some((i, _)) => &s[..i],
        None => s,
    }
}

/// **The scan sandbox, from inside the laid box** (lane HOLGER-MEDIA): the same assertions as
/// `holger-sandbox`'s `live_podman` probe, over the medium's own engine. `walk` is the pip walk
/// (it must show SCANNED: the scan ran in the sandbox, or the chain would have refused by name).
fn sandbox_probe(d: &Door, b: &Bearers, walk: &[String]) -> std::result::Result<String, String> {
    if !walk.iter().any(|w| w.ends_with("→SCANNED")) {
        return Err(format!(
            "[installtest-sandbox-walk] the pip walk never reached SCANNED: {}",
            walk.join(", ")
        ));
    }
    let v: serde_json::Value = match d.call("GET", "/-/sandbox/probe", Some(&b.console), None, None)
    {
        Ok((200, body)) => serde_json::from_slice(&body)
            .map_err(|e| format!("[installtest-sandbox-probe] not JSON: {e}"))?,
        other => {
            return Err(format!(
                "[installtest-sandbox-probe] GET /-/sandbox/probe: {}",
                said(&other)
            ))
        }
    };
    let s = |k: &str| v[k].as_str().unwrap_or("").to_string();
    let mut red = Vec::new();
    for k in ["connect_public", "connect_gateway"] {
        if !s(k).starts_with("refused") {
            red.push(format!("{k}={}", s(k)));
        }
    }
    if s("resolve").starts_with("Ok(") {
        red.push(format!("resolve={}", s("resolve")));
    }
    for k in ["write_root", "write_etc", "write_in", "write_traversal"] {
        if !s(k).starts_with("refused") {
            red.push(format!("{k}={}", s(k)));
        }
    }
    if s("write_scratch") != "written" {
        red.push(format!("write_scratch={}", s("write_scratch")));
    }
    if !s("write_past_scratch_cap").starts_with("refused") {
        red.push(format!(
            "write_past_scratch_cap={}",
            s("write_past_scratch_cap")
        ));
    }
    if !s("uid").starts_with("65532") {
        red.push(format!("uid={}", s("uid")));
    }
    if s("cap_eff") != "0000000000000000" {
        red.push(format!("cap_eff={}", s("cap_eff")));
    }
    if s("no_new_privs") != "1" {
        red.push(format!("no_new_privs={}", s("no_new_privs")));
    }
    if s("seccomp") != "2" {
        red.push(format!("seccomp={}", s("seccomp")));
    }
    if v["runs"].as_u64().unwrap_or(0) == 0 {
        red.push("runs=0".into());
    }
    if !red.is_empty() {
        return Err(format!(
            "[installtest-sandbox-limits] the sandbox's limits do not hold from inside: {} ({v})",
            red.join(", ")
        ));
    }
    Ok(format!(
        "the pip walk passed SCANNED in the sandbox; from inside: no network, read-only root, scratch cap holds, uid {}, CapEff {}, NoNewPrivs {}, Seccomp {}; engine: {}; {} runs",
        s("uid").split_whitespace().next().unwrap_or(""),
        s("cap_eff"),
        s("no_new_privs"),
        s("seccomp"),
        s("engine"),
        v["runs"]
    ))
}

/// Signed in (HHUNT-1): on a gated estate an anonymous pull is 401 whatever is there, so only a
/// signed-in pull of a crate nobody pushed shows that the pull check can say no.
fn red_fault(d: &Door, b: &Bearers) -> std::result::Result<String, String> {
    let path = format!("/crates/crates/{CRATE}/{CRATE_VERSION}/download");
    match d.call("GET", &path, Some(&b.console), None, None) {
        Ok((200, b)) => Err(format!("[installtest-red-fault] a pull of a crate nobody pushed answered 200 ({} bytes): the pull check could never say no", b.len())),
        Ok((st, _)) => Ok(format!("a pull before any push is refused (http {st})")),
        Err(e) => Err(format!("[installtest-red-fault] the door did not answer the empty pull: {e}")),
    }
}

/// The probes pull SIGNED IN: the estate medium gates reads (`access: (reads: Gated)`, HCON-5),
/// so a pull carries the console's bearer as a user's client carries the estate's token. The
/// anonymous refusal is the `red-fault-empty-pull` row's.
fn cargo_probe(d: &Door, b: &Bearers, a: &Artifacts) -> std::result::Result<String, String> {
    let want = sha256_hex(&a.crate_bytes);
    let put = d.call(
        "PUT",
        "/crates/api/v1/crates/new",
        Some(&b.console),
        Some("application/octet-stream"),
        Some(&cargo_publish_body(CRATE, CRATE_VERSION, &a.crate_bytes)),
    );
    if !matches!(put, Ok((200..=299, _))) {
        return Err(format!(
            "[installtest-cargo-push] cargo publish: {}",
            said(&put)
        ));
    }
    // The publish is INGESTED at the cargo door; Holger lists it only once it is AVAILABLE.
    let walk = walk_to_available(
        d,
        b,
        &format!("crates/{CRATE}/{CRATE}-{CRATE_VERSION}.crate"),
        "cargo",
    )?;
    let idx = d.call(
        "GET",
        &format!("/crates/index/{}", sparse_path(CRATE)),
        Some(&b.console),
        None,
        None,
    );
    let cksum = match &idx {
        Ok((200, body)) => index_cksum(&String::from_utf8_lossy(body), CRATE_VERSION),
        _ => None,
    };
    if cksum.as_deref() != Some(want.as_str()) {
        return Err(format!("[installtest-cargo-index] the sparse index names {CRATE} {CRATE_VERSION} with cksum {cksum:?}, the crate is {want}: {}", said(&idx)));
    }
    match d.call(
        "GET",
        &format!("/crates/crates/{CRATE}/{CRATE_VERSION}/download"),
        Some(&b.console),
        None,
        None,
    ) {
        Ok((200, got)) if sha256_hex(&got) == want => Ok(format!(
            "published, walked ({}) and pulled back {} bytes, sha256 {}",
            walk.join(", "),
            got.len(),
            &want[..16]
        )),
        other => Err(format!(
            "[installtest-cargo-pull] the download is not the pushed crate: {}",
            said(&other)
        )),
    }
}

fn pip_probe(d: &Door, b: &Bearers, a: &Artifacts) -> std::result::Result<String, String> {
    let file = wheel_name();
    let want = sha256_hex(&a.wheel);
    let (ctype, body) = twine_form(&file, &a.wheel);
    let up = d.call(
        "POST",
        "/incoming/legacy/",
        Some(&b.console),
        Some(&ctype),
        Some(&body),
    );
    if !matches!(up, Ok((200..=299, _))) {
        return Err(format!(
            "[installtest-pip-push] twine upload: {}",
            said(&up)
        ));
    }
    let walk = walk_to_available(d, b, &file, "pip")?;
    let page = d.call(
        "GET",
        &format!("/production/simple/{PIP_PROJECT}/"),
        Some(&b.console),
        None,
        None,
    );
    let html = match &page {
        Ok((200, b)) => String::from_utf8_lossy(b).to_string(),
        _ => {
            return Err(format!(
                "[installtest-pip-index] the simple page: {}",
                said(&page)
            ))
        }
    };
    if !html.contains(&file) || !html.contains(&want) {
        return Err(format!(
            "[installtest-pip-index] the simple page does not list {file} with sha256 {}: {}",
            &want[..16],
            &html[..html.len().min(300)]
        ));
    }
    match d.call(
        "GET",
        &format!("/production/{file}"),
        Some(&b.console),
        None,
        None,
    ) {
        Ok((200, got)) if sha256_hex(&got) == want => Ok(format!(
            "uploaded, walked ({}), listed and pulled back {} bytes, sha256 {}",
            walk.join(", "),
            got.len(),
            &want[..16]
        )),
        other => Err(format!(
            "[installtest-pip-pull] the linked file is not the pushed wheel: {}",
            said(&other)
        )),
    }
}

/// The walk a pip probe's detail names (`walked (console→SCANNED, …)`), for the sandbox row.
fn walk_of(detail: &str) -> Vec<String> {
    detail
        .split_once("walked (")
        .and_then(|(_, rest)| rest.split_once(')'))
        .map(|(w, _)| w.split(", ").map(str::to_string).collect())
        .unwrap_or_default()
}

/// The intake walk: whoever may take the next step takes it, until AVAILABLE. `name` is the
/// coordinate (the wheel's file name, the npm tarball's registry path); `leg` names the code.
fn walk_to_available(
    d: &Door,
    b: &Bearers,
    name: &str,
    leg: &str,
) -> std::result::Result<Vec<String>, String> {
    let actors: Vec<&str> = std::iter::once(b.console.as_str())
        .chain(b.approvers.iter().map(String::as_str))
        .collect();
    let mut walk = Vec::new();
    let mut state = String::from("INGESTED");
    'walk: for _ in 0..12 {
        if state == "AVAILABLE" {
            break;
        }
        let mut refusals = Vec::new();
        for (i, t) in actors.iter().enumerate() {
            let r = d.call(
                "POST",
                &format!("/-/intake/advance?name={name}&verb=advance"),
                Some(t),
                None,
                Some(b""),
            );
            match &r {
                Ok((200, body)) => {
                    let v: serde_json::Value = serde_json::from_slice(body).unwrap_or_default();
                    state = v["state"].as_str().unwrap_or("?").to_string();
                    walk.push(format!(
                        "{}→{state}",
                        if i == 0 { "console" } else { APPROVERS[i - 1] }
                    ));
                    continue 'walk;
                }
                _ => refusals.push(said(&r)),
            }
        }
        return Err(format!(
            "[installtest-{leg}-walk] held at {state}; nobody may advance it ({}); walk so far: {}",
            refusals.join(" | "),
            walk.join(", ")
        ));
    }
    if state != "AVAILABLE" {
        return Err(format!(
            "[installtest-{leg}-walk] never AVAILABLE: {}",
            walk.join(", ")
        ));
    }
    Ok(walk)
}

/// **The advisory database the airgap box imports**: three ed25519 signers minted for this
/// run (two approvers and a release signer), the trust policy that pins exactly them for
/// `holger.intel/v1`, and a signed bundle of a fresh advisory set.
pub struct IntelFeed {
    pub policy_ron: String,
    pub bundle: Vec<u8>,
    /// The bundle's sequence: the one the admin's first-import authorization names.
    pub sequence: u64,
}

impl IntelFeed {
    pub fn mint(work: &Path) -> Result<Self> {
        use holger_intel::chrono::{Duration as Days, Utc};
        use update_bundle::policy::{
            ApprovedSigner, CertificateValidity, ExpiredPosture, TrustMode, TrustPolicy,
        };
        let mint = |role: &str| -> Result<(holger_intel::SignerFiles, String)> {
            let kp = rcgen::KeyPair::generate_for(&rcgen::PKCS_ED25519)
                .context("mint an ed25519 key")?;
            let mut params = rcgen::CertificateParams::new(vec![]).context("cert params")?;
            params.distinguished_name.push(
                rcgen::DnType::CommonName,
                format!("holger installtest intel {role}"),
            );
            let cert = params.self_signed(&kp).context("self-sign")?;
            let sf = holger_intel::SignerFiles {
                cert_der: cert.der().to_vec(),
                key_pkcs8: kp.serialize_der(),
            };
            let fp = update_bundle::digest::sha256_hex(&sf.cert_der);
            Ok((sf, fp))
        };
        let (reviewer, reviewer_fp) = mint("security-reviewer")?;
        let (owner, owner_fp) = mint("system-owner")?;
        let (release, release_fp) = mint("release")?;
        let policy = TrustPolicy {
            schema_version: update_bundle::schema::POLICY_SCHEMA.into(),
            trust_mode: Some(TrustMode::PinnedSigners),
            trusted_roots: vec![],
            certificate_validity: CertificateValidity::Enforce,
            approved_signers: vec![
                ApprovedSigner {
                    fingerprint: reviewer_fp,
                    subject: None,
                    roles: vec!["security-reviewer".into()],
                },
                ApprovedSigner {
                    fingerprint: owner_fp,
                    subject: None,
                    roles: vec!["system-owner".into()],
                },
            ],
            required_approval_roles: vec!["security-reviewer".into(), "system-owner".into()],
            minimum_distinct_approvers: 2,
            trusted_release_signers: vec![release_fp],
            release_signer_must_be_distinct: true,
            maximum_bundle_age_secs: None,
            require_expiry: true,
            on_expired_metadata: Some(ExpiredPosture::Refuse),
            require_sbom: false,
            require_vex: false,
            require_scan: false,
            allowed_products: vec![holger_intel::INTEL_PRODUCT.into()],
            allowed_models: vec![],
        };
        let now = Utc::now();
        let set = holger_intel::AdvisorySet {
            schema: holger_intel::set::SET_SCHEMA.into(),
            generated_at: now,
            sources: vec![],
            advisories: vec![],
        };
        let out = work.join("installtest-intel.holger.znippy");
        let sequence = holger_intel::bundle::sequence_for(now);
        holger_intel::build_bundle(
            &set,
            holger_intel::BuildOptions {
                sequence,
                created_at: now,
                expires_at: Some(now + Days::days(30)),
                supplier: holger_intel::DEFAULT_SUPPLIER.into(),
                producer: "holger cargo xtask installtest".into(),
                policy: policy.clone(),
                approvals: vec![
                    ("security-reviewer".into(), reviewer),
                    ("system-owner".into(), owner),
                ],
                release,
                out: out.clone(),
            },
        )
        .context("[installtest-intel] sign the advisory set")?;
        let policy_ron = policy
            .to_ron()
            .map_err(|e| anyhow::anyhow!("[installtest-intel] the trust policy: {e}"))?;
        Ok(Self {
            policy_ron,
            bundle: std::fs::read(&out).with_context(|| format!("read {}", out.display()))?,
            sequence,
        })
    }
}

/// The airgap box's intel, the way an airgap estate takes it: the trust policy rode the medium
/// onto the release; the console (RepositoryAdmin) authorizes its first import
/// (`POST /-/bundle/first-import`, lane HOL-CDS) and the signed bundle goes to
/// `POST /-/intel/import`.
fn intel_probe(d: &Door, b: &Bearers, feed: &IntelFeed) -> std::result::Result<String, String> {
    let grant = format!(
        "/-/bundle/first-import?vendor={}&product={}&sequence={}",
        holger_intel::DEFAULT_SUPPLIER,
        holger_intel::INTEL_PRODUCT.replace('/', "%2F"),
        feed.sequence
    );
    match d.call("POST", &grant, Some(&b.console), None, Some(b"")) {
        Ok((200, _)) => {}
        other => {
            return Err(format!(
                "[installtest-intel-import] POST /-/bundle/first-import: {}",
                said(&other)
            ))
        }
    }
    let imp = d.call(
        "POST",
        "/-/intel/import",
        Some(&b.console),
        Some("application/octet-stream"),
        Some(&feed.bundle),
    );
    match &imp {
        Ok((200, body)) => {
            let v: serde_json::Value = serde_json::from_slice(body).unwrap_or_default();
            Ok(format!(
                "a signed intel bundle imported ({} seq {}, {} advisories)",
                v["bundle_id"].as_str().unwrap_or("?"),
                v["sequence"],
                v["advisories"]
            ))
        }
        _ => Err(format!(
            "[installtest-intel-import] POST /-/intel/import: {}",
            said(&imp)
        )),
    }
}

/// The npm intake coordinate: the tarball's registry path below the npm repository.
pub fn npm_coordinate() -> String {
    format!("{NPM_PACKAGE}/-/{NPM_PACKAGE}-{NPM_VERSION}.tgz")
}

fn npm_probe(
    d: &Door,
    b: &Bearers,
    a: &Artifacts,
    declaration: &str,
) -> std::result::Result<String, String> {
    let want = sha256_hex(&a.tgz);
    let undeclared = if declaration.contains("ron_repo_type: \"npm\"") {
        ""
    } else {
        " (the estate declaration names no npm repository)"
    };
    let put = d.call(
        "PUT",
        &format!("/npm/{NPM_PACKAGE}"),
        Some(&b.console),
        Some("application/json"),
        Some(npm_publish_doc(&a.tgz).as_bytes()),
    );
    if !matches!(put, Ok((200..=299, _))) {
        return Err(format!(
            "[installtest-npm-push] npm publish: {}{undeclared}",
            said(&put)
        ));
    }
    // The publish is INGESTED; Holger serves it only once it is AVAILABLE (the pip walk).
    let walk = walk_to_available(d, b, &npm_coordinate(), "npm")?;
    let doc = d.call(
        "GET",
        &format!("/npm/{NPM_PACKAGE}"),
        Some(&b.console),
        None,
        None,
    );
    let tarball = match &doc {
        Ok((200, body)) => serde_json::from_slice::<serde_json::Value>(body)
            .ok()
            .and_then(|v| {
                v["versions"][NPM_VERSION]["dist"]["tarball"]
                    .as_str()
                    .map(str::to_string)
            }),
        _ => None,
    };
    let Some(tarball) = tarball else {
        return Err(format!(
            "[installtest-npm-packument] no tarball for {NPM_VERSION}: {}",
            said(&doc)
        ));
    };
    // The packument may name the public host: the path is what this door serves.
    let path = tarball
        .find("/npm/")
        .map(|i| tarball[i..].to_string())
        .unwrap_or(tarball);
    match d.call("GET", &path, Some(&b.console), None, None) {
        Ok((200, got)) if sha256_hex(&got) == want => Ok(format!(
            "published, walked ({}) and pulled back {} bytes, sha256 {}",
            walk.join(", "),
            got.len(),
            &want[..16]
        )),
        other => Err(format!(
            "[installtest-npm-pull] {path} is not the pushed tarball: {}",
            said(&other)
        )),
    }
}

// ── the receipt (pure) ──────────────────────────────────────────────────────

/// The receipt's file name for this holger HEAD.
pub fn receipt_name(holger_head: &str) -> String {
    format!(
        "holger-installtest-{}.json",
        holger_head.chars().take(12).collect::<String>()
    )
}

/// Pure: the receipt. Green only with every leg and probe passed and the medium this HEAD's.
pub fn receipt(
    commits: &BTreeMap<String, String>,
    medium: &serde_json::Value,
    legs: &[Leg],
    probes: &[ProbeRow],
    medium_is_head: bool,
    at_unix: u64,
) -> serde_json::Value {
    let legs_ok = legs.len() >= 2 && legs.iter().all(|l| l.pass);
    let probes_ok = probes.len() >= 5 && probes.iter().all(|p| p.pass);
    serde_json::json!({
        "test": "holger-installtest",
        "green": legs_ok && probes_ok && medium_is_head,
        "medium_is_head": medium_is_head,
        "commits": commits,
        "medium": medium,
        "legs": legs.iter().map(|l| serde_json::json!({"name": l.name, "pass": l.pass, "ms": l.ms as u64, "detail": l.detail, "serial_tail": l.serial_tail})).collect::<Vec<_>>(),
        "probes": probes.iter().map(|p| serde_json::json!({"name": p.name, "pass": p.pass, "detail": p.detail})).collect::<Vec<_>>(),
        "at_unix": at_unix,
    })
}

// ── the run ─────────────────────────────────────────────────────────────────

fn head(dir: &Path) -> String {
    Command::new("git")
        .arg("-C")
        .arg(dir)
        .args(["rev-parse", "HEAD"])
        .output()
        .ok()
        .filter(|o| o.status.success())
        .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
        .unwrap_or_default()
}

/// The full HEADs of [`NAMED_BY`] (holger here, the rest beside it).
pub fn commits(root: &Path) -> BTreeMap<String, String> {
    NAMED_BY
        .iter()
        .map(|r| {
            (
                r.to_string(),
                head(&if *r == "holger" {
                    root.to_path_buf()
                } else {
                    root.parent().unwrap_or(root).join(r)
                }),
            )
        })
        .collect()
}

struct Lock(PathBuf);

impl Lock {
    fn take() -> Result<Self> {
        let dir = PathBuf::from(std::env::var_os("HOME").context("$HOME")?).join("locks");
        std::fs::create_dir_all(&dir)?;
        let p = dir.join("holger-installtest.lock");
        if let Ok(pid) = std::fs::read_to_string(&p) {
            if Path::new(&format!("/proc/{}", pid.trim())).exists() {
                bail!(
                    "[installtest-busy] another install test runs (pid {}, {}): one at a time",
                    pid.trim(),
                    p.display()
                );
            }
            let _ = std::fs::remove_file(&p);
        }
        let mut f = std::fs::OpenOptions::new()
            .write(true)
            .create_new(true)
            .open(&p)
            .with_context(|| format!("{}", p.display()))?;
        write!(f, "{}", std::process::id())?;
        Ok(Self(p))
    }
}

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

/// Compose the DEV main medium with the test declaration and move it out of `.iso-out`.
fn compose(root: &Path, work: &Path, ron: &str, intel_policy: &str) -> Result<PathBuf> {
    let suite = forge_door::declared_medium(
        VARIANT,
        ArtifactKind::Installer,
        "cargo xtask installer-iso",
    )?;
    let stage = work.join("stage");
    std::fs::create_dir_all(&stage)?;
    let staged = estate_media::Staged {
        marker: stage.join("seed-from-metadata.toml"),
        ron: stage.join("installtest-holger.ron"),
        korp_root: stage.join(estate_media::KORP_ROOT_FILE),
        intel_trust: Some(stage.join(estate_media::INTEL_TRUST_FILE)),
        bundles_trust: None,
    };
    std::fs::write(&staged.marker, estate_media::SEED_FROM_METADATA)?;
    std::fs::write(&staged.ron, ron)?;
    std::fs::write(&staged.korp_root, holger_recipe::estate_ron::KORP_ROOT_PEM)?;
    if let Some(why) = estate_media::intel_trust_refusal(intel_policy) {
        bail!("{why}");
    }
    std::fs::write(
        staged.intel_trust.as_ref().expect("staged above"),
        intel_policy,
    )?;
    // No seed marker: see the module (step 1).
    let mut params: Vec<String> = estate_media::forge_params(VARIANT, &staged)
        .into_iter()
        .filter(|p| !p.starts_with("pairing_seed="))
        .collect();
    params.extend(estate_media::live_update_params(true));
    let ask = forge_door::Ask {
        variant: Some(VARIANT.to_string()),
        params,
        opts: estate_media::forge_opts(estate_media::KIND_DEV),
        builds: vec!["kernel".to_string()],
        ..Default::default()
    };
    let iso = forge_door::run("installtest", root, &suite, VARIANT, &ask)?;
    // Out of `.iso-out`: a medium with the test's bearers is never uploaded.
    let (rec, app) = estate_media::live_companions(&iso);
    let mut moved = None;
    // The live release's signed bundle and its signature carry the test declaration too.
    let (bundle, sig) = (
        iso.with_extension("live-release.bundle"),
        iso.with_extension("live-release.sig"),
    );
    for from in [
        iso.clone(),
        iso.with_extension("medium.json"),
        rec,
        app,
        bundle,
        sig,
    ] {
        if let Some(name) = from.file_name() {
            let to = work.join(name);
            if from.is_file() {
                std::fs::rename(&from, &to)
                    .or_else(|_| {
                        std::fs::copy(&from, &to)
                            .map(|_| ())
                            .and_then(|_| std::fs::remove_file(&from))
                    })
                    .with_context(|| format!("move {} out of .iso-out", from.display()))?;
                if from == iso {
                    moved = Some(to);
                }
            }
        }
    }
    moved.context("the composed medium vanished")
}

/// Whether the medium's record (`<iso>.medium.json` beside it) names this HEAD.
fn medium_is_head(iso: &Path, holger_head: &str) -> (bool, serde_json::Value) {
    let rec = std::fs::read_to_string(iso.with_extension("medium.json"))
        .ok()
        .and_then(|s| serde_json::from_str::<serde_json::Value>(&s).ok())
        .unwrap_or_default();
    let named = rec["head"].as_str().unwrap_or("").to_string();
    let sha = std::fs::read(iso)
        .map(|b| sha256_hex(&b))
        .unwrap_or_default();
    (
        named.len() >= 7 && holger_head.starts_with(&named),
        serde_json::json!({"path": iso.display().to_string(), "sha256": sha, "record_head": named, "variant": VARIANT, "kind": estate_media::KIND_DEV}),
    )
}

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

fn run_with(o: &Opts, root: &Path) -> Result<()> {
    let c = commits(root);
    let holger_head = c.get("holger").cloned().unwrap_or_default();
    let work = o.work.clone().unwrap_or_else(|| {
        PathBuf::from(std::env::var_os("HOME").unwrap_or_default())
            .join("scratch/holger-installtest")
            .join(holger_head.chars().take(12).collect::<String>())
    });
    println!("installtest: holger {} — {} → KVM (SeaBIOS, {BOOT_DISK_GIB}+{DATA_DISK_GIB} GiB, -no-reboot) → boot the laid disks → serve → cargo, pip, npm push and pull", &holger_head[..holger_head.len().min(12)], o.iso.as_ref().map(|p| p.display().to_string()).unwrap_or_else(|| format!("compose {VARIANT} (DEV)")));
    println!(
        "  work: {}\n  receipt: {}/{}",
        work.display(),
        root.join(RECEIPTS).display(),
        receipt_name(&holger_head)
    );
    if o.plan {
        println!("  PLAN ONLY.");
        return Ok(());
    }
    let _lock = Lock::take()?;
    let qemu = kvmproof::which("qemu-system-x86_64")
        .context("[installtest-no-qemu] no qemu-system-x86_64 on PATH")?;
    if !Path::new("/dev/kvm").exists() {
        bail!("[installtest-no-kvm] /dev/kvm is absent: the install test runs on a KVM host");
    }
    std::fs::create_dir_all(&work)?;
    let bearers = Bearers::mint()?;
    let ron = declaration(&bearers)?;
    // The run's intel signers before the medium: their policy rides it (`intel_trust_policy`).
    let intel_feed = IntelFeed::mint(&work)?;
    let iso = match &o.iso {
        Some(p) => p.clone(),
        None => compose(root, &work, &ron, &intel_feed.policy_ron)?,
    };
    let (is_head, medium) = medium_is_head(&iso, &holger_head);
    let declaration_used = if o.iso.is_some() {
        None
    } else {
        Some(ron.clone())
    };
    let artifacts = Artifacts {
        crate_bytes: package_crate(&work)?,
        wheel: wheel_bytes(),
        tgz: npm_tarball(),
    };
    let port = korp_installer::isoproof::free_port();
    let mut machine = Machine::seabios();
    // 4 GiB: the laid box runs clamd in its sandbox (about 1.3 GiB with the full database) and a scan
    // worker with a 1 GiB ceiling beside the server (lane HOLGER-MEDIA).
    machine.mem_mb = 4096;
    machine.smp = 2;
    let proof = InstallThenServe {
        product: "holger".into(),
        qemu,
        machine,
        iso: iso.clone(),
        attach: MediumAttach::Cdrom,
        disks: vec![
            Disk {
                path: work.join("sys.img"),
                bytes: BOOT_DISK_GIB << 30,
            },
            Disk {
                path: work.join("data.img"),
                bytes: DATA_DISK_GIB << 30,
            },
        ],
        workdir: work.clone(),
        install_budget: Duration::from_secs(o.install_min * 60),
        serve_budget: Duration::from_secs(o.serve_min * 60),
        nics: vec![
            SlirpNic {
                net: None,
                hostfwds: vec![(port, HTTP_DOOR)],
            },
            SlirpNic {
                net: Some("10.0.3.0/24".into()),
                hostfwds: vec![],
            },
        ],
        nic_model: "virtio-net-pci".into(),
        refuse_serial: Vec::new(),
        probe_every: Duration::from_secs(5),
    };
    let mut legs = Vec::new();
    let mut probes: Vec<ProbeRow> = Vec::new();
    println!("── install ({} min budget) ──", o.install_min);
    let install = proof.install();
    println!(
        "  {} install  {} ms  {}",
        if install.pass { "PASS" } else { "FAIL" },
        install.ms,
        install.detail
    );
    let installed = install.pass;
    legs.push(install);
    if installed {
        println!("── boot the laid disks, wait for http://127.0.0.1:{port} (guest :{HTTP_DOOR}), then push and pull ──");
        let door = Door::new(port);
        let ron_for_npm = declaration_used.clone().unwrap_or_default();
        let mut collected: Vec<ProbeRow> = Vec::new();
        let serve = proof.serve(&mut |_serial| {
            // Serving at all: the index root answers with any status.
            match door.call("GET", "/crates/index/config.json", None, None, None) {
                Err(_) => return Probe::Wait,
                Ok((st, _)) => println!("  the door answers (http {st} on /crates/index/config.json)"),
            }
            collected.push(row("red-fault-empty-pull", red_fault(&door, &bearers)));
            // The intel bundle first: since the cargo door (HOL-CARGO-PUBLISH-DOOR) a crate walks
            // the intake chain like pip and npm, and a box with no advisory database blocks every
            // scan (HOLGER-INTEL-MISSING), so a crate pushed before the import is held at
            // INGESTED (the 3.1.1 KVM gate on dwarf, 2026-11-02).
            collected.push(row("intel", intel_probe(&door, &bearers, &intel_feed)));
            collected.push(row("cargo", cargo_probe(&door, &bearers, &artifacts)));
            let pip = pip_probe(&door, &bearers, &artifacts);
            let pip_walk: Vec<String> = pip.as_ref().ok().map(|d| walk_of(d)).unwrap_or_default();
            collected.push(row("pip", pip));
            collected.push(row("npm", npm_probe(&door, &bearers, &artifacts, &ron_for_npm)));
            collected.push(row("sandbox", sandbox_probe(&door, &bearers, &pip_walk)));
            collected.push(row("board", board_probe(&door, &bearers)));
            let red: Vec<String> = collected.iter().filter(|p| !p.pass).map(|p| format!("{}: {}", p.name, p.detail)).collect();
            Probe::Done(if red.is_empty() { Ok("cargo, pip and npm pushed and pulled back byte for byte; the sandbox probed; the board drawn".into()) } else { Err(red.join(" | ")) })
        });
        probes = collected;
        println!(
            "  {} serve  {} ms  {}",
            if serve.pass { "PASS" } else { "FAIL" },
            serve.ms,
            serve.detail
        );
        if !serve.pass && !serve.serial_tail.is_empty() {
            println!(
                "  serial tail:\n{}",
                serve
                    .serial_tail
                    .lines()
                    .map(|l| format!("    | {l}"))
                    .collect::<Vec<_>>()
                    .join("\n")
            );
        }
        legs.push(serve);
    } else if !legs[0].serial_tail.is_empty() {
        println!(
            "  serial tail:\n{}",
            legs[0]
                .serial_tail
                .lines()
                .map(|l| format!("    | {l}"))
                .collect::<Vec<_>>()
                .join("\n")
        );
    }
    for p in &probes {
        println!(
            "  {} {:<22} {}",
            if p.pass { "PASS" } else { "FAIL" },
            p.name,
            p.detail
        );
    }
    let at = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map(|d| d.as_secs())
        .unwrap_or(0);
    let r = receipt(&c, &medium, &legs, &probes, is_head, at);
    let dir = root.join(RECEIPTS);
    std::fs::create_dir_all(&dir)?;
    let path = dir.join(receipt_name(&holger_head));
    std::fs::write(&path, serde_json::to_string_pretty(&r)?)?;
    std::fs::write(
        work.join(receipt_name(&holger_head)),
        serde_json::to_string_pretty(&r)?,
    )?;
    if !o.keep_disks {
        for d in &proof.disks {
            let _ = std::fs::remove_file(&d.path);
        }
    }
    let green = r["green"].as_bool() == Some(true);
    println!(
        "\ninstalltest: {}\nreceipt: {}",
        if green { "GREEN" } else { "RED" },
        path.display()
    );
    if !is_head {
        println!(
            "  note: the medium's record does not name this HEAD ({}): never green",
            medium["record_head"]
        );
    }
    if !green {
        bail!("installtest RED: see {}", path.display());
    }
    Ok(())
}

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

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

    #[test]
    fn the_words_are_understood_and_strays_refused() {
        let o = parse(&s("--iso /m.iso --serve-min 5 --keep-disks")).unwrap();
        assert_eq!(
            (o.iso, o.serve_min, o.keep_disks, o.install_min),
            (Some(PathBuf::from("/m.iso")), 5, true, 20)
        );
        assert!(parse(&s("--wat")).is_err());
        assert!(parse(&s("--iso")).is_err());
    }

    fn bearers() -> Bearers {
        Bearers {
            console: "installtest-c0".into(),
            approvers: ["installtest-a1".into(), "installtest-a2".into()],
        }
    }

    /// A door that gates reads as the estate medium declares (`access: (reads: Gated)`,
    /// HCON-5): every GET without the console's bearer is `401 [read-gated]`. It serves one
    /// published crate: the push, its walk through the chain (the console takes it to
    /// AVAILABLE), its sparse index line and its download.
    fn gated_door(crate_bytes: Vec<u8>) -> u16 {
        use std::io::{BufRead as _, Read as _, Write as _};
        let l = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
        let port = l.local_addr().unwrap().port();
        std::thread::spawn(move || {
            for c in l.incoming() {
                let Ok(c) = c else { continue };
                let mut r = std::io::BufReader::new(c.try_clone().unwrap());
                let mut line = String::new();
                r.read_line(&mut line).unwrap();
                let (mut len, mut bearer) = (0usize, false);
                loop {
                    let mut h = String::new();
                    r.read_line(&mut h).unwrap();
                    let lower = h.to_ascii_lowercase();
                    if let Some(v) = lower.strip_prefix("content-length:") {
                        len = v.trim().parse().unwrap_or(0);
                    }
                    if lower.starts_with("authorization:") && h.contains("Bearer installtest-c0") {
                        bearer = true;
                    }
                    if h == "\r\n" || h.is_empty() {
                        break;
                    }
                }
                let mut body = vec![0u8; len];
                r.read_exact(&mut body).unwrap();
                let (st, out): (u16, Vec<u8>) = if line.starts_with("PUT /crates/api/v1/crates/new")
                {
                    (if bearer { 200 } else { 401 }, b"{}".to_vec())
                } else if bearer && line.starts_with(&format!(
                    "POST /-/intake/advance?name=crates/{CRATE}/{CRATE}-{CRATE_VERSION}.crate&verb=advance "
                )) {
                    (200, br#"{"state":"AVAILABLE"}"#.to_vec())
                } else if !bearer {
                    (401, b"[read-gated] reading `crates` on this Holger needs a sign-in (it is not a public repository)".to_vec())
                } else if line.starts_with(&format!("GET /crates/index/{} ", sparse_path(CRATE))) {
                    (200, format!("{{\"name\":\"{CRATE}\",\"vers\":\"{CRATE_VERSION}\",\"cksum\":\"{}\"}}\n", sha256_hex(&crate_bytes)).into_bytes())
                } else if line.starts_with(&format!(
                    "GET /crates/crates/{CRATE}/{CRATE_VERSION}/download "
                )) {
                    (200, crate_bytes.clone())
                } else {
                    (404, b"no".to_vec())
                };
                let mut c = c;
                let _ = write!(
                    c,
                    "HTTP/1.1 {st} X\r\nContent-Length: {}\r\nConnection: close\r\n\r\n",
                    out.len()
                );
                let _ = c.write_all(&out);
            }
        });
        port
    }

    /// ★ **HHUNT-1: on a gated estate the probes pull signed in.** The estate medium declares
    /// `access: (reads: Gated)` since HCON-5 (c57ba731), so an anonymous pull is `401
    /// [read-gated]`, as a user's cargo without a token sees it. The installtest's client is
    /// the console (the shop's bearer): its pulls carry that bearer, as `cargo` with the
    /// estate's token does.
    #[test]
    fn on_a_gated_estate_the_probes_pull_signed_in() {
        let crate_bytes = b"the crate".to_vec();
        let a = Artifacts {
            crate_bytes: crate_bytes.clone(),
            wheel: Vec::new(),
            tgz: Vec::new(),
        };
        let d = Door::new(gated_door(crate_bytes));
        let got = cargo_probe(&d, &bearers(), &a);
        assert!(
            got.as_ref()
                .is_ok_and(|s| s
                    .starts_with("published, walked (console→AVAILABLE) and pulled back 9 bytes")),
            "{got:?}"
        );
    }

    #[test]
    fn the_declaration_is_the_estates_with_the_fed_bearer_and_two_approvers() {
        let r = declaration(&bearers()).unwrap();
        assert!(
            r.contains("StaticToken(subject: \"console\", token: Some(\"installtest-c0\"))"),
            "{r}"
        );
        assert!(
            !r.contains("token_file"),
            "no bearer file a feed would write: {r}"
        );
        assert!(
            r.contains("\"installtest-approver-b\": [Developer, SecurityApprover]"),
            "{r}"
        );
        assert!(
            r.contains("\"console\": [Importer, RepositoryAdmin, Auditor]"),
            "the console's duties are the estate's: {r}"
        );
        // Everything else is the estate's own.
        let base = holger_recipe::estate_ron::render(
            &holger_recipe::estate_ron::EstateRon::on_a_box(Some(ESTATE)).airgapped(),
        );
        assert!(
            r.contains(&format!(
                "offline: true, trust_policy: Some(\"{}\")",
                intel_trust_on_box()
            )),
            "the airgap box verifies intel against the policy its medium carries: {r}"
        );
        assert_eq!(intel_trust_on_box(), "/run/korp/release/intel-trust.ron");
        for l in base.lines().filter(|l| {
            !l.contains("methods:")
                && !l.contains("roles: {\"console\"")
                && !l.contains("duties: {\"console\"")
        }) {
            assert!(
                r.contains(l),
                "the test declaration lost an estate line: {l}"
            );
        }
    }

    #[test]
    fn the_artifacts_are_what_the_clients_send() {
        let body = cargo_publish_body("x", "0.1.0", b"crate");
        let n = u32::from_le_bytes(body[..4].try_into().unwrap()) as usize;
        let meta: serde_json::Value = serde_json::from_slice(&body[4..4 + n]).unwrap();
        assert_eq!(
            (meta["name"].as_str(), meta["vers"].as_str()),
            (Some("x"), Some("0.1.0"))
        );
        assert_eq!(&body[8 + n..], b"crate");
        assert_eq!(sparse_path(CRATE), "ho/lg/holger-installtest-probe");
        assert_eq!(sparse_path("ab"), "2/ab");
        assert_eq!(sparse_path("abc"), "3/a/abc");
        assert_eq!(
            index_cksum("{\"vers\":\"0.1.0\",\"cksum\":\"ff\"}\n", "0.1.0").as_deref(),
            Some("ff")
        );
        let w = wheel_bytes();
        assert!(w.starts_with(b"PK\x03\x04") && w.windows(4).any(|x| x == b"PK\x05\x06"));
        assert_eq!(
            wheel_name(),
            "holger_installtest_probe-0.1.0-py3-none-any.whl"
        );
        assert_eq!(
            walk_of(
                "uploaded, walked (console→SCANNED, alice→APPROVED, console→AVAILABLE), listed"
            ),
            ["console→SCANNED", "alice→APPROVED", "console→AVAILABLE"]
        );
        assert!(walk_of("no walk here").is_empty());
        let t = npm_tarball();
        assert_eq!(&t[..3], &[0x1f, 0x8b, 8]);
        assert_eq!(sha1_hex(b"abc"), "a9993e364706816aba3e25717850c26c9cd0d89d");
        assert_eq!(b64(b"hello", false), "aGVsbG8=");
        assert_eq!(b64(b"hello", true), "aGVsbG8");
        let doc: serde_json::Value = serde_json::from_str(&npm_publish_doc(&t)).unwrap();
        assert_eq!(doc["versions"][NPM_VERSION]["name"], NPM_PACKAGE);
    }

    #[test]
    fn a_receipt_is_green_only_with_every_leg_and_probe_and_this_heads_medium() {
        let leg = |p: bool| Leg {
            name: "l".into(),
            pass: p,
            ms: 1,
            detail: String::new(),
            serial_tail: String::new(),
        };
        let probe = |p: bool| ProbeRow {
            name: "p".into(),
            pass: p,
            detail: String::new(),
        };
        let c = BTreeMap::new();
        let m = serde_json::json!({});
        let all = vec![probe(true); 5];
        assert_eq!(
            receipt(&c, &m, &[leg(true), leg(true)], &all, true, 0)["green"],
            true
        );
        assert_eq!(
            receipt(&c, &m, &[leg(true), leg(true)], &all, false, 0)["green"],
            false,
            "a medium of another HEAD"
        );
        assert_eq!(
            receipt(&c, &m, &[leg(true)], &[], true, 0)["green"],
            false,
            "an install that never served"
        );
        assert_eq!(
            receipt(
                &c,
                &m,
                &[leg(true), leg(true)],
                &[probe(true), probe(true), probe(true), probe(false)],
                true,
                0
            )["green"],
            false
        );
        assert_eq!(
            receipt(&c, &m, &[], &[], true, 0)["green"],
            false,
            "nothing run is never green"
        );
        assert_eq!(
            receipt_name(&"a".repeat(40)),
            format!("holger-installtest-{}.json", "a".repeat(12))
        );
    }
}