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

nordisk/holger

xtask/src/estate_media.rs

//! **`estate-media`** — Holger's release media for the ONE box (lane L21, 2026-10-18): what
//! Gunnar's kit does with `estate release-image`, in Holger's own words, for Holger's own sites.
//!
//! ```text
//! cargo xtask estate-media [mock] --where upcloud|kvm [--dev|--release] --media-to <dir> [--apply]
//!                          [--secrets <vault file>] [--composed] [--live-update on|off]
//!                          [--intel-trust <policy.ron>]
//! ```
//!
//! The ONE box's kit (private-monetize-ops `cargo xtask media`, `[products.holger.media]`) runs it
//! in this checkout. With `--apply` it:
//!
//! 1. composes the FOUR seedless media through the Build Thing ([`crate::forge_door`]), each
//!    with the metadata seed marker (the box reads its pairing seed off the provider's metadata):
//!    `holger-front` (holger-front-installer-metered), `holger` (holger-installer-metered, main
//!    and twin), `holger-keep` and `holger-front-keep` (the same media composed to rewrite a
//!    standing box in place, the update's fallback). The main and twin media (`holger`,
//!    `holger-keep`) also take the estate's server declaration, `holger_ron` — holger-recipe
//!    `estate_ron::medium`, staged as `.iso-out/estate-holger.ron` — and the korp root it
//!    trusts, `plugin_trust_root` (lane W2): no hand step names either. The estate's own name
//!    is filled in on the box from the installer's feed. `--intel-trust <policy.ron>` (lane P5)
//!    stages the estate's intel trust policy — an update-bundle `TrustPolicy` pinning the
//!    operator's OWN `holger.intel/v1` signers, checked here before anything is composed — as
//!    `intel_trust_policy` on the same two media; the declaration's `intel.trust_policy` names
//!    it on the release, and an airgap box imports signed intel at `POST /-/intel/import`
//!    against it. Without it the media carry the slot empty and an intel import is refused by
//!    name (`holger-import-4`): the trust root is the estate's, never the medium's;
//! 2. uploads each medium into the release bucket, content-addressed and immutable
//!    (`release/<kind>/<sha256>.iso`), and each app medium's LIVE app — korp's archive beside
//!    the composed `.iso` — as `release/<kind>-live/<pin>.live-app.znippy` (`holger-live`,
//!    `holger-front-live`): what the ONE box's update hands the front (`UpdateTake`) so twin,
//!    main and the front take the release into their live slot;
//! 3. writes the receipt `media-holger-<kind>.toml` into `--media-to`: the six objects, korp's
//!    live records as `[live_apps.holger]` / `[live_apps.holger-front]`, and
//!    `[release_notes."<id>"]` for each medium (label, date, the commits it carries).
//!
//! An app medium whose app is unsigned is refused by name: a Holger release without live apps
//! makes every update fall back to the keep media. Without `--apply` the plan is printed and
//! nothing is composed or uploaded. Replaces lane L13's one-off upload script.
//!
//! **DEV is the default kind** (owner 2026-10-19: "you must build NOT --release so DEBUG is on
//! EXACTLY like gunnar"). A DEV medium is composed with `--opt dev-narrate=on`: every box's
//! boot log leaves sealed over udp/51822 to the installer (holger-server's `dev_narrate`,
//! Skidbladnir `nordisk-narrate`), as Gunnar's `--dev-narrate` media do. `--release` composes
//! with `--opt dev-narrate=off` — the sender is not compiled in (`--release` IS
//! `--no-dev-narrate`) — and is the serving estate's last step, applied by an in-place
//! upgrade. The receipt says which kind it is (its header and each release note's label).
//!
//! `--composed` uploads the media ALREADY composed in `.iso-out` (by this verb's forge
//! step or `dwarves buildthing` per variant) instead of composing them again, so the exact
//! bytes a KVM install proved are the bytes that go up (lane R4). Each medium's record must
//! name this checkout's HEAD, or the medium is refused by name.
//!
//! The bucket: `mock` → the UpCloud mock's `/objsto` face on `127.0.0.1` (its API port from
//! `$MONETIZE_OPS_MOCK_PORTS`, the bench the ONE kit runs); the account → the upload pair
//! `RELEASE_MEDIA_*` read BY NAME from the vault file (`--secrets`, else `$HOLGER_RELEASE_SECRETS`,
//! else `$HOME/holger_secrets`), never printed.

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

use anyhow::{bail, Context, Result};
use nornir_build_thing::variant::ArtifactKind;

use crate::forge_door;

/// One release medium: the receipt's word for it, the declared variant that composes it, and
/// whether its app is a LIVE app the boxes take (`<word>-live`).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Medium {
    pub word: &'static str,
    pub variant: &'static str,
    pub live: bool,
}

/// **The release**: the four media monetize's `lifecycle::Holger` names (front, main and twin,
/// and their keep media), the two app media carrying the live apps.
pub const MEDIA: [Medium; 4] = [
    Medium {
        word: "holger-front",
        variant: holger_recipe::packaging::FRONT_INSTALLER_METERED,
        live: true,
    },
    Medium {
        word: "holger",
        variant: holger_recipe::packaging::METERED_INSTALLER,
        live: true,
    },
    Medium {
        word: "holger-keep",
        variant: holger_recipe::packaging::KEEP_MEDIUM,
        live: false,
    },
    Medium {
        word: "holger-front-keep",
        variant: holger_recipe::packaging::FRONT_KEEP_MEDIUM,
        live: false,
    },
];

/// The live app kind of a medium (monetize `update::LIVE_SUFFIX`).
pub fn live_kind(word: &str) -> String {
    format!("{word}-live")
}

/// The receipt's table of korp's live records (pmo `sells::RECEIPT_LIVE_APPS`).
pub const RECEIPT_LIVE_APPS: &str = "live_apps";
/// The receipt's table of what the build IS: its media kind (pmo `sells::RECEIPT_BUILD`).
pub const RECEIPT_BUILD: &str = "build";
/// The receipt's table of what each release IS (pmo `sells::RECEIPT_RELEASE_NOTES`).
pub const RECEIPT_RELEASE_NOTES: &str = "release_notes";

/// The marker every seedless medium is composed with: the box reads its seed off the metadata.
pub const SEED_FROM_METADATA: &str = "nordisk-pairing-seed-from = \"upcloud-metadata\"\n";

/// The mock bench's ports (private-monetize-ops `place::PORTS_ENV`) and its API port's default.
pub const MOCK_PORTS_ENV: &str = "MONETIZE_OPS_MOCK_PORTS";
pub const MOCK_API: u16 = 8791;
/// The vault file of the release bucket's upload pair.
pub const SECRETS_ENV: &str = "HOLGER_RELEASE_SECRETS";
/// The upload pair's names in it (Gunnar's kit reads the same names).
pub const UPLOAD_NAMES: [&str; 4] = [
    "RELEASE_MEDIA_ENDPOINT",
    "RELEASE_MEDIA_BUCKET",
    "RELEASE_MEDIA_ACCESS_KEY_ID",
    "RELEASE_MEDIA_SECRET_ACCESS_KEY",
];
pub const REGION_NAME: &str = "RELEASE_MEDIA_REGION";
pub const DEFAULT_REGION: &str = "europe-3";

/// What the operator (or the ONE kit) asked for.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Opts {
    pub mock: bool,
    pub place: String,
    /// `dev` (the default: narrate ON) or `release` (narrate compiled out).
    pub kind: String,
    pub media_to: PathBuf,
    pub apply: bool,
    pub secrets: Option<PathBuf>,
    /// Upload the media already in `.iso-out` (proved before upload), never compose again.
    pub composed: bool,
    /// **The estate's live-update switch** (`--live-update on|off`, absent: on): composed onto
    /// every medium as `live_app=on|off` ([`live_update_params`]), part of the BASE.
    pub live_update: bool,
    /// **The estate's intel trust policy** (`--intel-trust <policy.ron>`, lane P5): staged as
    /// `intel_trust_policy` for the box media. `None`: the slot stays empty.
    pub intel_trust: Option<PathBuf>,
    /// **The bundles door's trust policy** (`--bundles-trust <policy.ron>`, lane HOL-BUNDLES-DOOR):
    /// staged as `bundles_trust_policy` for the box media. `None`: the slot stays empty, and every
    /// PUT into the estate's `bundles` door is refused (`holger-bundles-1`).
    pub bundles_trust: Option<PathBuf>,
}

/// Pure: **the live-update switch as a medium parameter** — `live_app=on|off` (holger-recipe
/// `LIVE_UPDATE_PARAM`, the cmdline's `korp.live_update`), the pattern's words.
pub fn live_update_params(on: bool) -> Vec<String> {
    vec![format!(
        "{}={}",
        holger_recipe::packaging::LIVE_UPDATE_PARAM,
        if on { "on" } else { "off" }
    )]
}

/// Parse the verb's words; an unknown word is refused, never dropped.
pub fn parse(args: &[String]) -> Result<Opts> {
    let (
        mut mock,
        mut place,
        mut kind,
        mut media_to,
        mut apply,
        mut secrets,
        mut composed,
        mut live_update,
    ) = (false, None, None, None, false, None, false, true);
    let mut intel_trust = None;
    let mut bundles_trust = None;
    let mut it = args.iter();
    while let Some(a) = it.next() {
        let mut val = |flag: &str| {
            it.next()
                .cloned()
                .with_context(|| format!("estate-media: {flag} takes a value"))
        };
        match a.as_str() {
            "mock" => mock = true,
            "--where" => place = Some(val("--where")?),
            "--dev" | "--release" | "--dev-narrate" | "--no-dev-narrate" => {
                // `--dev-narrate` is `--dev`, `--no-dev-narrate` is `--release`: a DEV medium
                // narrates and a release one carries no sender, never the other two mixes.
                let k = if matches!(a.as_str(), "--dev" | "--dev-narrate") { KIND_DEV } else { KIND_RELEASE }.to_string();
                if kind.as_ref().is_some_and(|was| *was != k) {
                    bail!("estate-media: --dev or --release, not both (a DEV medium narrates; --release is --no-dev-narrate)");
                }
                kind = Some(k);
            }
            "--media-to" => media_to = Some(PathBuf::from(val("--media-to")?)),
            "--secrets" => secrets = Some(PathBuf::from(val("--secrets")?)),
            "--apply" => apply = true,
            "--composed" => composed = true,
            "--intel-trust" => intel_trust = Some(PathBuf::from(val("--intel-trust")?)),
            "--bundles-trust" => bundles_trust = Some(PathBuf::from(val("--bundles-trust")?)),
            "--live-update" => {
                live_update = match val("--live-update")?.as_str() {
                    "on" => true,
                    "off" => false,
                    other => bail!("estate-media: --live-update {other}: `on` or `off`"),
                }
            }
            other => bail!("estate-media: unknown word '{other}' (takes [mock] --where <place> [--dev|--release] --media-to <dir> [--apply] [--secrets <file>] [--composed] [--live-update on|off] [--intel-trust <policy.ron>] [--bundles-trust <policy.ron>])"),
        }
    }
    let place = place.context("estate-media: --where <place> (upcloud, kvm)")?;
    if !matches!(place.as_str(), "upcloud" | "kvm") {
        bail!("estate-media: --where {place}: `upcloud` or `kvm`");
    }
    // DEV unless `--release` is said: test estates run DEV media, the serving estate's last
    // step is the release.
    let kind = kind.unwrap_or_else(|| KIND_DEV.to_string());
    let media_to = media_to.context(
        "estate-media: --media-to <dir> (where the ONE kit reads media-holger-<kind>.toml)",
    )?;
    // Already composed media carry whatever their composer staged: a policy named here would
    // never reach them, and the receipt would claim a trust it does not carry.
    if composed && bundles_trust.is_some() {
        bail!("estate-media: --bundles-trust stages a policy for composing; --composed uploads media already composed (compose them with --param bundles_trust_policy=@<policy.ron>)");
    }
    if composed && intel_trust.is_some() {
        bail!("estate-media: --intel-trust stages a policy for composing; --composed uploads media already composed (compose them with --param intel_trust_policy=@<policy.ron>)");
    }
    Ok(Opts {
        mock,
        place,
        kind,
        media_to,
        apply,
        secrets,
        composed,
        live_update,
        intel_trust,
        bundles_trust,
    })
}

/// The DEV kind (the default): composed with the `dev-narrate` option ON.
pub const KIND_DEV: &str = "dev";
/// The RELEASE kind: the `dev-narrate` option OFF, no sender compiled in.
pub const KIND_RELEASE: &str = "release";

/// Pure: **whether a kind narrates** — DEV does, release does not.
pub fn narrates(kind: &str) -> bool {
    kind == KIND_DEV
}

/// Pure: **the forge's `--opt` for a kind** — `dev-narrate=on` for DEV, `=off` for release.
pub fn forge_opts(kind: &str) -> Vec<String> {
    vec![format!(
        "{}={}",
        holger_recipe::packaging::DEV_NARRATE_OPTION,
        if narrates(kind) { "on" } else { "off" }
    )]
}

/// Pure: **the payload row the forge builds for a medium of a kind** — the composer's own
/// resolution (dwarves `assemble_cell::resolve_installer_options`: the settings tree splits the
/// `--opt` line, edda `resolve_options` + `ResolvedOptions::apply_to_set`), read back off the
/// resolved set's cell. A DEV medium arrives at the payload's declared `…-narrate` row and its
/// `dev-media` profile, as Gunnar's dev media do; a release medium builds the payload row.
pub fn payload_row(medium: &str, kind: &str) -> Result<nornir_build_thing::variant::BuildVariant> {
    let set = holger_recipe::declared();
    let cell = set
        .installers
        .iter()
        .find(|i| i.name == medium)
        .with_context(|| format!("no [[build.installer]] cell named '{medium}'"))?;
    let tree = nornir_build_thing::tree::SettingsTree::of_installer(cell, None);
    let toggles = tree
        .split_opts(&forge_opts(kind))
        .map_err(|e| anyhow::anyhow!("installer '{medium}': {e:#}"))?
        .features;
    let resolved =
        nornir_build_thing::options::resolve_options(&cell.options, &cell.params, &toggles)?;
    let mut planned = set.clone();
    resolved.apply_to_set(&cell.options, &mut planned, &cell.variant)?;
    let row = &planned
        .installers
        .iter()
        .find(|i| i.name == medium)
        .context("the resolved set lost its cell")?
        .variant;
    Ok(planned.require(row)?.clone())
}

/// Pure: **what the receipt says the build is** — its header line and the label suffix.
pub fn kind_words(kind: &str) -> (String, &'static str) {
    let n = narrates(kind);
    (
        format!(
            "# build: kind = {kind:?}, dev_narrate = {n} ({})\n",
            if n {
                "every box narrates its boot log over udp/51822: a TEST estate's media"
            } else {
                "no narrate sender compiled in: the serving estate's media"
            }
        ),
        if n { " (DEV: narrate on)" } else { "" },
    )
}

/// **The front's own time sources** — the shared `estate_clock::config::CLOUD_SOURCES`
/// (`front_sources(FrontPlace::Cloud, _)`), the words Gunnar's kit composes its front with
/// (lane G3 moved them out of both kits): every front medium is composed with `clock=` these,
/// the estate's root of time. Without them the front syncs nothing and REFUSES to sign time,
/// so no box (`peer://10.44.0.1:50123`) ever takes the estate's clock (lane R10).
pub const FRONT_CLOCK_SOURCES: &str = estate_clock::config::CLOUD_SOURCES;

/// Pure: **a composed front medium carries the clock** — its command line (in the medium's
/// bytes) names `korp.env.ESTATE_CLOCK=` [`FRONT_CLOCK_SOURCES`]. A box medium is not asked.
pub fn composed_front_has_clock(word: &str, medium: &[u8]) -> Result<()> {
    if !word.contains("front") {
        return Ok(());
    }
    let needle = format!("korp.env.ESTATE_CLOCK={FRONT_CLOCK_SOURCES}");
    if medium.windows(needle.len()).any(|w| w == needle.as_bytes()) {
        return Ok(());
    }
    bail!("[estate-media-front-clock] the composed {word} medium's command line has no `{needle}`: compose it with `--param clock={FRONT_CLOCK_SOURCES}` (without it the front never syncs and no box's clock peer signs)")
}

/// Pure: **a composed medium is this checkout's** — its record (`<variant>.medium.json`)
/// names a `head` that is a prefix of `head` (this checkout's full HEAD).
pub fn composed_is_this_head(word: &str, record: &str, head: &str) -> Result<()> {
    let named = serde_json::from_str::<serde_json::Value>(record)
        .ok()
        .and_then(|v| v["head"].as_str().map(str::to_string))
        .filter(|h| h.len() >= 7);
    match named {
        Some(h) if head.starts_with(&h) => Ok(()),
        Some(h) => bail!("[estate-media-composed-stale] the composed {word} medium names head {h}, this checkout is {head}: compose it again"),
        None => bail!("[estate-media-composed-stale] the composed {word} medium's record names no head"),
    }
}

/// Pure: **a composed medium reads its own CD** (A7 row 25, Gunnar 09fe2c48, owner's order
/// 2026-09-23: "refuse when cd_driver is false"). Its record (`<variant>.medium.json`, dwarves
/// `media_steps::write_record`) says `cd_driver: true` only when the compose proved the kernel's
/// CD block (nornir-build-thing `kernel_requires::refuse_unless_reads_its_cd`). A medium that
/// cannot read the tray it is installed from boots, finds no ISO-9660 volume and parks on
/// FAILED, so `false`, a missing field or an unreadable record refuses the upload by name.
pub fn composed_reads_its_cd(word: &str, record: &str) -> Result<()> {
    let v = serde_json::from_str::<serde_json::Value>(record).with_context(|| {
        format!("REFUSED [medium-no-cd-driver] the {word} medium's record is not JSON")
    })?;
    let block = nornir_build_thing::kernel_requires::CD_BLOCK
        .iter()
        .map(|s| format!("{s}=y"))
        .collect::<Vec<_>>()
        .join(" ");
    match v["cd_driver"].as_bool() {
        Some(true) => Ok(()),
        Some(false) => bail!("REFUSED [medium-no-cd-driver] the {word} medium cannot read its own CD (its record says cd_driver: false): rebuild the donor kernel with the CD block ({block}) and compose again"),
        None => bail!("REFUSED [medium-no-cd-driver] the {word} medium's record has no cd_driver: its CD block was never proven (compose it with a dwarves that runs the CD preflight; the kernel needs {block})"),
    }
}

/// Pure: **the media kind of a kind word** (`dev` | `release`, nornir-build-thing `MediaKind`).
pub fn media_kind(kind: &str) -> Result<nornir_build_thing::media_gate::MediaKind> {
    nornir_build_thing::media_gate::MediaKind::parse(kind)
        .with_context(|| format!("estate-media: kind {kind:?} is `dev` or `release`"))
}

/// **A medium is of the kind this upload says** (lane B4, A9 row 10): read back from the
/// medium's own bytes (the `dev-narrate` option bakes `korp.env.NORDISK_MEDIA_KIND`), so a
/// DEV medium is never uploaded and labelled as the release.
pub fn held_to_kind(word: &str, kind: &str, medium: &[u8]) -> Result<()> {
    nornir_build_thing::media_gate::refuse_kind(word, media_kind(kind)?, medium)
        .with_context(|| format!("[estate-media-kind] the {word} medium"))
}

/// The composed medium of `variant` in `.iso-out`, held to this checkout's HEAD and `kind`.
fn composed_medium(root: &Path, word: &str, variant: &str, kind: &str) -> Result<PathBuf> {
    let iso = forge_door::iso_out(root, variant);
    let bytes = std::fs::metadata(&iso)
        .with_context(|| {
            format!(
                "[estate-media-composed] {} is absent: compose it first",
                iso.display()
            )
        })?
        .len();
    if bytes == 0 {
        bail!("[estate-media-composed] {} is EMPTY", iso.display());
    }
    let record = std::fs::read_to_string(iso.with_extension("medium.json")).with_context(|| {
        format!(
            "[estate-media-composed] {}: the medium record",
            iso.with_extension("medium.json").display()
        )
    })?;
    let o = std::process::Command::new("git")
        .arg("-C")
        .arg(root)
        .args(["rev-parse", "HEAD"])
        .output()
        .context("git rev-parse HEAD")?;
    composed_is_this_head(word, &record, String::from_utf8_lossy(&o.stdout).trim())?;
    composed_reads_its_cd(word, &record)?;
    let bytes = std::fs::read(&iso)
        .with_context(|| format!("[estate-media-composed] {}", iso.display()))?;
    composed_front_has_clock(word, &bytes)?;
    held_to_kind(word, kind, &bytes)?;
    Ok(iso)
}

/// The receipt's file name (pmo `sells::media_receipt("holger-<kind>")`).
pub fn receipt_name(kind: &str) -> String {
    format!("media-holger-{kind}.toml")
}

/// A medium's object key.
pub fn medium_key(word: &str, sha256: &str) -> String {
    format!("release/{word}/{sha256}.iso")
}

/// A live app's object key.
pub fn live_key(word: &str, pin: &str) -> String {
    format!("release/{}/{pin}.live-app.znippy", live_kind(word))
}

/// korp's live record and archive beside a composed `.iso` (`<stem>.live.toml`,
/// `<stem>.live-app.znippy`).
pub fn live_companions(iso: &Path) -> (PathBuf, PathBuf) {
    let dir = iso.parent().map(Path::to_path_buf).unwrap_or_default();
    let stem = iso
        .file_stem()
        .map(|s| s.to_string_lossy().to_string())
        .unwrap_or_default();
    (
        dir.join(format!("{stem}.live.toml")),
        dir.join(format!("{stem}.live-app.znippy")),
    )
}

/// The release declaration korp composed beside a `.iso` and its signature
/// (`<stem>.live-release.bundle`, `<stem>.live-release.sig`; absent: the medium declares none).
pub fn release_companions(iso: &Path) -> (PathBuf, PathBuf) {
    let dir = iso.parent().map(Path::to_path_buf).unwrap_or_default();
    let stem = iso
        .file_stem()
        .map(|s| s.to_string_lossy().to_string())
        .unwrap_or_default();
    (
        dir.join(format!(
            "{stem}.{}",
            korp_installer::live::MEDIUM_RELEASE_SUFFIX
        )),
        dir.join(format!(
            "{stem}.{}",
            korp_installer::live::MEDIUM_RELEASE_SIG_SUFFIX
        )),
    )
}

/// korp's live record of one app medium, its four fields checked — and what the release
/// declares beside its app (lane B5): the release declaration and its signature (hex), the
/// release's own `korp.env.*` tokens.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct LiveRecord {
    pub base: String,
    pub app: String,
    pub app_len: u64,
    pub sig: String,
    pub release: String,
    pub release_sig: String,
    pub env: Vec<String>,
}

/// Pure: **`r` with the release declaration korp composed beside its medium** (lane B5). A
/// declaration without its signature on a signed medium is refused by name: every box would
/// refuse it (`[release-unsigned]`) and fall back to the keep medium.
pub fn with_release(
    word: &str,
    mut r: LiveRecord,
    bundle: Option<Vec<u8>>,
    sig: Option<Vec<u8>>,
) -> Result<LiveRecord> {
    let hex = |b: &[u8]| b.iter().map(|x| format!("{x:02x}")).collect::<String>();
    if let Some(b) = bundle {
        let sig = sig.with_context(|| format!("[estate-media-release-unsigned] the {word} medium's release declaration carries no signature: no box that requires signed media takes it"))?;
        r.release = hex(&b);
        r.release_sig = hex(&sig);
    }
    Ok(r)
}

/// Pure: **korp's live record**, refused by name when a field is missing or the app is unsigned
/// (a box never takes an unsigned app live, so the release would update from keep only).
pub fn live_record(word: &str, rec: &str) -> Result<LiveRecord> {
    let get = |k: &str| {
        rec.lines()
            .find_map(|l| {
                l.split_once('=')
                    .filter(|(key, _)| key.trim() == k)
                    .map(|(_, v)| v.trim().trim_matches('"').to_string())
            })
            .filter(|v| !v.is_empty())
    };
    let hex64 = |k: &str| {
        get(k).filter(|h| h.len() == 64 && h.bytes().all(|b| b.is_ascii_hexdigit())).with_context(|| format!("[estate-media-live-record] the {word} medium's live record names no `{k}` (64 hex)"))
    };
    let (base, app) = (hex64("base")?, hex64("app")?);
    let app_len = get("app_len")
        .and_then(|n| n.parse::<u64>().ok())
        .filter(|n| *n > 0)
        .with_context(|| {
            format!("[estate-media-live-record] the {word} medium's live record names no `app_len`")
        })?;
    let sig = get("sig").with_context(|| format!("[estate-media-unsigned] the {word} medium's app is unsigned (composed without the root seed): no box takes it live, every update would fall back to {word}'s keep medium"))?;
    let env = get("env")
        .map(|e| e.split_whitespace().map(str::to_string).collect())
        .unwrap_or_default();
    Ok(LiveRecord {
        base,
        app,
        app_len,
        sig,
        env,
        ..LiveRecord::default()
    })
}

/// One object in the bucket, as the receipt names it.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Object {
    pub url: String,
    pub sha256: String,
    pub bytes: u64,
}

/// Pure: **the receipt** — every object (`<kind> = { object, sha256, bytes }`), then
/// `[live_apps.<kind>]` for each live app (its `<kind>-live` archive among the objects), then
/// `[release_notes."<id>"]` for each medium's release id.
pub fn receipt_text(
    kind: &str,
    objects: &BTreeMap<String, Object>,
    live: &BTreeMap<String, LiveRecord>,
    label: &str,
    built: &str,
    carries: &BTreeMap<String, String>,
) -> Result<String> {
    for k in live.keys() {
        if !objects.contains_key(&live_kind(k)) {
            bail!(
                "[estate-media-live-record] the {k} live app has no {} object",
                live_kind(k)
            );
        }
    }
    let mut out = format!("# [estate_orders.media] of Holger's own sites: the {kind} media, one per machine kind, and their live apps (holger `cargo xtask estate-media`)\n");
    let (header, suffix) = kind_words(kind);
    out.push_str(&header);
    let label = format!("{label}{suffix}");
    for (k, o) in objects {
        out.push_str(&format!(
            "{k} = {{ object = {:?}, sha256 = {:?}, bytes = {} }}\n",
            o.url, o.sha256, o.bytes
        ));
    }
    // The kind as data (lane B4): what the ONE box kit reads to refuse DEV media for a sold site.
    out.push_str(&format!(
        "\n[{RECEIPT_BUILD}]\nkind = {:?}\ndev_narrate = {}\n",
        media_kind(kind)?.word(),
        narrates(kind)
    ));
    for (k, r) in live {
        out.push_str(&format!(
            "\n[{RECEIPT_LIVE_APPS}.{k}]\nbase = {:?}\napp = {:?}\napp_len = {}\nsig = {:?}\n",
            r.base, r.app, r.app_len, r.sig
        ));
        // Lane B5: what the release declares beside its app (the ONE box's `LiveRecord` reads
        // these as optional).
        if !r.release.is_empty() {
            out.push_str(&format!(
                "release = {:?}\nrelease_sig = {:?}\n",
                r.release, r.release_sig
            ));
        }
        if !r.env.is_empty() {
            out.push_str(&format!(
                "env = [{}]\n",
                r.env
                    .iter()
                    .map(|e| format!("{e:?}"))
                    .collect::<Vec<_>>()
                    .join(", ")
            ));
        }
    }
    let carried: Vec<String> = carries
        .iter()
        .map(|(repo, commit)| format!("{repo:?} = {commit:?}"))
        .collect();
    for m in MEDIA {
        if let Some(o) = objects.get(m.word) {
            out.push_str(&format!("\n[{RECEIPT_RELEASE_NOTES}.{:?}]\nlabel = {label:?}\nbuilt = {built:?}\ncarries = {{ {} }}\n", o.sha256, carried.join(", ")));
        }
    }
    Ok(out)
}

/// The release bucket's upload pair.
#[derive(Clone, PartialEq, Eq)]
pub struct Bucket {
    pub endpoint: String,
    pub region: String,
    pub bucket: String,
    pub access: String,
    pub secret: String,
}

impl std::fmt::Debug for Bucket {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Bucket({} {} {})",
            self.endpoint, self.region, self.bucket
        )
    }
}

/// The mock's bucket on `api_port` (the UpCloud mock's `/objsto` face and its bench keys).
pub fn mock_bucket(api_port: u16) -> Bucket {
    Bucket {
        endpoint: format!("http://127.0.0.1:{api_port}/objsto"),
        region: "mock".into(),
        bucket: "release".into(),
        access: "mock-release-access-key".into(),
        secret: "mock-release-secret-key".into(),
    }
}

/// The mock's API port: `$MONETIZE_OPS_MOCK_PORTS` (api,clock,stripe,control), else the default.
pub fn mock_api_port(env: Option<&str>) -> Result<u16> {
    match env.map(str::trim).filter(|v| !v.is_empty()) {
        None => Ok(MOCK_API),
        Some(t) => t
            .split(',')
            .next()
            .and_then(|p| p.trim().parse::<u16>().ok())
            .filter(|p| *p >= 1024)
            .with_context(|| format!("{MOCK_PORTS_ENV}={t}: four ports api,clock,stripe,control")),
    }
}

/// Pure: **the account's bucket** from the vault file's text, read by NAME.
pub fn bucket_of(vault: &str) -> Result<Bucket> {
    let mut v = BTreeMap::new();
    for l in vault
        .lines()
        .map(str::trim)
        .filter(|l| !l.is_empty() && !l.starts_with('#'))
    {
        if let Some((k, val)) = l.trim_start_matches("export ").split_once('=') {
            v.insert(
                k.trim().to_string(),
                val.trim().trim_matches('"').trim_matches('\'').to_string(),
            );
        }
    }
    let get = |k: &str| {
        v.get(k).filter(|x| !x.is_empty()).cloned().with_context(|| format!("[estate-media-bucket] {k} is not in the vault file: the release bucket's upload pair"))
    };
    let endpoint = get(UPLOAD_NAMES[0])?.trim_end_matches('/').to_string();
    if !endpoint.starts_with("https://") {
        bail!("[estate-media-bucket] {} is not https://", UPLOAD_NAMES[0]);
    }
    Ok(Bucket {
        endpoint,
        region: v
            .get(REGION_NAME)
            .filter(|r| !r.is_empty())
            .cloned()
            .unwrap_or_else(|| DEFAULT_REGION.into()),
        bucket: get(UPLOAD_NAMES[1])?,
        access: get(UPLOAD_NAMES[2])?,
        secret: get(UPLOAD_NAMES[3])?,
    })
}

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

/// sha256 of `bytes`, hex.
pub fn sha256_hex(bytes: &[u8]) -> String {
    use sha2::Digest as _;
    hex(&sha2::Sha256::digest(bytes))
}

fn hmac(key: &[u8], msg: &str) -> Vec<u8> {
    use hmac::{KeyInit as _, Mac as _};
    let mut m = <hmac::Hmac<sha2::Sha256>>::new_from_slice(key).expect("hmac takes any key");
    m.update(msg.as_bytes());
    m.finalize().into_bytes().to_vec()
}

/// Pure: **a SigV4 path-style PUT** — the url and its three headers (`x-amz-date`,
/// `x-amz-content-sha256`, `Authorization`) at `amz_date` (`YYYYMMDDTHHMMSSZ`).
pub fn put_request(
    b: &Bucket,
    key: &str,
    payload_sha256: &str,
    amz_date: &str,
) -> (String, Vec<(String, String)>) {
    let scheme = if b.endpoint.starts_with("http://") {
        "http://"
    } else {
        "https://"
    };
    let bare = b.endpoint.trim_start_matches(scheme).trim_end_matches('/');
    let (host, prefix) = bare
        .split_once('/')
        .map(|(h, p)| (h.to_string(), format!("/{p}")))
        .unwrap_or((bare.to_string(), String::new()));
    let path = format!("{prefix}/{}/{key}", b.bucket);
    let day = &amz_date[..8];
    let canon = format!("PUT\n{path}\n\nhost:{host}\nx-amz-content-sha256:{payload_sha256}\nx-amz-date:{amz_date}\n\nhost;x-amz-content-sha256;x-amz-date\n{payload_sha256}");
    let scope = format!("{day}/{}/s3/aws4_request", b.region);
    let sts = format!(
        "AWS4-HMAC-SHA256\n{amz_date}\n{scope}\n{}",
        sha256_hex(canon.as_bytes())
    );
    let k = hmac(format!("AWS4{}", b.secret).as_bytes(), day);
    let k = hmac(&hmac(&hmac(&k, &b.region), "s3"), "aws4_request");
    let sig = hex(&hmac(&k, &sts));
    let auth = format!("AWS4-HMAC-SHA256 Credential={}/{scope}, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature={sig}", b.access);
    (
        format!("{scheme}{host}{path}"),
        vec![
            ("x-amz-date".into(), amz_date.into()),
            ("x-amz-content-sha256".into(), payload_sha256.into()),
            ("Authorization".into(), auth),
        ],
    )
}

/// Now, `YYYYMMDDTHHMMSSZ` (UTC).
pub fn amz_now() -> String {
    let s = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map(|d| d.as_secs())
        .unwrap_or(0);
    let (y, m, d) = civil((s / 86_400) as i64);
    let r = s % 86_400;
    format!(
        "{y:04}{m:02}{d:02}T{:02}{:02}{:02}Z",
        r / 3600,
        (r % 3600) / 60,
        r % 60
    )
}

/// Days since the epoch → (year, month, day) (Howard Hinnant).
pub fn civil(days: i64) -> (i64, u32, u32) {
    let z = days + 719_468;
    let era = z.div_euclid(146_097);
    let doe = z - era * 146_097;
    let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365;
    let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
    let mp = (5 * doy + 2) / 153;
    let d = (doy - (153 * mp + 2) / 5 + 1) as u32;
    let m = if mp < 10 { mp + 3 } else { mp - 9 } as u32;
    (yoe + era * 400 + i64::from(m <= 2), m, d)
}

/// **PUT one file** into the bucket; answers its url, sha256 and size.
pub fn put_file(b: &Bucket, key: &str, file: &Path) -> Result<Object> {
    let bytes = std::fs::read(file).with_context(|| format!("{}", file.display()))?;
    let sha = sha256_hex(&bytes);
    let (url, headers) = put_request(b, key, &sha, &amz_now());
    let agent: ureq::Agent = ureq::Agent::config_builder()
        .http_status_as_error(false)
        .timeout_global(Some(std::time::Duration::from_secs(1800)))
        .build()
        .into();
    let mut r = agent.put(&url);
    for (k, v) in &headers {
        r = r.header(k.as_str(), v.as_str());
    }
    let st = r
        .send(&bytes[..])
        .map(|res| res.status().as_u16())
        .with_context(|| format!("PUT {url}"))?;
    if !(200..300).contains(&st) {
        bail!("[estate-media-put] PUT {url}: the bucket answered HTTP {st}");
    }
    Ok(Object {
        url,
        sha256: sha,
        bytes: bytes.len() as u64,
    })
}

/// The release's label: `$HOLGER_RELEASE_LABEL`, else `holger <workspace version>`.
pub fn release_label(root: &Path) -> String {
    if let Some(l) = std::env::var("HOLGER_RELEASE_LABEL")
        .ok()
        .map(|l| l.trim().to_string())
        .filter(|l| !l.is_empty())
    {
        return l;
    }
    let text = std::fs::read_to_string(root.join("Cargo.toml")).unwrap_or_default();
    let version = text
        .split("[workspace.package]")
        .nth(1)
        .and_then(|t| {
            t.lines().find_map(|l| {
                l.trim().strip_prefix("version").map(|v| {
                    v.trim_start_matches([' ', '='])
                        .trim()
                        .trim_matches('"')
                        .to_string()
                })
            })
        })
        .unwrap_or_default();
    if version.is_empty() {
        "holger".into()
    } else {
        format!("holger {version}")
    }
}

/// The commits the media compose from: each repository's HEAD (short).
pub fn release_carries(root: &Path) -> BTreeMap<String, String> {
    let mut out = BTreeMap::new();
    for repo in [
        "holger",
        "holger-ui",
        "Skidbladnir",
        "tunnr",
        "edda",
        "dwarves",
    ] {
        let dir = gate_repo_dir(root, repo);
        if let Ok(o) = std::process::Command::new("git")
            .arg("-C")
            .arg(&dir)
            .args(["rev-parse", "--short=8", "HEAD"])
            .output()
        {
            let h = String::from_utf8_lossy(&o.stdout).trim().to_string();
            if o.status.success() && !h.is_empty() {
                out.insert(repo.to_string(), h);
            }
        }
    }
    out
}

/// Today, UTC, `YYYY-MM-DD`.
pub fn utc_date() -> String {
    let a = amz_now();
    format!("{}-{}-{}", &a[..4], &a[4..6], &a[6..8])
}

/// Run the verb.
/// **What every seedless medium is composed from**, written into `.iso-out`: the
/// metadata seed marker, the estate's declaration ([`ESTATE_RON_FILE`], holger-recipe
/// `estate_ron::medium`), the korp root it trusts ([`KORP_ROOT_FILE`]) and, when the operator
/// gives one, the estate's intel trust policy ([`INTEL_TRUST_FILE`]).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Staged {
    pub marker: PathBuf,
    pub ron: PathBuf,
    pub korp_root: PathBuf,
    /// The staged intel trust policy, or `None`: the medium's slot stays empty.
    pub intel_trust: Option<PathBuf>,
    /// The staged bundles-door trust policy, or `None`: the medium's slot stays empty.
    pub bundles_trust: Option<PathBuf>,
}

/// The estate declaration's file in `.iso-out`.
pub const ESTATE_RON_FILE: &str = "estate-holger.ron";
/// The korp root's file in `.iso-out`.
pub const KORP_ROOT_FILE: &str = "korp-root-ca.pem";
/// The intel trust policy's file in `.iso-out`.
pub const INTEL_TRUST_FILE: &str = "estate-intel-trust.ron";
/// The bundles door's trust policy's file in `.iso-out` (lane HOL-BUNDLES-DOOR).
pub const BUNDLES_TRUST_FILE: &str = "estate-bundles-trust.ron";

/// Pure: **an intel trust policy the box can verify against** — it parses as an update-bundle
/// `TrustPolicy` (schema, a coherent `trust_mode`, the expired posture) and does not shut out
/// `holger.intel/v1`. Refused here by name rather than on an airgap box at its first import.
pub fn intel_trust_refusal(text: &str) -> Option<String> {
    let policy = match update_bundle::policy::TrustPolicy::from_ron(text) {
        Ok(p) => p,
        Err(e) => {
            return Some(format!(
                "[estate-media-intel-trust] the intel trust policy does not load: {e}"
            ))
        }
    };
    if !policy.allowed_products.is_empty()
        && !policy
            .allowed_products
            .iter()
            .any(|p| p == holger_intel::INTEL_PRODUCT)
    {
        return Some(format!("[estate-media-intel-trust] the intel trust policy's allowed_products ({}) leave out {}: every intel import would be refused", policy.allowed_products.join(", "), holger_intel::INTEL_PRODUCT));
    }
    None
}

impl Staged {
    pub fn write(dir: &Path) -> Result<Self> {
        std::fs::create_dir_all(dir).with_context(|| format!("{}", dir.display()))?;
        let s = Self {
            marker: dir.join("seed-from-metadata.toml"),
            ron: dir.join(ESTATE_RON_FILE),
            korp_root: dir.join(KORP_ROOT_FILE),
            intel_trust: None,
            bundles_trust: None,
        };
        std::fs::write(&s.marker, SEED_FROM_METADATA)?;
        std::fs::write(&s.ron, holger_recipe::estate_ron::medium())?;
        std::fs::write(&s.korp_root, holger_recipe::estate_ron::KORP_ROOT_PEM)?;
        // A stale policy of an earlier run never rides a medium composed without one.
        let _ = std::fs::remove_file(dir.join(INTEL_TRUST_FILE));
        let _ = std::fs::remove_file(dir.join(BUNDLES_TRUST_FILE));
        Ok(s)
    }

    /// Stage the operator's bundles-door trust policy `policy` beside the declaration (lane
    /// HOL-BUNDLES-DOOR), after it loads as an update-bundle `TrustPolicy`.
    pub fn with_bundles_trust(mut self, policy: &Path) -> Result<Self> {
        let text = std::fs::read_to_string(policy)
            .with_context(|| format!("[estate-media-bundles-trust] {}", policy.display()))?;
        if let Err(e) = update_bundle::policy::TrustPolicy::from_ron(&text) {
            bail!(
                "[estate-media-bundles-trust] the bundles trust policy does not load: {e} ({})",
                policy.display()
            );
        }
        let dir = self
            .ron
            .parent()
            .context("[estate-media-bundles-trust] the staging directory")?;
        let staged = dir.join(BUNDLES_TRUST_FILE);
        std::fs::write(&staged, text).with_context(|| format!("{}", staged.display()))?;
        self.bundles_trust = Some(staged);
        Ok(self)
    }

    /// Stage the operator's intel trust policy `policy` beside the declaration, after
    /// [`intel_trust_refusal`] held it.
    pub fn with_intel_trust(mut self, policy: &Path) -> Result<Self> {
        let text = std::fs::read_to_string(policy)
            .with_context(|| format!("[estate-media-intel-trust] {}", policy.display()))?;
        if let Some(why) = intel_trust_refusal(&text) {
            bail!("{why} ({})", policy.display());
        }
        let dir = self
            .ron
            .parent()
            .context("[estate-media-intel-trust] the staging directory")?;
        let staged = dir.join(INTEL_TRUST_FILE);
        std::fs::write(&staged, text).with_context(|| format!("{}", staged.display()))?;
        self.intel_trust = Some(staged);
        Ok(self)
    }
}

/// Pure: **the forge's parameters for `variant`** — the seed marker always, and the
/// estate's declaration and korp root where the medium DECLARES the parameter (the
/// main and twin media; the front serves no repository). Read off the declaration,
/// so a medium that takes `holger_ron` can never be composed without it (R4).
pub fn forge_params(variant: &str, s: &Staged) -> Vec<String> {
    let declared = |name: &str| {
        holger_recipe::declared()
            .installers
            .iter()
            .any(|i| i.name == variant && i.params.iter().any(|p| p.name == name))
    };
    let mut out = vec![format!("pairing_seed=@{}", s.marker.display())];
    if declared("holger_ron") {
        out.push(format!("holger_ron=@{}", s.ron.display()));
    }
    if declared("plugin_trust_root") {
        out.push(format!("plugin_trust_root=@{}", s.korp_root.display()));
    }
    // The estate's intel signers (lane P5), only when the operator gave them.
    if let Some(policy) = s
        .intel_trust
        .as_ref()
        .filter(|_| declared("intel_trust_policy"))
    {
        out.push(format!("intel_trust_policy=@{}", policy.display()));
    }
    // The bundles door's signers (lane HOL-BUNDLES-DOOR), only when the operator gave them.
    if let Some(policy) = s
        .bundles_trust
        .as_ref()
        .filter(|_| declared("bundles_trust_policy"))
    {
        out.push(format!("bundles_trust_policy=@{}", policy.display()));
    }
    // The front's own time sources (Gunnar's kit, R10): the estate's root of time.
    if declared("clock") {
        out.push(format!("clock={FRONT_CLOCK_SOURCES}"));
    }
    out
}

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 receipt = o.media_to.join(receipt_name(&o.kind));
    println!(
        "estate-media {}--where {} --{}: {} media and {} live apps → {} (--opt {})",
        if o.mock { "mock " } else { "" },
        o.place,
        o.kind,
        MEDIA.len(),
        MEDIA.iter().filter(|m| m.live).count(),
        receipt.display(),
        forge_opts(&o.kind).join(" ")
    );
    for m in MEDIA {
        forge_door::declared_medium(
            m.variant,
            ArtifactKind::Installer,
            "cargo xtask installer-iso",
        )?;
        let row = payload_row(m.variant, &o.kind)?;
        println!(
            "  {:<18} ← {}{} (payload {}, profile {})",
            m.word,
            m.variant,
            if m.live {
                format!(" (+ {})", live_kind(m.word))
            } else {
                String::new()
            },
            row.name,
            row.profile.label()
        );
    }
    if !o.apply {
        println!(
            "  PLAN ONLY — `--apply` composes the media, uploads them and writes the receipt."
        );
        return Ok(());
    }
    // [holger-no-mock-proof] (lane GATE): RELEASE media reach the account's bucket only after
    // a green gate (DEV media are test2's and need none, owner 2026-11-03), for EVERY place (lane B4: `--where kvm` without `mock` uploads to the account's bucket too),
    // and only from clean trees at the commits the gate proved.
    if needs_gate(o) {
        let pins = gate_pins(root);
        if let Some(why) = gate_refusal(&root.join(GATE_RECEIPTS), &commits_of(&pins)) {
            bail!("{why}");
        }
        pins_refusal(
            &pins,
            std::env::var(forge_door::DWARVES_BIN_ENV).ok().as_deref(),
            o.composed,
        )?;
    }
    // A pointer is never composed nor uploaded: composed here or already (`--composed`).
    refuse_lfs_pointers(root)?;
    // The front medium carries holger's console, or nothing is composed (stinky SB #48),
    // and it is the console of the holger-ui the media pin (field test F11).
    refuse_front_without_console(
        &front_console_dist(
            root,
            std::env::var("HOLGER_FRONT_CONSOLE_DIST").ok().as_deref(),
        ),
        holger_ui_head(root).as_deref(),
    )?;
    let bucket = if o.mock {
        mock_bucket(mock_api_port(
            std::env::var(MOCK_PORTS_ENV).ok().as_deref(),
        )?)
    } else {
        let path = o.secrets.clone().or_else(|| std::env::var_os(SECRETS_ENV).filter(|v| !v.is_empty()).map(PathBuf::from)).or_else(|| std::env::var_os("HOME").map(|h| PathBuf::from(h).join("holger_secrets"))).context("[estate-media-bucket] name the vault file: --secrets <file> or $HOLGER_RELEASE_SECRETS")?;
        bucket_of(
            &std::fs::read_to_string(&path)
                .with_context(|| format!("[estate-media-bucket] {}", path.display()))?,
        )?
    };
    // The metadata seed marker every seedless medium is composed with, and the estate's
    // server declaration (lane W2): every estate's main and twin boot the same one.
    let staged = Staged::write(&root.join(".iso-out"))?;
    let staged = match &o.intel_trust {
        Some(policy) => staged.with_intel_trust(policy)?,
        None => {
            println!("  note intel trust      none given: the box media carry the slot empty, an intel import is refused (holger-import-4) until a medium carries the estate's policy (--intel-trust)");
            staged
        }
    };
    let staged = match &o.bundles_trust {
        Some(policy) => staged.with_bundles_trust(policy)?,
        None => {
            println!("  note bundles trust    none given: the box media carry the slot empty, a PUT into the bundles door is refused (holger-bundles-1) until a medium carries its policy (--bundles-trust)");
            staged
        }
    };
    let mut objects = BTreeMap::new();
    let mut live = BTreeMap::new();
    for m in MEDIA {
        let suite = forge_door::declared_medium(
            m.variant,
            ArtifactKind::Installer,
            "cargo xtask installer-iso",
        )?;
        let iso = if o.composed {
            println!("  note {:<18} the composed medium's narrate kind is its composer's: a {} medium is composed with --opt {}", m.word, o.kind, forge_opts(&o.kind).join(" "));
            composed_medium(root, m.word, m.variant, &o.kind)?
        } else {
            let mut params = forge_params(m.variant, &staged);
            params.extend(live_update_params(o.live_update));
            // The kernel is built IN THE CHAIN (`--build kernel`, as the ONE box kit's medium does):
            // a medium's kernel requirements reach the kernel only through its consumer's chained
            // build, and a standalone kernel is refused at compose (lane GATE, 2026-10-21).
            let ask = forge_door::Ask {
                variant: Some(m.variant.to_string()),
                params,
                opts: forge_opts(&o.kind),
                builds: vec!["kernel".to_string()],
                ..Default::default()
            };
            let iso = forge_door::run("estate-media", root, &suite, m.variant, &ask)?;
            // The forge refuses a kernel without the CD block itself (dwarves preflight); its
            // record is read back too, so a forge from before that preflight cannot slip one by.
            let rec = iso.with_extension("medium.json");
            composed_reads_its_cd(
                m.word,
                &std::fs::read_to_string(&rec).with_context(|| {
                    format!("[medium-no-cd-driver] {}: the medium record", rec.display())
                })?,
            )?;
            held_to_kind(
                m.word,
                &o.kind,
                &std::fs::read(&iso).with_context(|| format!("{}", iso.display()))?,
            )?;
            iso
        };
        // Read the live record BEFORE any upload: an unsigned app medium is refused whole.
        let rec = if m.live {
            let (rec_path, app_path) = live_companions(&iso);
            let text = std::fs::read_to_string(&rec_path).with_context(|| {
                format!(
                    "[estate-media-live-record] {}: korp's composer writes it beside the medium",
                    rec_path.display()
                )
            })?;
            let (bundle, sig) = release_companions(&iso);
            Some((
                with_release(
                    m.word,
                    live_record(m.word, &text)?,
                    std::fs::read(&bundle).ok(),
                    std::fs::read(&sig).ok(),
                )?,
                app_path,
            ))
        } else {
            None
        };
        let sha = sha256_hex(&std::fs::read(&iso)?);
        let o = put_file(&bucket, &medium_key(m.word, &sha), &iso)?;
        println!("  ok {:<18} → {} ({} bytes)", m.word, o.url, o.bytes);
        objects.insert(m.word.to_string(), o);
        if let Some((r, app_path)) = rec {
            let o = put_file(&bucket, &live_key(m.word, &r.app), &app_path)?;
            if o.bytes != r.app_len {
                bail!(
                    "[estate-media-live-record] the {} archive is {} bytes, its record says {}",
                    live_kind(m.word),
                    o.bytes,
                    r.app_len
                );
            }
            println!(
                "  ok {:<18} → {} (pin {})",
                live_kind(m.word),
                o.url,
                &r.app[..12]
            );
            objects.insert(live_kind(m.word), o);
            live.insert(m.word.to_string(), r);
        }
    }
    let text = receipt_text(
        &o.kind,
        &objects,
        &live,
        &release_label(root),
        &utc_date(),
        &release_carries(root),
    )?;
    std::fs::create_dir_all(&o.media_to).with_context(|| format!("{}", o.media_to.display()))?;
    std::fs::write(&receipt, text).with_context(|| format!("{}", receipt.display()))?;
    println!(
        "✅ estate-media: {} names {} objects and {} live apps",
        receipt.display(),
        objects.len(),
        live.len()
    );
    Ok(())
}

/// **Where the end-to-end gate's receipts are** (lane GATE, 2026-10-20): the ONE box kit's
/// `cargo xtask gate holger --where kvm --box test --apply` (private-monetize-ops) writes
/// `holger-gate-<holger>-<edda>-<Skidbladnir>.json` here after a 3-box mock lay through the
/// real ONE box path. `estate-media --where upcloud --apply` refuses without a GREEN one for
/// exactly this checkout's commits — Gunnar's `[install-no-mock-proof]`, Holger's words.
pub const GATE_RECEIPTS: &str = ".mock-receipts";

/// The repositories whose HEADs a gate receipt names (the media are composed from them):
/// holger, edda and Skidbladnir, and (lane B4, A9 row 11) tunnr — the kernel, korp-installer
/// and korp-start — and dwarves, the composer.
pub const GATE_REPOS: [&str; 5] = ["holger", "edda", "Skidbladnir", "tunnr", "dwarves"];

/// Pure: **an upload that needs the gate** — a RELEASE upload to every bucket but the mock's
/// (lane B4: an account upload with `--where kvm` skipped it). DEV media are test2's (the
/// test2.* sites run the dev kind) and need no gate (owner 2026-11-03: "I PAY for
/// test2.monetize.ignalina.dk FOR TEST. The rule is meant for prod monetize.ignalina.dk.").
pub fn needs_gate(o: &Opts) -> bool {
    // Fail-closed: every kind but DEV (release, or any other) needs the gate.
    !o.mock && o.kind != KIND_DEV
}

/// Where each of [`GATE_REPOS`] is checked out: holger here, dwarves at `$DWARVES_ROOT`
/// (the forge's own, [`forge_door`]) else beside it, the others beside it.
pub fn gate_repo_dir(root: &Path, repo: &str) -> PathBuf {
    let beside = || root.parent().map(|p| p.join(repo)).unwrap_or_default();
    match repo {
        "holger" => root.to_path_buf(),
        "dwarves" => std::env::var_os(forge_door::DWARVES_ROOT_ENV)
            .filter(|v| !v.is_empty())
            .map(PathBuf::from)
            .unwrap_or_else(beside),
        _ => beside(),
    }
}

/// **The pin of each of [`GATE_REPOS`]**: its full HEAD and its dirty paths, untracked files
/// included (nornir-build-thing `media_gate::pin_of`).
pub fn gate_pins(root: &Path) -> Vec<nornir_build_thing::media_gate::RepoPin> {
    GATE_REPOS
        .iter()
        .map(|r| nornir_build_thing::media_gate::pin_of(r, &gate_repo_dir(root, r)))
        .collect()
}

/// Pure: the commits of `pins`, by repository.
pub fn commits_of(pins: &[nornir_build_thing::media_gate::RepoPin]) -> BTreeMap<String, String> {
    pins.iter()
        .map(|p| (p.repo.clone(), p.head.clone()))
        .collect()
}

/// Pure: **the trees an account upload is composed from are pinned** — clean checkouts,
/// untracked files included (`[media-dirty-tree]`, `[media-not-a-checkout]`), and a forge
/// binary named by `$DWARVES_BIN` is refused when this upload composes: its commit is not the
/// dwarves checkout the gate pinned (`[estate-media-dwarves-unpinned]`).
pub fn pins_refusal(
    pins: &[nornir_build_thing::media_gate::RepoPin],
    dwarves_bin: Option<&str>,
    composed: bool,
) -> Result<()> {
    nornir_build_thing::media_gate::refuse_unpinned(pins)?;
    if let Some(bin) = dwarves_bin.filter(|b| !b.trim().is_empty() && !composed) {
        bail!("[estate-media-dwarves-unpinned] ${} = {bin}: an account upload composes with the dwarves checkout the gate pinned ({}), never a prebuilt forge — unset it, or upload the gate's own media with --composed", forge_door::DWARVES_BIN_ENV, forge_door::DWARVES_ROOT_ENV);
    }
    Ok(())
}

/// Pure: the receipt file a gate of these commits writes.
pub fn gate_receipt_name(commits: &BTreeMap<String, String>) -> String {
    let short = |r: &str| {
        commits
            .get(r)
            .map(|h| h.chars().take(12).collect::<String>())
            .unwrap_or_default()
    };
    format!(
        "holger-gate-{}-{}-{}.json",
        short("holger"),
        short("edda"),
        short("Skidbladnir")
    )
}

/// **`[holger-no-mock-proof]`**, or `None` when `dir` holds a GREEN gate receipt naming exactly
/// `commits` (`"green": true`, `"clean": true`, `"commits"` of every [`GATE_REPOS`] = these
/// full HEADs).
pub fn gate_refusal(dir: &Path, commits: &BTreeMap<String, String>) -> Option<String> {
    let refuse = |w: String| {
        Some(format!("REFUSED [holger-no-mock-proof] {w} — run the ONE box kit's end-to-end gate on these commits first (private-monetize-ops: `cargo xtask gate holger --where kvm --box test --apply`)"))
    };
    if let Some(r) = GATE_REPOS
        .iter()
        .find(|r| commits.get(**r).is_none_or(|h| h.len() < 40))
    {
        return refuse(format!(
            "{r} is not a checkout here, so no gate can name its commit"
        ));
    }
    let file = dir.join(gate_receipt_name(commits));
    let Ok(text) = std::fs::read_to_string(&file) else {
        return refuse(format!(
            "no gate receipt {} for holger {}, edda {}, Skidbladnir {}",
            file.display(),
            &commits["holger"][..12],
            &commits["edda"][..12],
            &commits["Skidbladnir"][..12]
        ));
    };
    let Ok(v) = serde_json::from_str::<serde_json::Value>(&text) else {
        return refuse(format!("{} is not a gate receipt (JSON)", file.display()));
    };
    if let Some(r) = GATE_REPOS
        .iter()
        .find(|r| v["commits"][**r].as_str() != commits.get(**r).map(String::as_str))
    {
        return refuse(format!(
            "{} names another {r} commit than this checkout's",
            file.display()
        ));
    }
    if v["clean"].as_bool() != Some(true) {
        return refuse(format!(
            "{} does not say the gate ran over clean trees (`\"clean\": true`)",
            file.display()
        ));
    }
    if v["green"].as_bool() != Some(true) {
        let red: Vec<String> = v["checks"]
            .as_array()
            .map(|a| {
                a.iter()
                    .filter(|c| c["pass"].as_bool() != Some(true))
                    .filter_map(|c| c["name"].as_str().map(str::to_string))
                    .collect()
            })
            .unwrap_or_default();
        return refuse(format!(
            "the gate of these commits is RED ({}): {}",
            file.display(),
            if red.is_empty() {
                "not green".into()
            } else {
                red.join(", ")
            }
        ));
    }
    None
}

/// Where the front's build reads holger's built console from — the same rule as
/// `holger-estate-front/build.rs` under `metered` (every front row is metered):
/// `HOLGER_FRONT_CONSOLE_DIST`, else the declared sibling `../holger-ui/dist-metered`
/// beside this checkout, holger-ui's PAID bundle (A28 M13; its open `dist/` carries no
/// sign-up, welcome or order page).
pub fn front_console_dist(root: &Path, env: Option<&str>) -> PathBuf {
    match env.map(str::trim).filter(|v| !v.is_empty()) {
        Some(v) => PathBuf::from(v),
        None => root.parent().unwrap_or(root).join("holger-ui/dist-metered"),
    }
}

/// **A front medium needs holger's console** (stinky SB #48 — the ONE-box kit's
/// no-console rule, in Holger's media door). `holger-estate-front`'s build only
/// WARNS when holger-ui's `dist/` is absent (its tests build without it) and the
/// front it ships then serves the console's API and `/login` but no console. So
/// every media run refuses first, by name, unless `dist` holds a built console: a
/// non-empty `index.html` that is not a Git LFS pointer, and the `.wasm` bundle
/// beside it. Build it with holger-ui's `cargo xtask build --variant mvp-ui-web-metered` (trunk).
///
/// **And it is the console of the pinned holger-ui** (lane hol-312fix, field test F11: 3.1.0
/// and 3.1.1 shipped the 3.0.0 console, a build left from an older run). holger-ui's trunk
/// rows write `BUILT_FROM`, the commit they built, beside `index.html`. A console with no
/// `BUILT_FROM`, or one that names another commit than `ui_head` (the HEAD of the holger-ui
/// checkout beside this one, the commit the media pin), is refused
/// (`[estate-media-front-stale-console]`). With no holger-ui checkout to compare with, the
/// console must still name its commit.
pub fn refuse_front_without_console(dist: &Path, ui_head: Option<&str>) -> Result<()> {
    let index = dist.join("index.html");
    let fix = "build holger-ui's metered console first (`cargo xtask build --variant mvp-ui-web-metered` in holger-ui, trunk --release) or name a built one in HOLGER_FRONT_CONSOLE_DIST";
    let html = std::fs::read(&index).map_err(|e| anyhow::anyhow!("REFUSED [estate-media-front-no-console] the front medium would carry no holger-ui console: {} ({e}); {fix}", index.display()))?;
    if html.iter().all(u8::is_ascii_whitespace) {
        bail!("REFUSED [estate-media-front-no-console] {} is EMPTY: the front medium would carry no console; {fix}", index.display());
    }
    if html.starts_with(b"version https://git-lfs") {
        bail!("REFUSED [estate-media-front-no-console] {} is a Git LFS pointer, not the console; {fix}", index.display());
    }
    let wasm = std::fs::read_dir(dist)
        .map(|rd| {
            rd.flatten()
                .any(|e| e.path().extension().is_some_and(|x| x == "wasm"))
        })
        .unwrap_or(false);
    if !wasm {
        bail!("REFUSED [estate-media-front-no-console] {} holds an index.html and no .wasm bundle: a half-built console; {fix}", dist.display());
    }
    let stamp = dist.join(CONSOLE_BUILT_FROM);
    let built = std::fs::read_to_string(&stamp)
        .ok()
        .map(|s| s.trim().to_string())
        .filter(|s| !s.is_empty());
    let rebuild = "rebuild it from the pinned holger-ui (`cargo xtask build --variant mvp-ui-web-metered` in that checkout writes BUILT_FROM)";
    match (built, ui_head) {
        (None, _) => bail!("REFUSED [estate-media-front-stale-console] the console in {} names no holger-ui commit it was built from (no {CONSOLE_BUILT_FROM}): an old build; {rebuild}", dist.display()),
        (Some(b), Some(h)) if b != h => bail!("REFUSED [estate-media-front-stale-console] the console in {} was built from holger-ui {b}, and the media pin holger-ui {h}; {rebuild}", dist.display()),
        _ => Ok(()),
    }
}

/// The stamp holger-ui's console builds write beside `index.html`: the full holger-ui commit
/// the bundle was built from (holger-ui `xtask` `BUILT_FROM`).
pub const CONSOLE_BUILT_FROM: &str = "BUILT_FROM";

/// The full HEAD commit of the holger-ui checkout the media compose from, if there is one.
pub fn holger_ui_head(root: &Path) -> Option<String> {
    let o = std::process::Command::new("git")
        .arg("-C")
        .arg(gate_repo_dir(root, "holger-ui"))
        .args(["rev-parse", "HEAD"])
        .output()
        .ok()?;
    let h = String::from_utf8_lossy(&o.stdout).trim().to_string();
    (o.status.success() && h.len() == 40).then_some(h)
}

/// **Every Git LFS pointer in what a medium packs, refused by name** (lane D13, 2026-10-20:
/// test3.holger.rs served holger.webp as a 131-byte pointer), over this checkout and every
/// sibling a medium's row mounts. The walk is edda's `nornir_build_thing::lfs` since lane G10
/// (so Gunnar's media refuse it too: dwarves' compose calls it for every installer cell);
/// this door keeps calling it, so an upload of `--composed` media is held as well.
pub fn refuse_lfs_pointers(root: &Path) -> Result<()> {
    let mut roots = vec![root.to_path_buf()];
    if let Some(up) = root.parent() {
        roots.extend(
            holger_recipe::front_siblings()
                .iter()
                .map(|s| up.join(s))
                .filter(|p| p.is_dir()),
        );
    }
    nornir_build_thing::lfs::refuse_lfs_pointers(&roots, &[]).context("[estate-media-lfs-pointer]")
}

#[cfg(test)]
mod tests {
    /// ★ **A front medium without holger's console is refused by name** (stinky SB
    /// #48, the ONE-box kit's no-console rule): the front's build only WARNS when
    /// holger-ui's built `dist/` is absent and ships a front that serves no console,
    /// so the media door refuses first — no `index.html`, an empty one, an LFS
    /// pointer, or no `.wasm` bundle beside it.
    #[test]
    fn a_front_medium_without_the_console_is_refused() {
        let t = std::env::temp_dir().join(format!(
            "sb48-console-{}-{}",
            std::process::id(),
            std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .unwrap()
                .as_nanos()
        ));
        let (root, dist) = (t.join("holger"), t.join("holger-ui/dist-metered"));
        std::fs::create_dir_all(&root).unwrap();
        assert_eq!(super::front_console_dist(&root, None), dist, "the declared sibling, as the front's build.rs reads it under `metered`: the PAID console (A28 M13), never the open `dist/`");
        assert_eq!(
            super::front_console_dist(&root, Some("/x/dist")),
            std::path::PathBuf::from("/x/dist"),
            "HOLGER_FRONT_CONSOLE_DIST wins"
        );
        let e = super::refuse_front_without_console(&dist, Some(UI_HEAD))
            .unwrap_err()
            .to_string();
        assert!(
            e.contains("[estate-media-front-no-console]") && e.contains("holger-ui"),
            "{e}"
        );
        std::fs::create_dir_all(&dist).unwrap();
        std::fs::write(dist.join("index.html"), "").unwrap();
        assert!(
            super::refuse_front_without_console(&dist, Some(UI_HEAD)).is_err(),
            "an empty index.html is no console"
        );
        std::fs::write(
            dist.join("index.html"),
            "<!doctype html><title>holger</title>",
        )
        .unwrap();
        let e = super::refuse_front_without_console(&dist, Some(UI_HEAD))
            .unwrap_err()
            .to_string();
        assert!(e.contains(".wasm"), "{e}");
        std::fs::write(dist.join("holger-ui-web-1_bg.wasm"), b"\0asm\x01\0\0\0").unwrap();
        std::fs::write(dist.join("BUILT_FROM"), format!("{UI_HEAD}\n")).unwrap();
        super::refuse_front_without_console(&dist, Some(UI_HEAD)).expect("a built console");
        std::fs::write(
            dist.join("index.html"),
            "version https://git-lfs.github.com/spec/v1\noid sha256:00\nsize 1\n",
        )
        .unwrap();
        assert!(
            super::refuse_front_without_console(&dist, Some(UI_HEAD)).is_err(),
            "an LFS pointer is no console"
        );
        let _ = std::fs::remove_dir_all(&t);
    }

    /// The holger-ui commit the media of these tests pin.
    const UI_HEAD: &str = "da876d0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";

    /// ★ **A front medium carries the console built from the pinned holger-ui commit**
    /// (lane hol-312fix, field test F11: the shipped console was the 3.0.0 bundle in 3.1.0 and
    /// 3.1.1, a build left over from an older run). holger-ui's trunk rows write
    /// `BUILT_FROM` (the commit they built) beside `index.html`; the media door refuses a
    /// console with none, or one built from another commit than the holger-ui the media pin.
    #[test]
    fn a_front_console_not_built_from_the_pinned_holger_ui_is_refused() {
        let dist = std::env::temp_dir().join(format!(
            "f11-console-{}-{}",
            std::process::id(),
            std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .unwrap()
                .as_nanos()
        ));
        std::fs::create_dir_all(&dist).unwrap();
        std::fs::write(
            dist.join("index.html"),
            "<!doctype html><title>holger</title>",
        )
        .unwrap();
        std::fs::write(dist.join("holger-ui-web-1_bg.wasm"), b"\0asm\x01\0\0\0").unwrap();
        // No stamp: an old build, of no known commit.
        let e = super::refuse_front_without_console(&dist, Some(UI_HEAD))
            .unwrap_err()
            .to_string();
        assert!(
            e.contains("[estate-media-front-stale-console]") && e.contains("BUILT_FROM"),
            "{e}"
        );
        // Built from another commit than the pinned holger-ui.
        let old = "2f41276781c2aaaaaaaaaaaaaaaaaaaaaaaaaaaa";
        std::fs::write(dist.join("BUILT_FROM"), format!("{old}\n")).unwrap();
        let e = super::refuse_front_without_console(&dist, Some(UI_HEAD))
            .unwrap_err()
            .to_string();
        assert!(
            e.contains("[estate-media-front-stale-console]")
                && e.contains(old)
                && e.contains(UI_HEAD),
            "{e}"
        );
        // Built from the pinned commit: carried.
        std::fs::write(dist.join("BUILT_FROM"), format!("{UI_HEAD}\n")).unwrap();
        super::refuse_front_without_console(&dist, Some(UI_HEAD)).expect("the pinned console");
        // A console named in HOLGER_FRONT_CONSOLE_DIST with no holger-ui checkout to pin it
        // still names a commit.
        super::refuse_front_without_console(&dist, None).expect("a stamped console");
        std::fs::remove_file(dist.join("BUILT_FROM")).unwrap();
        assert!(super::refuse_front_without_console(&dist, None).is_err());
        let _ = std::fs::remove_dir_all(&dist);
    }

    /// The release directory is korp's (lane B1): korp-start lays the medium's release
    /// declaration there, and the shipped declaration names its korp root under it.
    #[test]
    fn the_release_root_is_korp_starts() {
        assert_eq!(
            holger_recipe::estate_ron::RELEASE_ROOT,
            korp_installer::release::RUN_DIR
        );
    }

    /// ★ Owner 2026-11-03: "I PAY for test2.monetize.ignalina.dk FOR TEST. The rule is meant
    /// for prod." DEV media are test2's (test2.* sites run the dev kind); only RELEASE media,
    /// holger.rs's, need the gate.
    #[test]
    fn a_test2_dev_upload_needs_no_mock_proof() {
        let o = |words: &str| {
            parse(
                &words
                    .split_whitespace()
                    .map(str::to_string)
                    .collect::<Vec<_>>(),
            )
            .unwrap()
        };
        assert!(!needs_gate(&o("--where upcloud --media-to /r --apply")));
        assert!(!needs_gate(&o(
            "--where upcloud --dev --media-to /r --apply"
        )));
        assert!(needs_gate(&o(
            "--where upcloud --release --media-to /r --apply"
        )));
        let mut other = o("--where upcloud --media-to /r --apply");
        other.kind = "nightly".into();
        assert!(
            needs_gate(&other),
            "fail-closed: a kind that is not DEV needs the gate"
        );
    }

    /// ★ **Every RELEASE upload but the mock's needs the gate** (lane B4, A9 row 11): `--where kvm`
    /// without `mock` uploads to the account's bucket, and skipped the gate.
    #[test]
    fn an_upload_to_any_bucket_but_the_mocks_needs_the_gate() {
        let o = |words: &str| {
            parse(
                &words
                    .split_whitespace()
                    .map(str::to_string)
                    .collect::<Vec<_>>(),
            )
            .unwrap()
        };
        assert!(
            needs_gate(&o("--where kvm --release --media-to /r --apply")),
            "kvm without mock is the account's bucket"
        );
        assert!(needs_gate(&o(
            "--where upcloud --release --media-to /r --apply"
        )));
        assert!(
            !needs_gate(&o("mock --where kvm --release --media-to /r --apply")),
            "the mock's bench bucket is the gate's own"
        );
        let root = std::env::temp_dir().join(format!("b4-kvm-gate-{}", std::process::id()));
        let _ = std::fs::remove_dir_all(&root);
        std::fs::create_dir_all(root.join("holger")).unwrap();
        let mut kvm = o("--where kvm --release --media-to /r --apply");
        kvm.secrets = Some(root.join("no-vault"));
        let e = format!("{:#}", run_with(&kvm, &root.join("holger")).unwrap_err());
        assert!(e.contains("[holger-no-mock-proof]"), "{e}");
        let _ = std::fs::remove_dir_all(&root);
    }

    /// ★ **The gate receipt pins tunnr and dwarves, and a clean run** (lane B4): a receipt that
    /// names another tunnr or dwarves, or does not say its trees were clean, is refused.
    #[test]
    fn the_gate_receipt_pins_tunnr_and_dwarves_and_clean_trees() {
        assert!(
            GATE_REPOS.contains(&"tunnr") && GATE_REPOS.contains(&"dwarves"),
            "{GATE_REPOS:?}"
        );
        let dir = std::env::temp_dir().join(format!("b4-gate-pins-{}", std::process::id()));
        let _ = std::fs::remove_dir_all(&dir);
        std::fs::create_dir_all(&dir).unwrap();
        let c: BTreeMap<String, String> = GATE_REPOS
            .iter()
            .enumerate()
            .map(|(i, r)| {
                (
                    r.to_string(),
                    char::from(b'a' + i as u8).to_string().repeat(40),
                )
            })
            .collect();
        let write = |tunnr: &str, clean: Option<bool>| {
            let mut commits = serde_json::to_value(&c).unwrap();
            commits["tunnr"] = serde_json::json!(tunnr);
            let mut v = serde_json::json!({"green": true, "commits": commits, "checks": []});
            if let Some(clean) = clean {
                v["clean"] = serde_json::json!(clean);
            }
            std::fs::write(dir.join(gate_receipt_name(&c)), v.to_string()).unwrap();
        };
        write(&"9".repeat(40), Some(true));
        assert!(gate_refusal(&dir, &c)
            .unwrap()
            .contains("another tunnr commit"));
        write(&c["tunnr"], None);
        assert!(
            gate_refusal(&dir, &c).unwrap().contains("clean trees"),
            "a receipt that does not say its trees were clean"
        );
        write(&c["tunnr"], Some(false));
        assert!(gate_refusal(&dir, &c).unwrap().contains("clean trees"));
        write(&c["tunnr"], Some(true));
        assert_eq!(gate_refusal(&dir, &c), None);
        let mut no_dwarves = c.clone();
        no_dwarves.insert("dwarves".into(), String::new());
        assert!(gate_refusal(&dir, &no_dwarves)
            .unwrap()
            .contains("dwarves is not a checkout"));
        let _ = std::fs::remove_dir_all(&dir);
    }

    /// ★ **An account upload is composed from clean pinned trees** (lane B4): a dirty tree is
    /// refused by name, and so is a prebuilt forge when this upload composes.
    #[test]
    fn a_dirty_tree_or_an_unpinned_forge_is_refused_before_an_account_upload() {
        use nornir_build_thing::media_gate::RepoPin;
        let pin = |repo: &str, dirty: &[&str]| RepoPin {
            repo: repo.into(),
            head: "a".repeat(40),
            dirty: dirty.iter().map(|d| d.to_string()).collect(),
        };
        let clean: Vec<RepoPin> = GATE_REPOS.iter().map(|r| pin(r, &[])).collect();
        assert!(pins_refusal(&clean, None, false).is_ok());
        let mut dirty = clean.clone();
        dirty[3] = pin("tunnr", &["crates/korp-installer/src/new.rs"]);
        let e = format!("{:#}", pins_refusal(&dirty, None, false).unwrap_err());
        assert!(
            e.contains("[media-dirty-tree]")
                && e.contains("tunnr (crates/korp-installer/src/new.rs)"),
            "{e}"
        );
        let e = format!(
            "{:#}",
            pins_refusal(&clean, Some("/opt/dwarves"), false).unwrap_err()
        );
        assert!(e.contains("[estate-media-dwarves-unpinned]"), "{e}");
        assert!(
            pins_refusal(&clean, Some("/opt/dwarves"), true).is_ok(),
            "--composed runs no forge"
        );
    }

    /// ★ **The kind is in the medium and in the receipt** (lane B4, A9 row 10): the DEV option
    /// bakes it onto every medium, a medium of another kind is refused by name, and the
    /// receipt says it as data.
    #[test]
    fn the_kind_is_recorded_in_the_medium_and_the_receipt() {
        let opt = holger_recipe::packaging::dev_narrate_option();
        assert_eq!(
            opt.bakes,
            Some(nornir_build_thing::media_gate::kind_bake()),
            "the dev-narrate option bakes the kind"
        );
        for m in MEDIA {
            let cell = holger_recipe::declared()
                .installers
                .into_iter()
                .find(|i| i.name == m.variant)
                .unwrap();
            assert!(
                cell.options.iter().any(|o| o.bakes == opt.bakes),
                "{} records its kind",
                m.word
            );
        }
        let dev = b"console=ttyS0 korp.env.NORDISK_MEDIA_KIND=dev korp.live_update=on";
        held_to_kind("holger", KIND_DEV, dev).unwrap();
        let e = format!(
            "{:#}",
            held_to_kind("holger", KIND_RELEASE, dev).unwrap_err()
        );
        assert!(
            e.contains("[media-kind-mismatch]") && e.contains("holger"),
            "{e}"
        );
        assert!(format!(
            "{:#}",
            held_to_kind("holger", KIND_RELEASE, b"console=ttyS0").unwrap_err()
        )
        .contains("[media-kind-unrecorded]"));
        let obj = Object {
            url: "https://bucket.example/release/holger/1".into(),
            sha256: "1".repeat(64),
            bytes: 9,
        };
        let objects = BTreeMap::from([("holger".to_string(), obj)]);
        for (kind, narrated) in [(KIND_DEV, true), (KIND_RELEASE, false)] {
            let t: toml::Table = toml::from_str(
                &receipt_text(
                    kind,
                    &objects,
                    &BTreeMap::new(),
                    "holger",
                    "2026-10-21",
                    &BTreeMap::new(),
                )
                .unwrap(),
            )
            .unwrap();
            assert_eq!(
                (
                    t[RECEIPT_BUILD]["kind"].as_str(),
                    t[RECEIPT_BUILD]["dev_narrate"].as_bool()
                ),
                (Some(kind), Some(narrated))
            );
        }
    }

    /// ★ `[holger-no-mock-proof]` (lane GATE): no receipt, a red one, or one of other commits
    /// refuses by name; a green one of exactly these commits lets the account's bucket be used.
    #[test]
    fn the_account_upload_needs_a_green_gate_receipt_of_these_commits() {
        let dir = std::env::temp_dir().join(format!("holger-gate-receipt-{}", std::process::id()));
        let _ = std::fs::remove_dir_all(&dir);
        std::fs::create_dir_all(&dir).unwrap();
        let c: BTreeMap<String, String> = GATE_REPOS
            .iter()
            .enumerate()
            .map(|(i, r)| {
                (
                    r.to_string(),
                    char::from(b'a' + i as u8).to_string().repeat(40),
                )
            })
            .collect();
        assert_eq!(
            gate_receipt_name(&c),
            format!(
                "holger-gate-{}-{}-{}.json",
                "a".repeat(12),
                "b".repeat(12),
                "c".repeat(12)
            )
        );
        assert!(gate_refusal(&dir, &c)
            .unwrap()
            .contains("[holger-no-mock-proof] no gate receipt"));
        let write = |green: bool, holger: &str| {
            let mut commits = serde_json::to_value(&c).unwrap();
            commits["holger"] = serde_json::json!(holger);
            std::fs::write(dir.join(gate_receipt_name(&c)), serde_json::json!({"green": green, "clean": true, "commits": commits, "checks": [{"name": "clock", "pass": false}]}).to_string()).unwrap()
        };
        write(false, &"a".repeat(40));
        let red = gate_refusal(&dir, &c).unwrap();
        assert!(red.contains("is RED") && red.contains("clock"), "{red}");
        write(true, &"d".repeat(40));
        assert!(gate_refusal(&dir, &c)
            .unwrap()
            .contains("another holger commit"));
        write(true, &"a".repeat(40));
        assert_eq!(gate_refusal(&dir, &c), None, "green, these commits");
        let mut no_edda = c.clone();
        no_edda.insert("edda".into(), String::new());
        assert!(gate_refusal(&dir, &no_edda)
            .unwrap()
            .contains("edda is not a checkout"));
        let _ = std::fs::remove_dir_all(&dir);
    }

    /// **A Git LFS pointer is refused by name** (lane D13): the picture the front serves, a
    /// pointer in a sibling, nothing from `target/` or `docs/`, and real content passes.
    #[test]
    fn a_git_lfs_pointer_in_what_a_medium_packs_is_named() {
        let t = std::env::temp_dir().join(format!(
            "d13-lfs-{}-{}",
            std::process::id(),
            std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .unwrap()
                .as_nanos()
        ));
        let (holger, ui) = (t.join("holger"), t.join("holger-ui"));
        let pointer =
            b"version https://git-lfs.github.com/spec/v1\noid sha256:a8bcf252d5\nsize 578646\n";
        for (dir, name, bytes) in [
            (&holger, "holger-front/assets/holger.webp", &pointer[..]),
            (
                &holger,
                "holger-front/assets/real.png",
                &b"\x89PNG\r\n\x1a\n...."[..],
            ),
            (&holger, "target/release/build/x/pointer.webp", &pointer[..]),
            (&holger, "docs/manual.pdf", &pointer[..]),
            (&ui, "assets/logo.png", &pointer[..]),
        ] {
            let p = dir.join(name);
            std::fs::create_dir_all(p.parent().unwrap()).unwrap();
            std::fs::write(p, bytes).unwrap();
        }
        let mut found = nornir_build_thing::lfs::lfs_pointers(&[holger.clone(), ui.clone()], &[]);
        found.sort();
        assert_eq!(
            found,
            vec![
                holger.join("holger-front/assets/holger.webp"),
                ui.join("assets/logo.png")
            ]
        );
        let e = format!("{:#}", refuse_lfs_pointers(&holger).unwrap_err());
        assert!(
            e.contains("[estate-media-lfs-pointer]")
                && e.contains("holger.webp")
                && e.contains("git lfs pull"),
            "{e}"
        );
        std::fs::write(
            holger.join("holger-front/assets/holger.webp"),
            b"RIFF....WEBPVP8L",
        )
        .unwrap();
        std::fs::write(ui.join("assets/logo.png"), b"\x89PNG").unwrap();
        refuse_lfs_pointers(&holger).expect("real content passes");
        let _ = std::fs::remove_dir_all(&t);
    }
    use super::*;

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

    const H: &str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
    const B: &str = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";

    fn rec(sig: &str) -> String {
        format!("base = \"{B}\"\napp = \"{H}\"\napp_len = 9\nsig = \"{sig}\"\n")
    }

    /// ★ The ONE kit's words (pmo `[products.holger.media]`) are this verb's words.
    #[test]
    fn the_one_kits_argv_is_understood_and_strays_are_refused() {
        let o = parse(&s("mock --where upcloud --release --media-to /r --apply")).unwrap();
        assert_eq!(
            o,
            Opts {
                mock: true,
                place: "upcloud".into(),
                kind: "release".into(),
                media_to: "/r".into(),
                apply: true,
                secrets: None,
                composed: false,
                live_update: true,
                intel_trust: None,
                bundles_trust: None
            }
        );
        assert_eq!(
            parse(&s(
                "--where upcloud --media-to /r --intel-trust /p/intel-trust.ron"
            ))
            .unwrap()
            .intel_trust,
            Some(PathBuf::from("/p/intel-trust.ron")),
            "an airgap estate's intel signers"
        );
        assert!(
            parse(&s(
                "--where upcloud --release --media-to /r --apply --composed"
            ))
            .unwrap()
            .composed
        );
        let o = parse(&s("--where upcloud --dev --media-to /r")).unwrap();
        assert!(!o.mock && !o.apply && o.kind == "dev");
        assert_eq!(receipt_name(&o.kind), "media-holger-dev.toml");
        assert_eq!(
            receipt_name("release"),
            "media-holger-release.toml",
            "pmo reads media-holger-release.toml"
        );
        assert_eq!(
            parse(&s("--where upcloud --media-to /r")).unwrap().kind,
            "dev",
            "DEV unless --release is said (owner 2026-10-19)"
        );
        assert!(parse(&s("--where aws --release --media-to /r"))
            .unwrap_err()
            .to_string()
            .contains("aws"));
        assert!(parse(&s("--where upcloud --release"))
            .unwrap_err()
            .to_string()
            .contains("--media-to"));
        assert!(parse(&s("--where upcloud --release --media-to /r --dev"))
            .unwrap_err()
            .to_string()
            .contains("not both"));
        assert!(
            parse(&s("--where upcloud --release --media-to /r --variant x"))
                .unwrap_err()
                .to_string()
                .contains("unknown word")
        );
    }

    /// ★ **The estate's live-update switch reaches every medium** (lane M5): `--live-update off`
    /// composes `live_app=off` (korp.live_update=off on the cmdline), absent is on, a stray word
    /// is refused, and the compose path asks with it.
    #[test]
    fn the_estates_live_update_switch_reaches_the_media() {
        assert!(
            !parse(&s(
                "--where upcloud --release --media-to /r --live-update off"
            ))
            .unwrap()
            .live_update
        );
        assert!(
            parse(&s(
                "--where upcloud --release --media-to /r --live-update on"
            ))
            .unwrap()
            .live_update
        );
        assert!(
            parse(&s("--where upcloud --release --media-to /r"))
                .unwrap()
                .live_update,
            "absent: on"
        );
        assert!(parse(&s(
            "--where upcloud --release --media-to /r --live-update maybe"
        ))
        .unwrap_err()
        .to_string()
        .contains("`on` or `off`"));
        assert_eq!(live_update_params(false), ["live_app=off"]);
        assert_eq!(live_update_params(true), ["live_app=on"]);
        for m in MEDIA {
            let i = holger_recipe::declared()
                .installers
                .into_iter()
                .find(|i| i.name == m.variant)
                .unwrap();
            assert!(
                i.params
                    .iter()
                    .any(|p| p.name == "live_app" && p.boot_param == "korp.live_update"),
                "{} declares the switch",
                m.variant
            );
        }
        let src = include_str!("estate_media.rs");
        assert_eq!(
            src.matches(&["params.extend(live_update_params(", "o.live_update));"].concat())
                .count(),
            1,
            "the compose asks with the switch"
        );
    }

    /// ★ Owner 2026-10-19: Holger's media are DEV unless `--release` is said — DEV composes with
    /// the narrate sender ON (Gunnar's `--dev-narrate`), `--release` is `--no-dev-narrate`, and
    /// the receipt says which kind it is.
    #[test]
    fn dev_is_the_default_and_narrates_and_release_compiles_the_sender_out() {
        let dev = parse(&s("--where upcloud --media-to /r --apply")).unwrap();
        assert_eq!(dev.kind, KIND_DEV);
        assert!(narrates(&dev.kind));
        assert_eq!(forge_opts(&dev.kind), ["dev-narrate=on"]);
        assert_eq!(
            parse(&s("--where upcloud --dev-narrate --media-to /r"))
                .unwrap()
                .kind,
            KIND_DEV
        );
        let rel = parse(&s("--where upcloud --release --media-to /r")).unwrap();
        assert!(!narrates(&rel.kind));
        assert_eq!(forge_opts(&rel.kind), ["dev-narrate=off"]);
        assert_eq!(
            parse(&s("--where upcloud --no-dev-narrate --media-to /r"))
                .unwrap()
                .kind,
            KIND_RELEASE,
            "--no-dev-narrate is --release"
        );
        for mixed in [
            "--release --dev-narrate",
            "--dev --no-dev-narrate",
            "--release --dev",
        ] {
            assert!(
                parse(&s(&format!("--where upcloud {mixed} --media-to /r")))
                    .unwrap_err()
                    .to_string()
                    .contains("not both"),
                "{mixed}"
            );
        }
        // The option the forge is given is the one every estate medium declares.
        for m in MEDIA {
            let i = holger_recipe::declared()
                .installers
                .into_iter()
                .find(|i| i.name == m.variant)
                .unwrap();
            assert!(
                i.options
                    .iter()
                    .any(|o| o.name == holger_recipe::packaging::DEV_NARRATE_OPTION && !o.default),
                "{}: no default-off dev-narrate option",
                m.word
            );
        }
        let objects: BTreeMap<String, Object> = BTreeMap::from([(
            "holger-keep".to_string(),
            Object {
                url: "https://b/x".into(),
                sha256: "1".repeat(64),
                bytes: 9,
            },
        )]);
        let (none, carries) = (BTreeMap::new(), BTreeMap::new());
        let d = receipt_text(
            KIND_DEV,
            &objects,
            &none,
            "holger 0.7.0",
            "2026-10-19",
            &carries,
        )
        .unwrap();
        assert!(
            d.contains("# build: kind = \"dev\", dev_narrate = true"),
            "{d}"
        );
        let t: toml::Table = toml::from_str(&d).unwrap();
        assert_eq!(
            t[RECEIPT_RELEASE_NOTES][&"1".repeat(64)]["label"].as_str(),
            Some("holger 0.7.0 (DEV: narrate on)")
        );
        let r = receipt_text(
            KIND_RELEASE,
            &objects,
            &none,
            "holger 0.7.0",
            "2026-10-19",
            &carries,
        )
        .unwrap();
        assert!(
            r.contains("dev_narrate = false") && r.contains("label = \"holger 0.7.0\""),
            "{r}"
        );
    }

    /// ★ P1 (R6's finding): a DEV medium is built exactly like Gunnar's dev media — the forge's
    /// resolution of `--opt dev-narrate=on` arrives at the payload's declared `…-narrate` row and
    /// its `dev-media` profile (the artifact under `<target>/dev-media/`), for the main and the
    /// front medium and their keep media; a release medium builds the payload row, `release`.
    #[test]
    fn a_dev_medium_builds_the_narrate_row_with_the_dev_media_profile() {
        use holger_recipe::packaging::{FRONT_PAYLOAD_VARIANT, METERED_PAYLOAD_VARIANT};
        for m in MEDIA {
            let base = if m.word.contains("front") {
                FRONT_PAYLOAD_VARIANT
            } else {
                METERED_PAYLOAD_VARIANT
            };
            let dev = payload_row(m.variant, KIND_DEV).unwrap();
            assert_eq!(dev.name, format!("{base}-narrate"), "{}", m.word);
            assert_eq!(dev.profile.label(), "dev-media", "{}", m.word);
            let args = dev.cargo_args();
            assert!(
                args.windows(2).any(|w| w == ["--profile", "dev-media"]),
                "{}: {args:?}",
                m.word
            );
            assert!(
                dev.features.iter().any(|f| f == "dev-narrate"),
                "{}",
                m.word
            );
            let rel = payload_row(m.variant, KIND_RELEASE).unwrap();
            assert_eq!(rel.name, base, "{}", m.word);
            assert_eq!(rel.profile.label(), "release", "{}", m.word);
            assert!(
                !rel.features.iter().any(|f| f == "dev-narrate"),
                "{}",
                m.word
            );
        }
        assert_eq!(
            payload_row(holger_recipe::packaging::METERED_INSTALLER, KIND_DEV)
                .unwrap()
                .name,
            "mvp-server-musl-metered-narrate"
        );
        assert_eq!(
            payload_row(holger_recipe::packaging::FRONT_INSTALLER_METERED, KIND_DEV)
                .unwrap()
                .name,
            "estate-front-musl-metered-narrate"
        );
    }

    /// ★ The release is monetize `lifecycle::Holger`'s media: front, main/twin, both keeps, and
    /// the two app media carry the live apps `holger-live` and `holger-front-live`; every medium is
    /// a declared Holger installer, and nothing of Gunnar's is named.
    #[test]
    fn the_release_is_holgers_four_media_and_two_live_apps() {
        let words: Vec<&str> = MEDIA.iter().map(|m| m.word).collect();
        assert_eq!(
            words,
            ["holger-front", "holger", "holger-keep", "holger-front-keep"]
        );
        let live: Vec<String> = MEDIA
            .iter()
            .filter(|m| m.live)
            .map(|m| live_kind(m.word))
            .collect();
        assert_eq!(live, ["holger-front-live", "holger-live"]);
        for m in MEDIA {
            forge_door::declared_medium(m.variant, ArtifactKind::Installer, "x").unwrap();
            assert!(!m.word.contains("gunnar") && !m.variant.contains("gunnar"));
        }
        assert_eq!(medium_key("holger", H), format!("release/holger/{H}.iso"));
        assert_eq!(
            live_key("holger-front", H),
            format!("release/holger-front-live/{H}.live-app.znippy")
        );
        assert_eq!(
            live_companions(Path::new("/x/.iso-out/holger-installer-metered.iso")).1,
            PathBuf::from("/x/.iso-out/holger-installer-metered.live-app.znippy")
        );
    }

    /// ★ R4: the main medium is composed WITHOUT a hand-set `holger_ron` — estate-media
    /// stages the estate's declaration and the korp root itself and names them for every
    /// medium that declares the parameter, never for the front.
    #[test]
    fn the_main_medium_is_composed_with_the_estates_declaration_and_no_hand_step() {
        let dir =
            std::env::temp_dir().join(format!("holger-estate-media-staged-{}", std::process::id()));
        let s = Staged::write(&dir).unwrap();
        assert_eq!(
            std::fs::read_to_string(&s.ron).unwrap(),
            holger_recipe::estate_ron::medium()
        );
        assert!(std::fs::read_to_string(&s.korp_root)
            .unwrap()
            .starts_with("-----BEGIN CERTIFICATE-----"));
        for m in MEDIA {
            let p = forge_params(m.variant, &s);
            assert_eq!(
                p[0],
                format!("pairing_seed=@{}", s.marker.display()),
                "{}",
                m.word
            );
            let box_medium = m.word == "holger" || m.word == "holger-keep";
            assert_eq!(
                p.contains(&format!("holger_ron=@{}", s.ron.display())),
                box_medium,
                "{}: {p:?}",
                m.word
            );
            assert_eq!(
                p.contains(&format!("plugin_trust_root=@{}", s.korp_root.display())),
                box_medium,
                "{}: {p:?}",
                m.word
            );
            // Every parameter named is one the medium declares, and every required one is named.
            let i = holger_recipe::declared()
                .installers
                .into_iter()
                .find(|i| i.name == m.variant)
                .unwrap();
            for given in &p {
                let name = given.split('=').next().unwrap();
                assert!(
                    i.params.iter().any(|d| d.name == name),
                    "{}: {name} is not declared",
                    m.word
                );
            }
            for req in i.params.iter().filter(|d| d.required) {
                assert!(
                    p.iter().any(|g| g.starts_with(&format!("{}=", req.name))),
                    "{}: the required {} is not named",
                    m.word,
                    req.name
                );
            }
        }
        // P5: an intel trust policy given is staged for every box medium, and only then.
        assert!(
            s.intel_trust.is_none(),
            "no policy given: the slot stays empty"
        );
        let policy = dir.join("given-intel-trust.ron");
        let text = intel_policy(&[]);
        std::fs::write(&policy, &text).unwrap();
        let s = s.with_intel_trust(&policy).unwrap();
        let staged = s.intel_trust.clone().expect("staged");
        assert_eq!(std::fs::read_to_string(&staged).unwrap(), text);
        for m in MEDIA {
            let p = forge_params(m.variant, &s);
            let box_medium = m.word == "holger" || m.word == "holger-keep";
            assert_eq!(
                p.contains(&format!("intel_trust_policy=@{}", staged.display())),
                box_medium,
                "{}: {p:?}",
                m.word
            );
        }
        let _ = std::fs::remove_dir_all(&dir);
    }

    /// A coherent intel trust policy (pinned signers) whose `allowed_products` are `products`.
    fn intel_policy(products: &[&str]) -> String {
        use update_bundle::policy::{
            ApprovedSigner, CertificateValidity, ExpiredPosture, TrustMode, TrustPolicy,
        };
        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: "ab".repeat(32),
                subject: None,
                roles: vec!["security-reviewer".into()],
            }],
            required_approval_roles: vec!["security-reviewer".into()],
            minimum_distinct_approvers: 1,
            trusted_release_signers: vec!["cd".repeat(32)],
            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: products.iter().map(|p| p.to_string()).collect(),
            allowed_models: vec![],
        }
        .to_ron()
        .unwrap()
    }

    /// ★ P6: the operator's intel trust policy is held BEFORE it is staged — a policy the box
    /// could not load, or one that shuts out `holger.intel/v1`, is refused by name here, never
    /// at an airgap box's first import; `--intel-trust` with `--composed` is refused (those
    /// media carry their composer's policy), and a later run without one stages none.
    #[test]
    fn an_intel_trust_policy_is_held_before_it_rides_a_medium() {
        assert_eq!(
            intel_trust_refusal(&intel_policy(&[])),
            None,
            "empty allowed_products = any product"
        );
        assert_eq!(
            intel_trust_refusal(&intel_policy(&[holger_intel::INTEL_PRODUCT, "x"])),
            None
        );
        let why =
            intel_trust_refusal(&intel_policy(&["acme.firmware/v1"])).expect("shuts intel out");
        assert!(
            why.starts_with("[estate-media-intel-trust]")
                && why.contains(holger_intel::INTEL_PRODUCT),
            "{why}"
        );
        let why = intel_trust_refusal("(schema_version: \"x\")").expect("not a policy");
        assert!(why.contains("does not load"), "{why}");
        let unset =
            intel_policy(&[]).replace("trust_mode: Some(PinnedSigners)", "trust_mode: None");
        assert_ne!(unset, intel_policy(&[]), "the fixture names its trust mode");
        assert!(
            intel_trust_refusal(&unset).is_some(),
            "an unanswered trust question is refused"
        );

        let dir =
            std::env::temp_dir().join(format!("holger-estate-media-intel-{}", std::process::id()));
        let st = Staged::write(&dir).unwrap();
        let bad = dir.join("bad.ron");
        std::fs::write(&bad, intel_policy(&["acme.firmware/v1"])).unwrap();
        let e = st.clone().with_intel_trust(&bad).unwrap_err().to_string();
        assert!(e.contains("[estate-media-intel-trust]"), "{e}");
        assert!(
            !dir.join(INTEL_TRUST_FILE).exists(),
            "a refused policy is never staged"
        );
        let good = dir.join("good.ron");
        std::fs::write(&good, intel_policy(&[])).unwrap();
        st.with_intel_trust(&good).unwrap();
        assert!(dir.join(INTEL_TRUST_FILE).exists());
        let again = Staged::write(&dir).unwrap();
        assert!(
            again.intel_trust.is_none() && !dir.join(INTEL_TRUST_FILE).exists(),
            "an earlier run's policy never rides a medium composed without one"
        );
        assert!(MEDIA.iter().all(|m| !forge_params(m.variant, &again)
            .iter()
            .any(|p| p.starts_with("intel_trust_policy="))));
        let _ = std::fs::remove_dir_all(&dir);

        let e = parse(&s(
            "--where upcloud --media-to /r --composed --intel-trust /p.ron",
        ))
        .unwrap_err()
        .to_string();
        assert!(
            e.contains("--intel-trust") && e.contains("--composed"),
            "{e}"
        );
    }

    /// ★ R10 (Gunnar's kit, `FRONT_CLOCK_SOURCES`): the FRONT media are composed with the
    /// estate's root of time, `clock=https://api.stripe.com/,https://www.cloudflare.com/`;
    /// without it the front syncs nothing and refuses to sign time for every box
    /// (`peer://10.44.0.1:50123`). The box media declare no clock and are given none.
    #[test]
    fn the_front_media_are_composed_with_the_clock_sources() {
        let dir =
            std::env::temp_dir().join(format!("holger-estate-media-clock-{}", std::process::id()));
        let s = Staged::write(&dir).unwrap();
        assert_eq!(
            FRONT_CLOCK_SOURCES, "https://api.stripe.com/,https://www.cloudflare.com/",
            "Gunnar's sources, the same words"
        );
        assert_eq!(
            FRONT_CLOCK_SOURCES,
            estate_clock::config::front_sources(
                estate_clock::config::FrontPlace::Cloud,
                estate_clock::config::BENCH_CLOCK_PORT
            ),
            "the shared API's cloud sources"
        );
        for m in MEDIA {
            let p = forge_params(m.variant, &s);
            let front = m.word.contains("front");
            assert_eq!(
                p.contains(&format!("clock={FRONT_CLOCK_SOURCES}")),
                front,
                "{}: {p:?}",
                m.word
            );
            assert_eq!(
                p.iter().any(|g| g.starts_with("clock=")),
                front,
                "{}: {p:?}",
                m.word
            );
        }
        let _ = std::fs::remove_dir_all(&dir);
    }

    /// ★ R10: a composed FRONT medium is uploaded only if its command line carries the clock
    /// sources (r10's first compose had none); a box medium is not asked.
    #[test]
    fn a_composed_front_medium_without_the_clock_is_refused() {
        let with = format!("korp.appliance=lean korp.entry=holger-estate-front korp.env.ESTATE_CLOCK={FRONT_CLOCK_SOURCES} korp.require_signed=1");
        let without = "korp.appliance=lean korp.entry=holger-estate-front korp.live_update=on korp.require_signed=1";
        assert!(composed_front_has_clock("holger-front", with.as_bytes()).is_ok());
        let e = composed_front_has_clock("holger-front-keep", without.as_bytes())
            .unwrap_err()
            .to_string();
        assert!(
            e.contains("[estate-media-front-clock]")
                && e.contains("holger-front-keep")
                && e.contains(FRONT_CLOCK_SOURCES),
            "{e}"
        );
        assert!(
            composed_front_has_clock("holger", without.as_bytes()).is_ok(),
            "a box medium takes its clock from the front's offer"
        );
    }

    /// ★ korp's record becomes `[live_apps.<kind>]`; an unsigned app is refused by name (a
    /// Holger release without live apps would update from keep only), a missing field too.
    #[test]
    fn a_live_record_is_taken_signed_only() {
        let r = live_record("holger", &rec("4b4f")).unwrap();
        assert_eq!(
            (r.base.as_str(), r.app.as_str(), r.app_len, r.sig.as_str()),
            (B, H, 9, "4b4f")
        );
        assert!(live_record("holger", &rec(""))
            .unwrap_err()
            .to_string()
            .contains("[estate-media-unsigned]"));
        assert!(live_record("holger", &rec("ab").replace("app_len = 9", ""))
            .unwrap_err()
            .to_string()
            .contains("app_len"));
        assert!(live_record("holger", &rec("ab").replace(B, "short"))
            .unwrap_err()
            .to_string()
            .contains("`base`"));
    }

    /// ★ **What the release declares rides its live record** (lane B5): the release's env off
    /// korp's record, the declaration korp composed beside the `.iso` (hex in the receipt's
    /// `[live_apps.<kind>]`, the shape the ONE box's `LiveRecord` reads); a declaration
    /// without its signature on a signed medium is refused by name; a record before B5 reads
    /// as none declared.
    #[test]
    fn a_live_record_carries_the_release_declaration_and_env() {
        let text = format!(
            "{}env = \"korp.env.HOLGER_DATA=/data korp.env.X=1\"\n",
            rec("4b4f")
        );
        let r = with_release(
            "holger",
            live_record("holger", &text).unwrap(),
            Some(b"korp".to_vec()),
            Some(vec![0x77; 2]),
        )
        .unwrap();
        assert_eq!(
            (r.env.clone(), r.release.as_str(), r.release_sig.as_str()),
            (
                vec![
                    "korp.env.HOLGER_DATA=/data".to_string(),
                    "korp.env.X=1".to_string()
                ],
                "6b6f7270",
                "7777"
            )
        );
        assert!(
            with_release("holger", r.clone(), Some(b"korp".to_vec()), None)
                .unwrap_err()
                .to_string()
                .contains("[estate-media-release-unsigned]")
        );
        let none = with_release(
            "holger",
            live_record("holger", &rec("4b4f")).unwrap(),
            None,
            None,
        )
        .unwrap();
        assert!(none.env.is_empty() && none.release.is_empty());
        let obj = |w: &str| {
            (
                w.to_string(),
                Object {
                    url: format!("https://bucket.example/{w}"),
                    sha256: "1".repeat(64),
                    bytes: 9,
                },
            )
        };
        let objects: BTreeMap<String, Object> =
            ["holger", "holger-live"].iter().map(|w| obj(w)).collect();
        let live: BTreeMap<String, LiveRecord> = [("holger".to_string(), r)].into();
        let t: toml::Table = toml::from_str(
            &receipt_text("release", &objects, &live, "l", "d", &BTreeMap::new()).unwrap(),
        )
        .unwrap();
        let l = &t[RECEIPT_LIVE_APPS]["holger"];
        assert_eq!(
            (
                l["release"].as_str(),
                l["release_sig"].as_str(),
                l["env"].as_array().map(|a| a.len())
            ),
            (Some("6b6f7270"), Some("7777"), Some(2))
        );
        assert_eq!(
            release_companions(Path::new("/x/.iso-out/holger-installer.iso")).0,
            PathBuf::from(format!(
                "/x/.iso-out/holger-installer.{}",
                korp_installer::live::MEDIUM_RELEASE_SUFFIX
            ))
        );
    }

    /// ★ The receipt pmo feeds (`sells::site_media`): six objects, both live records with their
    /// archives, and a release note per medium; a live record without its archive is refused.
    #[test]
    fn the_receipt_names_the_media_the_live_apps_and_the_notes() {
        let obj = |w: &str, sha: &str| {
            (
                w.to_string(),
                Object {
                    url: format!("https://bucket.example/release/{w}/{sha}"),
                    sha256: sha.to_string(),
                    bytes: 9,
                },
            )
        };
        let objects: BTreeMap<String, Object> = [
            "holger-front",
            "holger",
            "holger-keep",
            "holger-front-keep",
            "holger-live",
            "holger-front-live",
        ]
        .iter()
        .enumerate()
        .map(|(i, w)| obj(w, &format!("{i}").repeat(64)))
        .collect();
        let r = live_record("holger", &rec("4b4f")).unwrap();
        let live: BTreeMap<String, LiveRecord> = [
            ("holger".to_string(), r.clone()),
            ("holger-front".to_string(), r.clone()),
        ]
        .into();
        let carries: BTreeMap<String, String> =
            [("holger".to_string(), "8579df6a".to_string())].into();
        let text = receipt_text(
            "release",
            &objects,
            &live,
            "holger 0.6.10",
            "2026-10-18",
            &carries,
        )
        .unwrap();
        let t: toml::Table = toml::from_str(&text).unwrap();
        for k in objects.keys() {
            assert!(t[k.as_str()].get("object").is_some(), "{k}: {text}");
        }
        for k in ["holger", "holger-front"] {
            let l = &t[RECEIPT_LIVE_APPS][k];
            assert_eq!(
                (
                    l["base"].as_str(),
                    l["app"].as_str(),
                    l["app_len"].as_integer(),
                    l["sig"].as_str()
                ),
                (Some(B), Some(H), Some(9), Some("4b4f"))
            );
        }
        let notes = t[RECEIPT_RELEASE_NOTES].as_table().unwrap();
        assert_eq!(notes.len(), 4, "one note per medium: {text}");
        assert_eq!(
            notes[&"1".repeat(64)]["carries"]["holger"].as_str(),
            Some("8579df6a")
        );
        assert!(!text.to_lowercase().contains("gunnar"), "{text}");
        let mut lost = objects.clone();
        lost.remove("holger-front-live");
        assert!(receipt_text("release", &lost, &live, "l", "d", &carries)
            .unwrap_err()
            .to_string()
            .contains("holger-front-live"));
    }

    /// ★ `--composed` takes only media composed at this HEAD (the bytes a KVM install proved).
    #[test]
    fn a_composed_medium_is_held_to_this_head() {
        let head = "e6b1bb7c1ab2154e5ccbdfecc67134b53e5a48a6";
        composed_is_this_head("holger", r#"{"head":"e6b1bb7"}"#, head).unwrap();
        assert!(
            composed_is_this_head("holger", r#"{"head":"8579df6"}"#, head)
                .unwrap_err()
                .to_string()
                .contains("[estate-media-composed-stale]")
        );
        assert!(composed_is_this_head("holger", r#"{"iso":"x"}"#, head)
            .unwrap_err()
            .to_string()
            .contains("names no head"));
    }

    /// ★ A medium that cannot read its own CD is refused before upload (A7 row 25, Gunnar
    /// 09fe2c48): its record must say `cd_driver: true`; false or absent refuses by name.
    #[test]
    fn a_medium_that_cannot_read_its_cd_is_refused() {
        composed_reads_its_cd("holger", r#"{"head":"e6b1bb7","cd_driver":true}"#).unwrap();
        let e = composed_reads_its_cd("holger-front", r#"{"cd_driver":false}"#)
            .unwrap_err()
            .to_string();
        assert!(
            e.contains("[medium-no-cd-driver]")
                && e.contains("holger-front")
                && e.contains("CONFIG_BLK_DEV_SR=y"),
            "{e}"
        );
        let e = composed_reads_its_cd("holger", r#"{"head":"e6b1bb7"}"#)
            .unwrap_err()
            .to_string();
        assert!(
            e.contains("[medium-no-cd-driver]") && e.contains("no cd_driver"),
            "an unproven medium refuses too: {e}"
        );
        assert!(composed_reads_its_cd("holger", "not json").is_err());
    }

    /// ★ The bucket: the mock's bench face, or the account's pair read by name (https only).
    #[test]
    fn the_bucket_is_the_mocks_or_the_vaults_by_name() {
        assert_eq!(mock_api_port(None).unwrap(), MOCK_API);
        assert_eq!(mock_api_port(Some("8391,18591,12491,12492")).unwrap(), 8391);
        assert!(mock_api_port(Some("x")).is_err());
        assert_eq!(mock_bucket(8391).endpoint, "http://127.0.0.1:8391/objsto");
        let v = "# vault\nRELEASE_MEDIA_ENDPOINT=https://abc.upcloudobjects.com/\nRELEASE_MEDIA_BUCKET=holger-release\nRELEASE_MEDIA_ACCESS_KEY_ID=AK\nRELEASE_MEDIA_SECRET_ACCESS_KEY=SK\n";
        let b = bucket_of(v).unwrap();
        assert_eq!(
            (b.endpoint.as_str(), b.region.as_str(), b.bucket.as_str()),
            (
                "https://abc.upcloudobjects.com",
                DEFAULT_REGION,
                "holger-release"
            )
        );
        assert!(
            !format!("{b:?}").contains("SK"),
            "the secret is never printed"
        );
        assert!(
            bucket_of(&v.replace("RELEASE_MEDIA_BUCKET=holger-release\n", ""))
                .unwrap_err()
                .to_string()
                .contains("RELEASE_MEDIA_BUCKET")
        );
        assert!(bucket_of(&v.replace("https://", "http://"))
            .unwrap_err()
            .to_string()
            .contains("https"));
    }

    /// ★ SigV4: the AWS documentation's signing-key vector, and a PUT path-style under the
    /// endpoint's own prefix.
    #[test]
    fn a_put_is_signed_sigv4_path_style() {
        // AWS's published vector: kSigning for 20150830/us-east-1/iam.
        let k = hmac(b"AWS4wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", "20150830");
        let k = hmac(&hmac(&hmac(&k, "us-east-1"), "iam"), "aws4_request");
        assert_eq!(
            hex(&k),
            "c4afb1cc5771d871763a393e44b703571b55cc28424d1a5e86da6ed3c154a4b9"
        );
        let (url, h) = put_request(
            &mock_bucket(8391),
            "release/holger/x.iso",
            H,
            "20261018T120000Z",
        );
        assert_eq!(
            url,
            "http://127.0.0.1:8391/objsto/release/release/holger/x.iso"
        );
        assert_eq!(h[1], ("x-amz-content-sha256".to_string(), H.to_string()));
        assert!(h[2].1.starts_with("AWS4-HMAC-SHA256 Credential=mock-release-access-key/20261018/mock/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature="), "{}", h[2].1);
        assert_eq!(civil(20_744), (2026, 10, 18));
    }

    /// ★ `put_file` PUTs the bytes with their sha256 and answers the object; a refusing bucket
    /// is refused by name.
    #[test]
    fn put_file_sends_the_bytes_and_reads_the_answer() {
        use std::io::{Read, Write};
        let l = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
        let port = l.local_addr().unwrap().port();
        let serve = std::thread::spawn(move || {
            let mut heard = Vec::new();
            for status in ["200 OK", "403 Forbidden"] {
                let (mut c, _) = l.accept().unwrap();
                let mut buf = Vec::new();
                let mut chunk = [0u8; 4096];
                loop {
                    let n = c.read(&mut chunk).unwrap();
                    buf.extend_from_slice(&chunk[..n]);
                    let text = String::from_utf8_lossy(&buf).to_string();
                    if let Some(i) = text.find("\r\n\r\n") {
                        let len: usize = text
                            .lines()
                            .find_map(|l| {
                                l.to_ascii_lowercase()
                                    .strip_prefix("content-length:")
                                    .map(|v| v.trim().parse().unwrap())
                            })
                            .unwrap_or(0);
                        if buf.len() >= i + 4 + len {
                            break;
                        }
                    }
                    if n == 0 {
                        break;
                    }
                }
                heard.push(String::from_utf8_lossy(&buf).to_string());
                c.write_all(format!("HTTP/1.1 {status}\r\ncontent-length: 0\r\n\r\n").as_bytes())
                    .unwrap();
            }
            heard
        });
        let d = std::env::temp_dir().join(format!("holger-estate-media-{}", std::process::id()));
        std::fs::create_dir_all(&d).unwrap();
        let f = d.join("m.iso");
        std::fs::write(&f, b"holger medium").unwrap();
        let b = mock_bucket(port);
        let o = put_file(&b, "release/holger/m.iso", &f).unwrap();
        assert_eq!(
            (o.bytes, o.sha256.as_str()),
            (13, sha256_hex(b"holger medium").as_str())
        );
        assert!(
            o.url.ends_with("/objsto/release/release/holger/m.iso"),
            "{}",
            o.url
        );
        assert!(put_file(&b, "release/holger/m.iso", &f)
            .unwrap_err()
            .to_string()
            .contains("HTTP 403"));
        let heard = serve.join().unwrap();
        assert!(
            heard[0].starts_with("PUT /objsto/release/release/holger/m.iso "),
            "{}",
            heard[0]
        );
        assert!(
            heard[0].contains("holger medium")
                && heard[0]
                    .to_ascii_lowercase()
                    .contains(&format!("x-amz-content-sha256: {}", o.sha256))
        );
        let _ = std::fs::remove_dir_all(&d);
    }
}