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

nordisk/edda

crates/nornir-build-thing/src/image_build.rs

//! **Image / VM build RUNNER + provenance sealer** — the IMAGE-target peer of
//! [`build_variant`](crate::provenance::build_variant).
//!
//! `build_variant` runs a `cargo build` and seals provenance over the produced
//! executables. This module is its sibling for the *other* two build target
//! kinds — [`ContainerBuild`](crate::container::ContainerBuild) images and
//! [`VmBuild`](crate::vm::VmBuild) appliances — which had NO build+seal peer.
//! tunnr flagged the gap: consumers fell back to
//! [`ContainerBuild::content_id`](crate::container::ContainerBuild::content_id) —
//! a *descriptor* hash — because nothing sealed provenance over the actually
//! **produced artifact** (the built image's content digest, or the kernel+rootfs
//! sha256).
//!
//! # What it seals
//!
//! A [`TargetProvenance`] binds the **produced-artifact** hash, not just the
//! descriptor fingerprint:
//!
//! * **container** — the image's engine-reported content digest (`sha256:…`).
//! * **vm** — the sha256 of each produced kernel / rootfs / disk file.
//!
//! The [`TargetProvenance::descriptor_id`] still records the descriptor's own
//! `content_id`, so a reader can tell the SPEC hash (what was asked for) from the
//! ARTIFACT hash (what came out). [`TargetAttestation`] folds the record to a
//! SHA-256 digest and seals it with the same [`ProvenanceSealer`] seam
//! `build_variant` uses — so a tampered artifact digest breaks the seal.
//!
//! # Seam-based / dep-light default
//!
//! The build itself rides a **seam** so the default crate stays dependency-light:
//!
//! * [`ImageBuilder`] — actually builds a container image and returns its digest.
//!   The heavy real impl [`BollardImageBuilder`] (feature `image-build`) drives
//!   podman/Docker over the REST API (bollard, zero-shell); tests inject a fake.
//! * [`VmImageBuilder`] — the VM handoff. The heavy Buildroot kernel/rootfs build
//!   stays **tunnr's**; the dep-light [`PrebuiltVmBuilder`] seals provenance over
//!   the kernel+rootfs/disk artifacts tunnr already produced (the blessed "hash
//!   the produced artifacts if present" path).
//!
//! ```no_run
//! use nornir_build_thing::image_build::{build_vm, PrebuiltVmBuilder};
//! use nornir_build_thing::VmBuild;
//! # fn main() -> anyhow::Result<()> {
//! let vm = VmBuild { name: "appliance".into(), kernel: "artifacts/bzImage".into(),
//!                    rootfs: "artifacts/rootfs.cpio.gz".into(), ..Default::default() };
//! let att = build_vm(&PrebuiltVmBuilder::new(), &vm, "tunnr", std::path::Path::new("."))?;
//! // `att` binds the produced kernel+rootfs sha256 — seal it with a ProvenanceSealer.
//! println!("{}", att.attestation.digest_hex());
//! # Ok(()) }
//! ```

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

use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};

use crate::container::ContainerBuild;
use crate::iso::IsoBuild;
use crate::provenance::{
    self, Artifact, BuildProvenance, HostInfo, Inputs, Outcome, ProvenanceSealer, Seal, Toolchain,
};
use crate::variant::BuildVariant;
use crate::vm::VmBuild;

/// Schema tag stamped into every image/vm provenance record.
pub const TARGET_PROVENANCE_SCHEMA: &str = "nornir.target-provenance/v1";

/// Which build target kind produced the artifacts a [`TargetProvenance`] seals.
///
/// This was a bespoke `{Container, Vm}` enum — a strict SUBSET of the suite's
/// output-kind axis. It is now an alias for the first-class
/// [`ArtifactKind`](crate::variant::ArtifactKind); image/vm provenance records
/// only ever carry the `Container` / `Vm` members. `ArtifactKind`'s string serde
/// yields `"container"` / `"vm"` — byte-identical to the old
/// `#[serde(rename_all = "snake_case")]` output — so existing sealed
/// target-provenance JSON keeps deserializing.
///
/// Kept as a deprecated alias for external callers that import the old name.
pub use crate::variant::ArtifactKind as TargetKind;

/// The identity of a **built container image** an [`ImageBuilder`] hands back —
/// the produced artifact, not the descriptor.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BuiltImage {
    /// The image reference (primary tag) that was built.
    pub image_ref: String,
    /// The image content digest reported by the engine (e.g. `sha256:abcd…`).
    pub digest: String,
    /// Image size in bytes (0 when unknown).
    pub bytes: u64,
    /// The exported distributable archive, when the descriptor declared
    /// [`ContainerBuild::archive`] — a real file artifact beside the in-storage
    /// image. `None` when no archive was requested.
    pub archive: Option<Artifact>,
}

impl BuiltImage {
    /// Fold the built image into a produced-artifact [`Artifact`]: path = the image
    /// reference, sha256 = the engine digest with any `sha256:` prefix stripped.
    pub fn to_artifact(&self) -> Artifact {
        let sha = self.digest.strip_prefix("sha256:").unwrap_or(&self.digest).to_ascii_lowercase();
        Artifact { path: self.image_ref.clone(), bytes: self.bytes, sha256: sha }
    }

    /// Every produced artifact: the image identity, plus the exported archive
    /// file when one was declared. THE list [`build_container`] records — so an
    /// archive can never be produced yet missing from the record, or recorded yet
    /// never produced (one writer).
    pub fn to_artifacts(&self) -> Vec<Artifact> {
        let mut v = vec![self.to_artifact()];
        v.extend(self.archive.clone());
        v
    }
}

/// The **container image build seam**: actually build a [`ContainerBuild`] and
/// return the produced image's identity (digest). The dep-light default crate ships
/// only the trait; the heavy real impl is [`BollardImageBuilder`] (feature
/// `image-build`), tests inject a fake.
pub trait ImageBuilder {
    /// Build `image` in `repo_root` (the build context root) and return the built
    /// image's [`BuiltImage`] (ref + content digest).
    fn build(&self, image: &ContainerBuild, repo_root: &Path) -> Result<BuiltImage>;

    /// The engine label recorded in provenance — `bollard` for the real
    /// [`BollardImageBuilder`], a fake's own label in tests. Defaulted so existing
    /// impls need no change, but read by [`build_container`] so the record names the
    /// engine that actually ran (not a hard-coded literal).
    fn engine(&self) -> &str {
        "bollard"
    }

    /// **Every name the built image answers to** — its image id and each manifest digest,
    /// `sha256:`-less. What [`build_container`] compares a pin against: a pin may be either
    /// (`toolchain_archive` pins image ids, the kernel rows manifest digests). Defaulted to the
    /// one digest [`BuiltImage`] carries, for a builder that knows no more.
    fn identities(&self, built: &BuiltImage) -> Result<Vec<String>> {
        Ok(vec![crate::kernel::normalize_sha256(&built.digest)])
    }

    /// **Apply `tags` to the image built as `built`** — the second half of a pinned build,
    /// run only after the image matched its pin. Defaulted to nothing, for a builder that
    /// applies no tags of its own (a fake; a prebuilt resolver).
    fn tag(&self, built: &BuiltImage, tags: &[String]) -> Result<()> {
        let _ = (built, tags);
        Ok(())
    }

    /// **Drop the tag `reference`, never the image under another name.** Used to take a pinned
    /// build's staging tag off again, match or not.
    fn untag(&self, reference: &str) -> Result<()> {
        let _ = reference;
        Ok(())
    }

    /// **The pinned image, when the engine ALREADY holds it under every declared tag**
    /// (2026-10-13). `Some` only when each of `image.tags` names an image that answers to
    /// `pin`. Asked ONLY of an engine that [`cannot_mint_reproducibly`](Self::cannot_mint_reproducibly);
    /// an engine that can is never asked, because there a mint is the recipe-drift gate.
    /// Defaulted to `None`, for a builder that cannot look.
    fn held(&self, image: &ContainerBuild, pin: &str) -> Result<Option<BuiltImage>> {
        let _ = (image, pin);
        Ok(None)
    }

    /// **The engine, named, when it cannot mint a pinned recipe as its pin** — read from the
    /// engine itself ([`engine_that_cannot_reproduce`]), never assumed. `None` (the default,
    /// and the answer for an engine that could not be asked) keeps the rule every pinned
    /// build has had since 2026-09-22: always mint, always compare.
    fn cannot_mint_reproducibly(&self) -> Option<String> {
        None
    }
}

/// The libpod major [`reproducible_build_uri`] targets (`/v5.0.0/libpod/build`): the first
/// whose build door honours `timestamp` the way the pins were minted.
pub const REPRODUCIBLE_LIBPOD_MAJOR: u32 = 5;

/// **Is this an engine that cannot reproduce a pinned recipe?** Pure, over the `/version`
/// components the engine answered (`(name, version)`): `Some("podman 4.9.3")` for a Podman
/// Engine below [`REPRODUCIBLE_LIBPOD_MAJOR`], `None` for one at or above it — and `None` for
/// anything that is not recognisably podman or whose version does not parse, so an unknown
/// engine keeps minting and comparing rather than being excused.
///
/// MEASURED 2026-10-13: podman 4.9.3 (buildah 1.33.7) answers `ea2c07ca` for the kernel
/// toolchain recipe podman 5.7 mints as `3f632569`/`78eaa55a` — no `force-mtime` on its
/// layers, and an `io.buildah.version` label in the config.
pub fn engine_that_cannot_reproduce(components: &[(String, String)]) -> Option<String> {
    let (_, version) = components.iter().find(|(name, _)| name == "Podman Engine")?;
    let major: u32 = version.trim().split('.').next()?.parse().ok()?;
    (major < REPRODUCIBLE_LIBPOD_MAJOR).then(|| format!("podman {}", version.trim()))
}

/// The words a held pinned image is recorded with — in the provenance record's `engine` and
/// on the step's own line — so a release can tell a MINTED pin from a COURIERED one.
pub fn held_evidence(engine: &str) -> String {
    format!("held, not minted on this engine ({engine})")
}

/// The tag a PINNED image is built under before it is checked (2026-09-22): never one a
/// reader resolves, so an image that does not match its pin is never reachable by any tag
/// the declaration names.
pub fn staging_tag(image: &str) -> String {
    format!("{image}:unverified-pin-check")
}

/// **The minting instant a container cell's recipe states**, or `None` when it states none.
///
/// A FILE-mode cell whose Containerfile carries `ENV SOURCE_DATE_EPOCH=<seconds>` answers that
/// number (parsed by the one parser, [`crate::kernel::source_date_epoch_of`], so a malformed
/// value is refused by name rather than dropped); a Containerfile without the line, and every
/// synthesized cell, answers `None` and builds as it always did.
pub fn source_date_epoch_of_cell(image: &ContainerBuild, repo_root: &Path) -> Result<Option<i64>> {
    if !image.is_file_mode() {
        return Ok(None);
    }
    let path = repo_root.join(image.containerfile.trim());
    let text = std::fs::read_to_string(&path)
        .with_context(|| format!("container '{}': reading {}", image.name, path.display()))?;
    if !text.lines().any(|l| l.trim().starts_with("ENV SOURCE_DATE_EPOCH=")) {
        return Ok(None);
    }
    crate::kernel::source_date_epoch_of(&text).map(Some)
}

/// **The build request, at the recipe's instant.** The Docker-compat `/build` that bollard
/// sends has no `timestamp`; podman's own `libpod/build` does. Rewritten to it with
/// `layers=true`, `timestamp=<epoch>` and the OCI output format — the request draupnir's
/// `build_image_reproducible` sends, which is what `podman build --timestamp <epoch>`
/// does. Every other request, and a `/build` already on `libpod`, is left alone (`None`).
pub fn reproducible_build_uri(uri: &str, epoch: i64) -> Option<String> {
    let (scheme, rest) = uri.split_once("://")?;
    let (authority, path_query) = rest.split_once('/').map(|(a, p)| (a, format!("/{p}")))?;
    let (path, query) = match path_query.split_once('?') {
        Some((p, q)) => (p.to_string(), Some(q.to_string())),
        None => (path_query.clone(), None),
    };
    if !path.ends_with("/build") || path.contains("/libpod/") {
        return None;
    }
    let q = query.map(|q| format!("{q}&")).unwrap_or_default();
    Some(format!(
        "{scheme}://{authority}/v5.0.0/libpod/build?{q}layers=true&timestamp={epoch}&outputformat=application%2Fvnd.oci.image.manifest.v1%2Bjson"
    ))
}

/// Does `identities` (image id and manifest digests of a built image) include `pin`?
pub fn built_matches_pin(identities: &[String], pin: &str) -> bool {
    let pin = crate::kernel::normalize_sha256(pin);
    !pin.is_empty() && identities.iter().any(|i| crate::kernel::normalize_sha256(i) == pin)
}

/// A PINNED build: build under [`staging_tag`], check against the pin, and only on a match
/// apply the declared tags. On a mismatch nothing the declaration names moves and the build
/// FAILS by name.
///
/// **The one exception is an engine that says it cannot reproduce**
/// ([`ImageBuilder::cannot_mint_reproducibly`], 2026-10-13). There a mint can only ever fail,
/// so the pinned image must already be in the store under the declared tags (couriered):
/// it is taken as HELD — nothing minted, no tag moved, the outcome says so
/// ([`held_evidence`]) — and when it is not there the build fails naming the two fixes. An
/// engine that CAN reproduce is never excused: it mints and compares even when the store
/// already holds the pin, which is what catches a changed recipe with a forgotten pin.
fn build_pinned(builder: &dyn ImageBuilder, image: &ContainerBuild, repo_root: &Path) -> Result<(BuiltImage, Option<String>)> {
    let pin = crate::kernel::normalize_sha256(&image.expected_sha256);
    if let Some(engine) = builder.cannot_mint_reproducibly() {
        let fixes = format!(
            "courier the pinned image into this box's store under {} (`podman save --format oci-archive \
             --uncompressed <image> | podman load` keeps its digest), or build on a podman \
             {REPRODUCIBLE_LIBPOD_MAJOR} builder",
            image.tags.join(", "),
        );
        // An exported archive is a mint's business, and this engine's mint is never the pin.
        if !image.archive.trim().is_empty() {
            anyhow::bail!(
                "'{}' declares an exported archive ({}) and this engine ({engine}) cannot mint the \
                 pinned image {} reproducibly, so it has nothing verified to export — build on a \
                 podman {REPRODUCIBLE_LIBPOD_MAJOR} builder",
                image.name,
                image.archive.trim(),
                pin.get(..12).unwrap_or(&pin),
            );
        }
        return match builder.held(image, &pin)? {
            Some(held) => Ok((held, Some(held_evidence(&engine)))),
            None => Err(anyhow::anyhow!(
                "the pinned image for '{}' ({}) is not in this box's store, and this engine \
                 ({engine}) cannot mint it: its builder does not reproduce a pinned recipe. {fixes}",
                image.name,
                pin.get(..12).unwrap_or(&pin),
            )),
        };
    }
    let staging = staging_tag(&image.name);
    let mut staged = image.clone();
    staged.tags = vec![staging.clone()];
    let mut built = builder.build(&staged, repo_root)?;
    let ids = builder.identities(&built);
    let ids = match ids {
        Ok(ids) => ids,
        Err(e) => {
            let _ = builder.untag(&staging);
            return Err(e.context(format!("reading back what {staging} names")));
        }
    };
    if !built_matches_pin(&ids, &pin) {
        builder.untag(&staging).with_context(|| format!("taking the staging tag {staging} off again"))?;
        anyhow::bail!(
            "the image built for '{}' is NOT the pinned one: it answers to {} and the pin is {} — \
             its tags ({}) were NOT moved. The recipe or an input changed since the pin was taken, \
             or the engine's layer cache answered with an older build; re-pin deliberately or \
             mint the pinned image (`toolchain_archive`), never by retagging",
            image.name,
            ids.iter().map(|i| i.get(..12).unwrap_or(i)).collect::<Vec<_>>().join(" / "),
            pin.get(..12).unwrap_or(&pin),
            image.tags.join(", "),
        );
    }
    builder.tag(&built, &image.tags).with_context(|| format!("tagging the verified image {}", image.tags.join(", ")))?;
    builder.untag(&staging).with_context(|| format!("taking the staging tag {staging} off again"))?;
    if let Some(primary) = image.primary_tag() {
        built.image_ref = primary.to_string();
    }
    Ok((built, None))
}

/// The **ISO build seam**: produce (or locate) an [`IsoBuild`]'s mastered `.iso`
/// artifact and return its content hash. Real ISO-9660 mastering is done in **pure
/// Rust** by [`PureRustIsoBuilder`] (no external ISO tool); the default
/// [`PrebuiltIsoBuilder`] hashes an ISO **already produced** on disk (the blessed
/// "hash the produced artifact" path, mirroring [`PrebuiltVmBuilder`]).
pub trait IsoImageBuilder {
    /// Resolve the mastered `.iso` for `iso` under `repo_root` and return its
    /// [`Artifact`] hash (plus any staged `from_variant` payload).
    fn build(&self, iso: &IsoBuild, repo_root: &Path) -> Result<Vec<Artifact>>;

    /// The engine label recorded in provenance — `prebuilt` for the dep-light
    /// [`PrebuiltIsoBuilder`], `pure-rust` for the [`PureRustIsoBuilder`] masterer.
    /// Defaulted so existing impls need no change.
    fn engine(&self) -> &str {
        "prebuilt"
    }
}

/// The **VM build seam**: produce (or locate) a [`VmBuild`]'s kernel + rootfs/disk
/// artifacts and return their content hashes. The heavy Buildroot build stays
/// tunnr's; the default [`PrebuiltVmBuilder`] hashes the artifacts tunnr already
/// produced.
pub trait VmImageBuilder {
    /// Resolve the produced kernel + rootfs/disk artifacts for `vm` under
    /// `repo_root` and return their [`Artifact`] hashes (in kernel, rootfs, disk
    /// order for whichever are present).
    fn build(&self, vm: &VmBuild, repo_root: &Path) -> Result<Vec<Artifact>>;

    /// The engine label recorded in provenance — `prebuilt` for the dep-light
    /// [`PrebuiltVmBuilder`], `tunnr` for a real `build.sh` handoff impl. Defaulted
    /// so existing impls need no change; read by [`build_vm`] so a real VM build is
    /// not mislabeled `prebuilt`.
    fn engine(&self) -> &str {
        "prebuilt"
    }
}

/// A build-provenance record for a container/vm target — the image/vm analogue of
/// [`BuildProvenance`](crate::provenance::BuildProvenance). Fully serde; the seal
/// binds to the PRODUCED-artifact hashes in [`Self::artifacts`].
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TargetProvenance {
    /// Always [`TARGET_PROVENANCE_SCHEMA`].
    pub schema: String,
    /// Stable per-record identity (UUID v4).
    pub id: String,
    /// The repo whose target was built.
    pub repo: String,
    /// The build target kind.
    pub kind: TargetKind,
    /// The target's stable name (image/vm name).
    pub target: String,
    /// The DESCRIPTOR's own `content_id` — the SPEC fingerprint (what was asked
    /// for), kept distinct from the produced-artifact hashes below.
    pub descriptor_id: String,
    /// The full target descriptor, verbatim (a `ContainerBuild` / `VmBuild`).
    pub descriptor: serde_json::Value,
    /// The PRODUCED artifacts, content-addressed — the image digest, or the
    /// kernel+rootfs/disk sha256s. THIS is what the seal binds to.
    pub artifacts: Vec<Artifact>,
    /// The builder engine that produced the artifact (`bollard`, `prebuilt`, …).
    pub engine: String,
    /// The build host (cores + memory).
    pub host: HostInfo,
    /// VCS commit the build was cut from.
    pub vcs_rev: String,
    /// VCS branch (best-effort).
    pub vcs_branch: String,
    /// `true` when the working tree had uncommitted changes at build time.
    pub vcs_dirty: bool,
    /// Build outcome.
    pub outcome: Outcome,
    /// RFC 3339 UTC timestamp of capture.
    pub built_at: String,
    /// Best-effort `user@host` of the builder.
    pub builder: String,
}

impl TargetProvenance {
    /// Capture a record for a built target from its descriptor + produced artifacts.
    fn capture(
        kind: TargetKind,
        target: &str,
        descriptor_id: String,
        descriptor: serde_json::Value,
        repo: &str,
        repo_root: &Path,
        engine: &str,
        artifacts: Vec<Artifact>,
        outcome: Outcome,
    ) -> Self {
        let (vcs_rev, vcs_branch) = provenance::git_head(repo_root);
        let (vcs_dirty, _worktree) = provenance::git_worktree_freshness(repo_root);
        TargetProvenance {
            schema: TARGET_PROVENANCE_SCHEMA.to_string(),
            id: crate::provenance::record_id(),
            repo: repo.to_string(),
            kind,
            target: target.to_string(),
            descriptor_id,
            descriptor,
            artifacts,
            engine: engine.to_string(),
            host: HostInfo::detect(),
            vcs_rev,
            vcs_branch,
            vcs_dirty,
            outcome,
            built_at: chrono::Utc::now().to_rfc3339(),
            builder: provenance::builder_identity(),
        }
    }

    /// Pretty JSON serialization.
    pub fn to_json(&self) -> Result<String> {
        serde_json::to_string_pretty(self).context("serialize target provenance")
    }

    /// Parse a record from JSON.
    pub fn from_json(text: &str) -> Result<Self> {
        serde_json::from_str(text).context("parse target provenance")
    }

    /// The canonical byte string the attestation digest is taken over: compact,
    /// key-sorted JSON so the same record folds to the same bytes anywhere.
    pub fn canonical_bytes(&self) -> Result<Vec<u8>> {
        let value: serde_json::Value =
            serde_json::to_value(self).context("target provenance to value")?;
        let canon = provenance::canonicalize(&value);
        serde_json::to_vec(&canon).context("canonical target provenance bytes")
    }

    /// Fold the record into a [`TargetAttestation`] (canonical bytes + SHA-256 digest).
    pub fn attest(self) -> Result<TargetAttestation> {
        let canonical = self.canonical_bytes()?;
        let digest: [u8; 32] = Sha256::digest(&canonical).into();
        Ok(TargetAttestation { record: self, canonical, digest, seal: None })
    }

    /// **Fold this target record into the ONE record type the `build_provenance`
    /// ledger holds.**
    ///
    /// A container / ISO / VM cell is described by a [`TargetProvenance`], a cargo
    /// cell by a [`BuildProvenance`] — and the warehouse has exactly one build-record
    /// table, fed by exactly one submit
    /// (`Telemetry.SubmitBuildProvenance`). That asymmetry is why an assembled cell
    /// filed nothing at all: there was no record of the right shape to hand the
    /// writer, so the producer returned early and ⚒ Build Thing's records pane stayed
    /// empty however well the ISO mastered.
    ///
    /// This is that missing shape — a projection, **not a second provenance path**.
    /// Every field is carried across rather than re-derived, so what lands is what
    /// the builder actually sealed:
    ///
    /// * `id` is the TARGET record's id verbatim, so the `build_info` lineage rows
    ///   (already keyed by it via [`target_manifest_rows`](crate::build_info::target_manifest_rows))
    ///   join the provenance row instead of forming a second disconnected pile;
    /// * `artifacts` are the PRODUCED artifacts — the image reference + its engine
    ///   digest + size, or the mastered ISO's path + sha256 + size;
    /// * `variant` is the declared cell, so the row names WHICH cell and which
    ///   [`ArtifactKind`](crate::variant::ArtifactKind).
    ///
    /// What has no home in `BuildProvenance` — the `engine`, the `descriptor_id` and
    /// the descriptor itself — is not lost either: `target_manifest_rows` already
    /// lowers all three into `build_info` under the same `build_id`. `cargo_args` is
    /// EMPTY rather than a fabricated argv: no cargo ran for the target itself, and a
    /// plausible-looking lie in a provenance record is worse than a blank.
    pub fn to_build_provenance(&self, variant: &BuildVariant) -> BuildProvenance {
        BuildProvenance {
            schema: provenance::PROVENANCE_SCHEMA.to_string(),
            id: self.id.clone(),
            repo: self.repo.clone(),
            variant: variant.clone(),
            inputs: Inputs {
                cargo_args: Vec::new(),
                features: variant.features.clone(),
                profile: String::from(variant.profile.clone()),
                target: variant.target.clone(),
                rustflags_cfg: variant.cfg.clone(),
                vcs_rev: self.vcs_rev.clone(),
                vcs_branch: self.vcs_branch.clone(),
                vcs_dirty: self.vcs_dirty,
                // The target capture reads `dirty` but not the porcelain digest;
                // an empty string is "not captured", never a forged "clean".
                worktree_digest: String::new(),
                sources: Vec::new(),
            },
            toolchain: Toolchain::detect(),
            host: self.host.clone(),
            outcome: self.outcome,
            artifacts: self.artifacts.clone(),
            built_at: self.built_at.clone(),
            builder: self.builder.clone(),
            // A target build (image / VM / ISO) is not planned by a cargo
            // build-thing variant recipe, so there is no coordinate to name.
            // `None` rather than a plausible-looking one, for the same reason
            // `cargo_args` above is empty.
            build_thing: None,
            builder_id: None,
        }
    }
}

/// An attestation over an image/vm build: the record, the canonical bytes folded,
/// the digest, and (once sealed) the detached seal. Mirrors
/// [`Attestation`](crate::provenance::Attestation) for the image/vm targets.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TargetAttestation {
    /// The provenance record.
    pub record: TargetProvenance,
    /// The exact canonical bytes the digest was taken over.
    #[serde(skip)]
    pub canonical: Vec<u8>,
    /// SHA-256 over [`Self::canonical`]. Serialized as lowercase hex.
    #[serde(with = "crate::provenance::digest_hex")]
    pub digest: [u8; 32],
    /// The detached seal, once applied.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub seal: Option<Seal>,
}

impl TargetAttestation {
    /// Seal this attestation with `sealer`, binding a detached signature to the
    /// [`Self::digest`] (which covers the produced-artifact hashes).
    pub fn seal_with(mut self, sealer: &dyn ProvenanceSealer) -> Result<Self> {
        self.seal = Some(sealer.seal(&self.digest)?);
        Ok(self)
    }

    /// True once a real seal has been applied.
    pub fn is_sealed(&self) -> bool {
        self.seal.is_some()
    }

    /// Lowercase-hex form of the digest.
    pub fn digest_hex(&self) -> String {
        provenance::hex(&self.digest)
    }

    /// Recompute the digest from the record and confirm it still matches — the
    /// tamper check (a mutated produced-artifact hash breaks this).
    pub fn digest_matches_record(&self) -> Result<bool> {
        let canonical = self.record.canonical_bytes()?;
        let digest: [u8; 32] = Sha256::digest(&canonical).into();
        Ok(digest == self.digest)
    }

    /// Serialize the whole attestation (record + digest + seal) to pretty JSON.
    pub fn to_json(&self) -> Result<String> {
        serde_json::to_string_pretty(self).context("serialize target attestation")
    }

    /// Parse an attestation from JSON, recomputing the canonical bytes.
    pub fn from_json(text: &str) -> Result<Self> {
        let mut att: TargetAttestation =
            serde_json::from_str(text).context("parse target attestation")?;
        att.canonical = att.record.canonical_bytes()?;
        Ok(att)
    }
}

/// **Build the cargo variants a target CHAINS and hand back their artifact paths**
/// — the `staged` map every one of the three builders takes through
/// `with_variant_artifacts`.
///
/// A `from_variant = "x"` layer/kernel/payload means *the payload IS the artifact
/// cargo variant `x` produced*, and the renderers emit a context-relative
/// `COPY ./x` (or stage `x` into the ISO/VM tree). Nothing puts `./x` there unless
/// the variant is built first, so this is the step between "the declaration names
/// a cargo variant" and "the image build finds its payload".
///
/// One writer for all three target kinds: pass
/// [`ContainerBuild::variant_deps`](crate::container::ContainerBuild::variant_deps),
/// [`IsoBuild::variant_deps`](crate::iso::IsoBuild::variant_deps) or
/// [`VmBuild::variant_deps`](crate::vm::VmBuild::variant_deps). The path is read
/// off the build's OWN [`BuildOutcome::artifacts`](crate::provenance::BuildOutcome)
/// — what cargo actually wrote — never guessed from `target/<profile>/<bin>`.
///
/// `label` names the target in the error text (`container 'gunnar-scratch': …`).
pub fn stage_variant_artifacts(
    set: &crate::variant::BuildVariantSet,
    label: &str,
    variant_deps: &[&str],
    repo_root: &Path,
    sink: &dyn crate::build_info::BuildInfoSink,
) -> Result<BTreeMap<String, PathBuf>> {
    let mut staged: BTreeMap<String, PathBuf> = BTreeMap::new();
    for vname in variant_deps {
        if staged.contains_key(*vname) {
            continue;
        }
        let v = set.require(vname).with_context(|| {
            format!(
                "{label}: chains cargo variant '{vname}', which this repo does not declare"
            )
        })?;
        set.validate(v)?;
        // The road is the declaration's, not the caller's: a set that pins a
        // toolchain image builds its chained payload INSIDE that image, the same
        // road a standalone cargo variant takes through the forge. MEASURED
        // 2026-09-05 (gunnar, the owner: "building aws, azure etc all is done in
        // containers not on host system — that is a pattern"): every 1.3.0
        // medium's kernel and wasm recipe went through the container road while
        // the `gunnar-server` payload they packed was a HOST build off
        // `target/x86_64-unknown-linux-musl/release`, because this function
        // only knew the host road. A payload whose toolchain is whatever the
        // box has is not the payload the declaration describes.
        // The one override (`NORDISK_CARGO_TOOLCHAIN_IMAGE`) applies here too, so a
        // compose proven in a not-yet-pinned image builds its payload in that image.
        let effective_tc = crate::cargo_container::toolchain_with_override(set.toolchain.clone())?;
        let outcome = match effective_tc.as_ref() {
            Some(tc) => {
                eprintln!(
                    "[nornir] {label}: chained variant '{vname}' builds in {} (the declaration's toolchain)",
                    tc.image_ref()
                );
                chained_variant_in_container(set, vname, repo_root, tc)
                    .with_context(|| format!("{label}: build chained variant '{vname}' in {}", tc.image_ref()))?
            }
            None => provenance::build_variant_with_info(set, vname, repo_root, sink)
                .with_context(|| format!("{label}: build chained variant '{vname}'"))?,
        };
        anyhow::ensure!(
            outcome.success,
            "{label}: chained variant '{vname}' failed to build"
        );
        let path = outcome.artifacts.first().cloned().with_context(|| {
            format!(
                "{label}: chained variant '{vname}' has kind '{:?}', which produced no single \
                 artifact to stage",
                v.kind
            )
        })?;
        staged.insert((*vname).to_string(), path);
    }
    Ok(staged)
}

/// A chained payload built inside the declaration's pinned toolchain image —
/// the same call the forge makes for a standalone cargo variant
/// (`dwarves::build_thing::build_in_container_observed`), so the two roads
/// share one writer: `--locked --offline`, the registry bound read-only, the
/// target dir bound at the container's `/target`.
#[cfg(feature = "cargo-container")]
fn chained_variant_in_container(
    set: &crate::variant::BuildVariantSet,
    name: &str,
    repo_root: &Path,
    tc: &crate::cargo_container::CargoToolchain,
) -> Result<crate::provenance::BuildOutcome> {
    use crate::cargo_container::{build_variant_in_container, DraupnirCargoRunner, HostPaths};
    let paths = HostPaths::from_env(repo_root);
    std::fs::create_dir_all(&paths.target_dir)
        .with_context(|| format!("create the target dir {}", paths.target_dir.display()))?;
    let runner = DraupnirCargoRunner::new();
    build_variant_in_container(set, name, repo_root, &paths.target_dir, paths.registry_bind(), tc, &runner)
}

/// Without the container road compiled in, a toolchain-pinned declaration is
/// REFUSED rather than quietly built on the host: the host road would produce
/// a payload the declaration does not describe, and the receipt would say
/// otherwise.
#[cfg(not(feature = "cargo-container"))]
fn chained_variant_in_container(
    _set: &crate::variant::BuildVariantSet,
    name: &str,
    _repo_root: &Path,
    tc: &crate::cargo_container::CargoToolchain,
) -> Result<crate::provenance::BuildOutcome> {
    anyhow::bail!(
        "chained variant '{name}': the declaration pins the toolchain image {} and this build \
         of nornir-build-thing carries no container road (feature `cargo-container` is off). A \
         host build here would be a payload whose toolchain is whatever the box has, under a \
         receipt that names the image — refused. Enable `cargo-container`, or drop the \
         `[build.toolchain]` pin if a host build is what you mean.",
        tc.image_ref()
    )
}

/// The result of [`build_container`] / [`build_vm`]: the produced artifacts + the
/// unsealed provenance attestation. Mirrors
/// [`BuildOutcome`](crate::provenance::BuildOutcome).
#[derive(Debug, Clone)]
pub struct TargetBuildOutcome {
    /// The target name that was built.
    pub target: String,
    /// The target kind.
    pub kind: TargetKind,
    /// The produced artifacts (image digest / kernel+rootfs hashes).
    pub artifacts: Vec<Artifact>,
    /// The unsealed provenance attestation (seal it via [`TargetAttestation::seal_with`]).
    pub attestation: TargetAttestation,
    /// Whether the build succeeded.
    pub success: bool,
    /// Why it did not, when [`success`](Self::success) is `false` — the builder's own
    /// error, rendered with its context chain.
    ///
    /// The cargo peer has no equivalent field because cargo's reasons reach a caller
    /// as `CompilerMessage`s on the observer stream. A target builder has no such
    /// stream: this string is the ONLY account of why an image or ISO did not build,
    /// and it used to be thrown away with the record it belonged to.
    pub error: Option<String>,
}

impl TargetBuildOutcome {
    /// **This outcome as a hard failure** — `Err` carrying the builder's own reason —
    /// for a caller that wants the build to STOP rather than to be recorded.
    ///
    /// A producer filing provenance wants the record and must not use this. A test or
    /// a script that only proceeds if the artifact exists wants the reason at the
    /// point of failure, and without this it would hand-roll the same three lines and
    /// eventually get them subtly different (LAW 5). Note what it does NOT do: it
    /// consumes the outcome, so the record is gone — which is exactly why it is a
    /// deliberate call and not the default any more.
    pub fn into_result(self) -> Result<Self> {
        match &self.error {
            Some(e) => anyhow::bail!("{} '{}' did not build: {e}", self.kind.as_str(), self.target),
            None => Ok(self),
        }
    }
}

/// **The ONE place a target build's outcome becomes a record** — the writer
/// [`build_container`], [`build_vm`] and [`build_iso`] all route through.
///
/// # Why this exists rather than three copies
///
/// It used to be three. `build_container` captured a `Failed` attestation and then
/// dropped it on the floor with `built?`; `build_vm` and `build_iso` did not even
/// capture one, because their `?` fired before the capture. So a red image or ISO
/// build filed **nothing**, while the cargo arm
/// ([`build_variant_observed_with_info`](crate::provenance::build_variant_observed_with_info))
/// returns `Ok(BuildOutcome { success: false, .. })` and its record is filed like any
/// other. A green-only ledger is the one that cannot answer the question you actually
/// ask it — *what happened the night the build broke* — and three copies of this
/// decision is how the arms drifted apart in the first place (LAW 5).
///
/// # The contract, which this changes deliberately
///
/// A builder error is **no longer propagated**. It is recorded — `Outcome::Failed`,
/// no artifacts, the error text in [`TargetBuildOutcome::error`] — and returned as
/// `Ok`, exactly as the cargo arm returns `Ok` for a red cargo exit. `Ok` here means
/// *the build reached a terminal outcome and there is a record of it*, not *it
/// worked*; [`TargetBuildOutcome::success`] is what says which.
///
/// `Err` is still returned, and now means only what it should: the build never
/// reached an outcome. An invalid descriptor, a descriptor that will not serialise,
/// an attestation that will not fold. There is genuinely nothing to file for those.
fn capture_target_build(
    kind: TargetKind,
    target: &str,
    descriptor_id: String,
    descriptor: serde_json::Value,
    repo: &str,
    repo_root: &Path,
    engine: &str,
    built: Result<Vec<Artifact>>,
) -> Result<TargetBuildOutcome> {
    let (artifacts, outcome, success, error) = match built {
        Ok(artifacts) => (artifacts, Outcome::Success, true, None),
        // `{e:#}` and not `{e}`: the context chain is the useful half of an
        // anyhow error, and this string is the whole account of the failure.
        Err(e) => (Vec::new(), Outcome::Failed, false, Some(format!("{e:#}"))),
    };
    let attestation = TargetProvenance::capture(
        kind,
        target,
        descriptor_id,
        descriptor,
        repo,
        repo_root,
        engine,
        artifacts.clone(),
        outcome,
    )
    .attest()?;
    Ok(TargetBuildOutcome { target: target.to_string(), kind, artifacts, attestation, success, error })
}

/// **Build a container image + seal provenance over the PRODUCED image digest.**
/// The IMAGE-target peer of [`build_variant`](crate::provenance::build_variant):
/// validate the descriptor, build it through `builder` (the seam), then capture an
/// unsealed [`TargetAttestation`] binding the built image's content digest (not the
/// descriptor hash).
///
/// A builder failure is **recorded, not propagated** — see
/// [`capture_target_build`] for the contract and why it changed.
pub fn build_container(
    builder: &dyn ImageBuilder,
    image: &ContainerBuild,
    repo: &str,
    repo_root: &Path,
) -> Result<TargetBuildOutcome> {
    // `validate_in`, not `validate`: a `containerfile = "…"` cell whose file is not
    // in this checkout must be refused BEFORE anything is captured, so the outcome is
    // an honest "the declaration is broken" rather than a recorded engine failure
    // several minutes downstream. Synthesis-mode cells touch no disk and are
    // unaffected.
    image.validate_in(repo_root).context("validate container descriptor")?;
    let (built, held) = if image.expected_sha256.trim().is_empty() {
        (builder.build(image, repo_root), None)
    } else {
        match build_pinned(builder, image, repo_root) {
            Ok((b, held)) => (Ok(b), held),
            Err(e) => (Err(e), None),
        }
    };
    // A held image is recorded AS held: the record's `engine` is what a release reads to tell
    // a minted pin from a couriered one, and the line is the step's own account of it.
    let engine = match &held {
        Some(h) => {
            eprintln!(
                "   pinned image '{}': {h} — {} answers to the pin {}",
                image.name,
                image.tags.join(", "),
                image.expected_sha256.trim().trim_start_matches("sha256:").get(..12).unwrap_or(""),
            );
            format!("{} — {h}", builder.engine())
        }
        None => builder.engine().to_string(),
    };
    capture_target_build(
        TargetKind::Container,
        &image.name,
        image.content_id(),
        serde_json::to_value(image).context("container descriptor to value")?,
        repo,
        repo_root,
        &engine,
        built.map(|b| b.to_artifacts()),
    )
}

/// **Seal provenance over a VM's PRODUCED kernel+rootfs/disk artifacts.** The
/// VM-target peer of [`build_variant`](crate::provenance::build_variant): validate
/// the descriptor, resolve the produced artifacts through `builder` (the default
/// [`PrebuiltVmBuilder`] hashes what tunnr already built), then capture an unsealed
/// [`TargetAttestation`] binding those file hashes.
pub fn build_vm(
    builder: &dyn VmImageBuilder,
    vm: &VmBuild,
    repo: &str,
    repo_root: &Path,
) -> Result<TargetBuildOutcome> {
    vm.validate().context("validate vm descriptor")?;
    capture_target_build(
        TargetKind::Vm,
        &vm.name,
        vm.content_id(),
        serde_json::to_value(vm).context("vm descriptor to value")?,
        repo,
        repo_root,
        builder.engine(),
        builder.build(vm, repo_root).context("resolve vm artifacts"),
    )
}

/// **Seal provenance over an ISO's PRODUCED `.iso` artifact.** The ISO-target peer
/// of [`build_vm`]: validate the descriptor, resolve the mastered ISO through
/// `builder` (the default [`PrebuiltIsoBuilder`] hashes an ISO already mastered on
/// disk), then capture an unsealed [`TargetAttestation`] binding that file hash. The
/// provenance `kind` is [`TargetKind::Iso`].
pub fn build_iso(
    builder: &dyn IsoImageBuilder,
    iso: &IsoBuild,
    repo: &str,
    repo_root: &Path,
) -> Result<TargetBuildOutcome> {
    build_iso_as(builder, iso, TargetKind::Iso, repo, repo_root)
}

/// [`build_iso`] with the provenance `kind` CHOSEN by the caller — the seam
/// that lets an INSTALLER composition record `TargetKind::Installer` instead
/// of `Iso`. Until this existed the attestation spelled both artifacts
/// `"iso"`, so an auditor could not tell a bootable product from the tool
/// that installs it (the gap analysis's item 20, verbatim).
pub fn build_iso_as(
    builder: &dyn IsoImageBuilder,
    iso: &IsoBuild,
    kind: TargetKind,
    repo: &str,
    repo_root: &Path,
) -> Result<TargetBuildOutcome> {
    iso.validate().context("validate iso descriptor")?;
    // Applied staged-file parameters land on the staging tree BEFORE mastering
    // — the per-site hub.conf becomes a real file the ISO carries, and the
    // same applied values already ride the descriptor serialized below (the
    // provenance: which ISO holds which hub key, readable off the record).
    for (rel, contents) in iso.staged_param_files() {
        let dst = repo_root.join(&iso.staging).join(&rel);
        if let Some(parent) = dst.parent() {
            std::fs::create_dir_all(parent)
                .with_context(|| format!("create {}", parent.display()))?;
        }
        std::fs::write(&dst, contents)
            .with_context(|| format!("land staged parameter file {}", dst.display()))?;
    }
    capture_target_build(
        kind,
        &iso.name,
        iso.content_id(),
        serde_json::to_value(iso).context("iso descriptor to value")?,
        repo,
        repo_root,
        builder.engine(),
        builder.build(iso, repo_root).context("resolve iso artifact"),
    )
}

/// **The one allowance for a seeded `/data`**: set to anything, and the
/// appliance builder grafts whatever the staging tree holds under `data/`
/// instead of refusing it. The forge's own e2e sets it; a release compose never
/// does, so a release medium cannot carry a fixture control store or a
/// test-minted key by accident.
///
/// It lives HERE, beside [`iso_output_path`], rather than beside the refusal it
/// gates, because it decides two things that must never disagree: **whether the
/// seed is grafted** and **what the medium is called**. MEASURED 2026-09-05
/// (gunnar): the seed was refused correctly and the fixture medium was still
/// written to `.iso-out/gunnar-appliance-iso.iso` — the release name — so for
/// four minutes the only thing saying it was a fixture was a file inside it.
pub const APPLIANCE_SEED_FIXTURE_ENV: &str = "KORP_APPLIANCE_SEED_FIXTURE";

/// What a fixture medium's file name carries that a release medium's does not.
pub const FIXTURE_ISO_SUFFIX: &str = "-fixture";

/// Whether this process has declared itself a FIXTURE compose, by
/// [`APPLIANCE_SEED_FIXTURE_ENV`]. The single reader of that variable: every
/// consequence of a fixture run — the graft, the medium's name, the scratch
/// directory — hangs off this one call, so none of them can be true while
/// another is false.
pub fn appliance_seed_fixture_requested() -> bool {
    std::env::var_os(APPLIANCE_SEED_FIXTURE_ENV).is_some()
}

/// The file stem an ISO cell's outputs are named from: the cell's own name, or
/// the cell's name plus [`FIXTURE_ISO_SUFFIX`] for a fixture compose. Pure —
/// [`iso_output_path`] is this with the environment read for `fixture`, and the
/// scratch directory (`<stem>.work`) is the same stem, so a fixture run cannot
/// leave a seeded ESP behind under the release name either.
pub fn iso_out_stem(iso_name: &str, fixture: bool) -> String {
    if fixture {
        format!("{iso_name}{FIXTURE_ISO_SUFFIX}")
    } else {
        iso_name.to_string()
    }
}

/// The conventional output path a mastered ISO lands at: `<repo_root>/.iso-out/<name>.iso`,
/// or `<repo_root>/.iso-out/<name>-fixture.iso` when this run is a fixture compose.
///
/// The forge masters to this path and the buildgate expects a product at this
/// path, both by calling this function — so the fixture name is not something a
/// caller opts into and can forget. A release compose never sets the variable,
/// so it gets the unchanged name and can neither write to the fixture name nor
/// read from it.
pub fn iso_output_path(repo_root: &Path, iso: &IsoBuild) -> PathBuf {
    repo_root
        .join(".iso-out")
        .join(format!("{}.iso", iso_out_stem(&iso.name, appliance_seed_fixture_requested())))
}

/// **Seal provenance over a built kernel's PRODUCED artifacts** (`bzImage`,
/// `.config`, the sha256 pin file). The KERNEL-target peer of [`build_iso`]:
/// validate the descriptor, compile through `builder` (the
/// [`KernelBuilder`](crate::kernel::KernelBuilder) seam — the container-driving
/// [`ContainerKernelBuilder`](crate::kernel::ContainerKernelBuilder) in
/// production, a fake in tests), then capture an unsealed [`TargetAttestation`]
/// binding those file hashes. The record's `kind` is [`TargetKind::Kernel`] and
/// its descriptor carries version + tarball sha256 + profile — the receipt an
/// auditor reads the pin off.
///
/// A builder failure is **recorded, not propagated** — see
/// [`capture_target_build`] for the contract.
pub fn build_kernel(
    builder: &dyn crate::kernel::KernelBuilder,
    kernel: &crate::kernel::KernelBuild,
    repo: &str,
    repo_root: &Path,
) -> Result<TargetBuildOutcome> {
    // `validate_in`, not `validate`: a declared `kconfig_fragment` missing from
    // this checkout is refused BEFORE anything is fetched or captured (the
    // container-kind precedent).
    kernel.validate_in(repo_root).context("validate kernel descriptor")?;
    capture_target_build(
        TargetKind::Kernel,
        &kernel.name,
        kernel.content_id(),
        serde_json::to_value(kernel).context("kernel descriptor to value")?,
        repo,
        repo_root,
        builder.engine(),
        builder.build(kernel, repo_root).context("compile kernel"),
    )
}

/// **Assemble a distribution package + seal provenance over the PRODUCED BYTES.**
///
/// The package peer of [`build_kernel`] and [`build_container`], and the place the
/// provenance rule this kind exists for is ENFORCED rather than documented:
///
/// 1. `validate_in` — a cell naming a source directory that is not in this
///    checkout is refused BEFORE a container starts (the container-kind
///    precedent);
/// 2. the builder produces the distributions;
/// 3. [`write_sha256sums`](crate::package::write_sha256sums) writes the manifest —
///    the ONE writer, so this door and the repo's own publish verb leave the same
///    bytes;
/// 4. [`verify_produced`](crate::package::verify_produced) reads the DIRECTORY back
///    and refuses if a declared member is not there;
/// 5. the attestation is captured over the paths step 4 returned.
///
/// # The order of 3 and 4 is the point
///
/// The manifest is written BEFORE the set is verified, because `SHA256SUMS` is
/// itself a required member: verifying first would refuse every build for missing
/// a file only this function writes. And the verification then reads the manifest
/// it just wrote as evidence, which is exactly right — it is on disk or it is not.
///
/// # What is hashed
///
/// The DISTRIBUTIONS, as they landed. `capture_target_build` receives
/// [`Artifact`]s built by [`Artifact::hash_file`] over the produced paths — never
/// the descriptor, never the recipe. That distinction is the whole reason this
/// kind is not a `Wasm` with extra fields: a record built from the declaration
/// describes what was ASKED FOR, and facett measured two builds of one declaration
/// differing by 1.7 MB when a feature flipped.
///
/// A builder failure is **recorded, not propagated** — see [`capture_target_build`].
pub fn build_package(
    builder: &dyn crate::package::PackageBuilder,
    pkg: &crate::package::PackageBuild,
    repo: &str,
    repo_root: &Path,
) -> Result<TargetBuildOutcome> {
    pkg.validate_in(repo_root).context("validate package descriptor")?;
    let dist = pkg.out_dir(repo_root);
    let produced = builder
        .build(pkg, repo_root)
        .context("assemble package")
        .and_then(|dists| {
            // ── THE ORDER, and it is load-bearing ────────────────────────────
            // `SHA256SUMS` and `provenance.json` are BOTH required members, so
            // both have to be written before the set is verified — verifying
            // first would refuse every build for missing files only this function
            // writes. And they are written HERE, by this crate, so the forge door
            // and a repo's own publish verb leave the same release behind.
            //
            // This is where the end-to-end test earned its place: the manifest
            // writer worked and the verification worked, and together they were a
            // door that could NEVER succeed, because each assumed the other had
            // written the provenance. Nothing but running the whole thing could
            // have shown that.
            crate::package::write_sha256sums(pkg.format, &dist)
                .with_context(|| format!("write the checksum manifest in {}", dist.display()))?;
            crate::package::write_provenance(
                pkg,
                &crate::package::variant_for(pkg),
                repo,
                repo_root,
                // The toolchain that produced the distributions is the CONTAINER's,
                // named by the engine and the pinned image. `Toolchain::detect()`
                // would attest to the host's rustc, which did not build this.
                crate::provenance::Toolchain {
                    rustc: format!(
                        "{} in {}",
                        builder.engine(),
                        pkg.image.as_deref().unwrap_or("<no pinned image>")
                    ),
                    host: String::new(),
                    cargo: String::new(),
                    ..Default::default()
                }
                // The image also goes in the FIELD, not only inside a prose
                // `rustc` line. A sentence cannot be queried; a field can, and
                // `to_slsa_statement` emits it as a material where SLSA puts
                // the things a build consumed.
                .with_container_image(
                    pkg.image.as_deref().map(|r| {
                        r.split_once("@sha256:").map_or_else(|| r.to_string(), |(n, _)| n.to_string())
                    }),
                    pkg.image
                        .as_deref()
                        .and_then(|r| r.split_once("@sha256:").map(|(_, d)| d.to_string())),
                ),
                &dists,
            )
            .with_context(|| format!("write the provenance record in {}", dist.display()))?;
            crate::package::verify_produced(pkg, &dist)
        })
        .and_then(|paths| {
            paths
                .iter()
                .map(|p| Artifact::hash_file(repo_root, p))
                .collect::<Result<Vec<Artifact>>>()
        });
    capture_target_build(
        TargetKind::Package,
        &pkg.name,
        pkg.content_id(),
        serde_json::to_value(pkg).context("package descriptor to value")?,
        repo,
        repo_root,
        builder.engine(),
        produced,
    )
}

/// The conventional path a built kernel's boot image lands at:
/// `<repo_root>/.kernel-out/<name>/bzImage` (beside `kernel.config` and
/// `<name>.kernel-pin.json` — [`kernel_output_dir`](crate::kernel::kernel_output_dir)).
pub fn kernel_output_path(repo_root: &Path, kernel: &crate::kernel::KernelBuild) -> PathBuf {
    crate::kernel::kernel_output_dir(repo_root, kernel).join("bzImage")
}

/// The dep-light default [`IsoImageBuilder`]: seal provenance over the mastered
/// `.iso` an ISO build **already produced** (real xorriso mastering runs elsewhere).
/// Hashes the conventional [`iso_output_path`] (or an override staged via
/// [`with_output`](Self::with_output)); errors if no ISO is present (nothing to
/// seal). A `from_variant` payload staged via
/// [`with_variant_artifacts`](Self::with_variant_artifacts) is hashed FIRST so the
/// seal binds the freshly-built app binary that rode onto the ISO — the exact peer
/// of [`PrebuiltVmBuilder`]'s `from_variant` staging.
#[derive(Default)]
pub struct PrebuiltIsoBuilder {
    /// An explicit produced-ISO path override (else the conventional `.iso-out/<name>.iso`).
    output: Option<PathBuf>,
    /// Cargo-variant artifacts staged for a `from_variant` chain.
    staged: BTreeMap<String, PathBuf>,
}

impl PrebuiltIsoBuilder {
    /// A builder resolving the conventional output path.
    pub fn new() -> Self {
        Self::default()
    }

    /// Override the produced-ISO path to hash (else `<repo_root>/.iso-out/<name>.iso`).
    pub fn with_output(mut self, output: PathBuf) -> Self {
        self.output = Some(output);
        self
    }

    /// Stage the cargo-variant artifacts a `from_variant` ISO chains: variant name →
    /// the produced artifact path on the host.
    pub fn with_variant_artifacts(mut self, staged: BTreeMap<String, PathBuf>) -> Self {
        self.staged = staged;
        self
    }
}

impl IsoImageBuilder for PrebuiltIsoBuilder {
    fn build(&self, iso: &IsoBuild, repo_root: &Path) -> Result<Vec<Artifact>> {
        let mut artifacts = Vec::new();
        // `from_variant` staging: hash the app binary that rode onto the ISO FIRST,
        // so provenance binds the freshly-built payload, not just the ISO image.
        if let Some(variant) = &iso.from_variant {
            let src = self.staged.get(variant).with_context(|| {
                format!(
                    "iso '{}': chains cargo variant '{variant}' via `from_variant` but no built \
                     artifact was staged for it — build the variant first and pass it via \
                     `with_variant_artifacts`",
                    iso.name
                )
            })?;
            let abs = if src.is_absolute() { src.clone() } else { repo_root.join(src) };
            artifacts.push(Artifact::hash_file(repo_root, &abs)?);
        }
        let out = self
            .output
            .clone()
            .unwrap_or_else(|| iso_output_path(repo_root, iso));
        let abs = if out.is_absolute() { out.clone() } else { repo_root.join(&out) };
        if abs.is_file() {
            artifacts.push(Artifact::hash_file(repo_root, &abs)?);
        }
        if artifacts.is_empty() {
            anyhow::bail!(
                "iso '{}': no produced .iso artifact found on disk to seal at {} \
                 (master it first — the heavy ISO build is the consumer's)",
                iso.name,
                abs.display()
            );
        }
        Ok(artifacts)
    }
}

/// **The pure-Rust [`IsoImageBuilder`]** (engine `pure-rust`): masters a bootable
/// ISO-9660 + El-Torito image with **ZERO external process** via
/// [`crate::iso_writer::IsoWriter`], then seals provenance over the produced `.iso`
/// (and any `from_variant` payload) by reusing [`PrebuiltIsoBuilder`]'s hashing —
/// one home for the sealing, so this builder adds only the *produce* step.
///
/// This is the law-compliant replacement for the deleted `XorrisoIsoBuilder`: it
/// does **not** shell out to `xorriso` / `mkisofs` / `genisoimage` (or any binary).
/// The ISO structures — Primary Volume Descriptor, path tables, directory records,
/// and the El-Torito boot catalog with a UEFI (`0xEF`) section entry pointing at
/// the `efiboot.img` EFI System Partition — are emitted byte-for-byte from Rust.
///
/// The descriptor maps onto the writer exactly as the old `xorriso -as mkisofs`
/// argv did: the `staging` tree is grafted at the volume root, the `kernel` /
/// `rootfs` are placed at their relative ISO paths, the `kernel` is registered as
/// the BIOS (`-b`) El-Torito boot image, and a UEFI/hybrid `format` registers
/// `efiboot.img` as the EFI (`-e … -no-emul-boot`) boot image. The kernel
/// command-line (`console=ttyS0 …`) rides inside the staged bootloader config
/// (grub.cfg in the ESP), exactly as it did under `xorriso` — the writer masters
/// the filesystem, the staged bootloader consumes the cmdline.
#[derive(Default)]
pub struct PureRustIsoBuilder {
    /// An explicit output-ISO path (else the conventional `.iso-out/<name>.iso`).
    output: Option<PathBuf>,
    /// Cargo-variant artifacts staged for a `from_variant` chain (sealed too).
    staged: BTreeMap<String, PathBuf>,
    /// A real legacy-BIOS stage to lay as the El-Torito default entry, patched
    /// with the payload placements — see [`crate::iso_writer::IsoWriter::set_bios_stage`].
    bios_stage: Option<crate::iso_writer::BiosStage>,
    /// The 440 bytes of MBR boot code a HYBRID medium carries at LBA 0 — see
    /// [`crate::iso_writer::IsoWriter::set_hybrid_mbr`]. The CALLER assembles
    /// them (this crate cannot depend on the assembler that does).
    hybrid_mbr: Option<Vec<u8>>,
    /// Lay the GPT ESP partition whatever the ESP's size
    /// ([`crate::iso_writer::GptHybrid::Always`]) — the declaration's
    /// `medium_boot = "gpt"`: a medium that boots by UEFI off a cdrom AND off a
    /// disk it was imported as, with no legacy stage and no boot code at LBA 0.
    gpt_always: bool,
}

impl PureRustIsoBuilder {
    /// A builder that masters to the conventional [`iso_output_path`].
    pub fn new() -> Self {
        Self::default()
    }

    /// **Lay a real legacy-BIOS stage** as the El-Torito default (80x86) entry
    /// beside the EFI section: the staging tree must carry `stage.image`,
    /// `stage.kernel` and (if named) `stage.initrd`; the writer fills the
    /// stage's patch table with their placements and `stage.cmdline`. One
    /// medium, two firmware paths; without this a UEFI ISO gets the refusal stub.
    pub fn with_bios_stage(mut self, stage: crate::iso_writer::BiosStage) -> Self {
        self.bios_stage = Some(stage);
        self
    }

    /// **Make the mastered medium HYBRID** — bootable as a DISK as well as as
    /// a cdrom. `mbr_code` is
    /// `korp_boot_bios::emit_mbr(`[`iso_writer::HYBRID_STAGE_LBA`](crate::iso_writer::HYBRID_STAGE_LBA)`,
    /// stage_sectors)`. Refused at master time without
    /// [`with_bios_stage`](Self::with_bios_stage).
    pub fn with_hybrid_mbr(mut self, mbr_code: Vec<u8>) -> Self {
        self.hybrid_mbr = Some(mbr_code);
        self
    }

    /// **Lay the GPT ESP partition unconditionally** — the declaration's
    /// `medium_boot = "gpt"`. UEFI firmware then boots the medium off a disk
    /// (a USB stick, a cloud that imports the image as a disk — Azure Gen2)
    /// through the GPT, and off a cdrom through El Torito, with no legacy
    /// stage and nothing at LBA 0 a BIOS would run. Under the El Torito ceiling
    /// the medium's shape otherwise stays as it was.
    pub fn with_gpt_always(mut self) -> Self {
        self.gpt_always = true;
        self
    }

    /// Override the produced-ISO output path (else `<repo_root>/.iso-out/<name>.iso`).
    pub fn with_output(mut self, output: PathBuf) -> Self {
        self.output = Some(output);
        self
    }

    /// Stage the cargo-variant artifacts a `from_variant` ISO chains (sealed FIRST,
    /// like [`PrebuiltIsoBuilder::with_variant_artifacts`]).
    pub fn with_variant_artifacts(mut self, staged: BTreeMap<String, PathBuf>) -> Self {
        self.staged = staged;
        self
    }

    /// The resolved output-ISO path under `repo_root`.
    fn out_path(&self, iso: &IsoBuild, repo_root: &Path) -> PathBuf {
        let out = self.output.clone().unwrap_or_else(|| iso_output_path(repo_root, iso));
        if out.is_absolute() { out } else { repo_root.join(out) }
    }

    /// Assemble the [`IsoWriter`] tree from the descriptor (no I/O beyond staging
    /// the file list) and master the `.iso` to `out`. Pure Rust, no subprocess.
    fn master(&self, iso: &IsoBuild, repo_root: &Path, out: &Path) -> Result<()> {
        use crate::iso_writer::IsoWriter;

        let mut w = IsoWriter::new(iso.volume_id.clone());

        // The staging tree is grafted at the volume root (mkisofs' final path arg).
        // A DECLARED tree that is not on disk is a hard error, not a skip — see
        // `IsoBuild::staging_tree` for the empty ISO that used to master clean.
        if let Some(staging) = iso.staging_tree(repo_root)? {
            w.add_dir_recursive("", &staging).with_context(|| {
                format!("iso '{}': stage tree {}", iso.name, staging.display())
            })?;
        }

        // The kernel rides at its relative ISO path and is the BIOS El-Torito
        // boot image (the old `-b <kernel> -no-emul-boot`).
        if !iso.kernel.is_empty() {
            let src = repo_root.join(&iso.kernel);
            w.add_file(&iso.kernel, &src)
                .with_context(|| format!("iso '{}': add kernel {}", iso.name, src.display()))?;
            w.set_bios_boot(&iso.kernel, 4);
        }

        // The rootfs/initramfs rides at its relative ISO path (data payload).
        if !iso.rootfs.is_empty() {
            let src = repo_root.join(&iso.rootfs);
            w.add_file(&iso.rootfs, &src)
                .with_context(|| format!("iso '{}': add rootfs {}", iso.name, src.display()))?;
        }

        // A UEFI/hybrid ISO boots the `efiboot.img` EFI System Partition off the
        // El-Torito EFI (0xEF) section — the old `-eltorito-alt-boot -e efiboot.img
        // -no-emul-boot`. The ESP is provided by the staging tree (as with xorriso);
        // if it is missing the writer errors clearly rather than faking a boot.
        //
        // And it is laid as a GPT HYBRID the moment the ESP outgrows El-Torito's
        // u16 sector count (`iso_writer::GptHybrid::WhenEspExceedsElTorito`): a
        // protective MBR + GPT whose one EFI System partition spans the whole
        // `efiboot.img`, so the medium boots off USB/disk through the GPT with no
        // 32 MiB ceiling, and the clamped El-Torito entry is RECORDED on the
        // layout line below rather than silently cut. Under the ceiling the medium
        // is byte-identical to what this masterer always produced. Decided HERE,
        // in the one masterer every composition routes through (the appliance
        // forge, the installer composer, a plain `[[build.iso]]`), so no
        // composer carries its own ceiling arithmetic. MEASURED 2026-08-22 (oden):
        // korp-appliance-iso's ESP is 88 385 024 B — 2.6× the ceiling — and
        // shrinks by no wish.
        if iso.format.is_uefi() {
            w.set_efi_boot("efiboot.img");
            w.set_gpt_hybrid(if self.gpt_always {
                crate::iso_writer::GptHybrid::Always
            } else {
                crate::iso_writer::GptHybrid::WhenEspExceedsElTorito
            });
        }

        // A real legacy-BIOS stage (the composer stages it; the writer patches
        // it): the default El-Torito entry becomes a loader that reads the
        // kernel + initramfs off the volume by the LBAs the writer just chose.
        if let Some(stage) = &self.bios_stage {
            w.set_bios_stage(stage.clone());
        }

        // The HYBRID boot sector — the declaration's (`medium_boot = "hybrid"`)
        // consequence, carried here by the composer that assembled the code.
        if let Some(code) = &self.hybrid_mbr {
            w.set_hybrid_mbr(code.clone());
        }

        w.write(out)
            .with_context(|| format!("iso '{}': master pure-Rust ISO to {}", iso.name, out.display()))?;
        // The receipt line: what was LAID, from the writer's applied layout — a
        // hybrid says so by name, a plain medium says its catalog count.
        if let Some(layout) = w.layout() {
            eprintln!("   · iso '{}': {}", iso.name, layout.describe());
        }
        Ok(())
    }
}

impl IsoImageBuilder for PureRustIsoBuilder {
    fn engine(&self) -> &str {
        "pure-rust"
    }

    fn build(&self, iso: &IsoBuild, repo_root: &Path) -> Result<Vec<Artifact>> {
        // 1. resolve the output path + ensure its parent dir exists.
        let out = self.out_path(iso, repo_root);
        if let Some(parent) = out.parent() {
            std::fs::create_dir_all(parent).with_context(|| {
                format!("iso '{}': create output dir {}", iso.name, parent.display())
            })?;
        }

        // 2. master the bootable ISO in PURE RUST — no `xorriso`, no subprocess.
        self.master(iso, repo_root, &out)?;

        if !out.is_file() {
            anyhow::bail!(
                "iso '{}': pure-Rust master reported success but produced no ISO at {}",
                iso.name,
                out.display()
            );
        }

        // 3. seal provenance over the freshly-mastered ISO (+ staged from_variant
        //    payload) via the reused prebuilt hashing — one home for the sealing.
        PrebuiltIsoBuilder::new()
            .with_output(out)
            .with_variant_artifacts(self.staged.clone())
            .build(iso, repo_root)
    }
}

/// The dep-light default [`VmImageBuilder`]: seal provenance over the kernel +
/// rootfs/disk artifacts a VM build **already produced** (tunnr's heavy Buildroot
/// build runs elsewhere). Hashes each of `kernel` / `rootfs` / `disk` that resolves
/// to a real file under `repo_root`; errors if none is present (nothing to seal).
///
/// **`from_variant` staging.** When the descriptor chains a cargo build via
/// [`VmBuild::from_variant`](crate::vm::VmBuild::from_variant) — the "build it,
/// then boot it" chain — the produced artifact of that variant (e.g. the PID-1
/// binary that becomes `/init` in the initramfs) is staged in via
/// [`with_variant_artifacts`](Self::with_variant_artifacts) and hashed as a sealed
/// produced-artifact **first**, so the seal binds the freshly-built app binary and
/// not merely a stand-in kernel/rootfs. A `from_variant` set with nothing staged is
/// a hard error — the chain is unsatisfied, never silently skipped. This is the
/// exact peer of [`BollardImageBuilder::with_variant_artifacts`] on the image path.
///
/// The real tunnr `build.sh` invocation that *produces* the kernel+rootfs is a
/// heavy handoff that stays tunnr's (`tunnr-build`'s `BuildShVmBuilder`, engine
/// `tunnr`, wraps this for the sealing); this dep-light path seals what is on disk.
#[derive(Default)]
pub struct PrebuiltVmBuilder {
    /// Cargo-variant artifacts staged for a `from_variant` chain: variant name →
    /// the produced artifact path on the host.
    staged: BTreeMap<String, PathBuf>,
}

impl PrebuiltVmBuilder {
    /// A builder with no staged variant artifacts (the plain prebuilt path).
    pub fn new() -> Self {
        Self::default()
    }

    /// Stage the cargo-variant artifacts a `from_variant` VM chains: a map from
    /// variant name → the produced artifact path on the host. A VM whose
    /// `from_variant` names a staged variant then seals that produced binary as its
    /// boot payload (the "build the app, then boot it" chain honoured).
    pub fn with_variant_artifacts(mut self, staged: BTreeMap<String, PathBuf>) -> Self {
        self.staged = staged;
        self
    }
}

impl VmImageBuilder for PrebuiltVmBuilder {
    fn build(&self, vm: &VmBuild, repo_root: &Path) -> Result<Vec<Artifact>> {
        let mut artifacts = Vec::new();
        // `from_variant` staging: the boot payload produced by a cargo build (the
        // PID-1 binary) is hashed FIRST as a sealed produced-artifact, so provenance
        // binds the freshly-built app, not just the kernel/rootfs it rides in.
        if let Some(variant) = &vm.from_variant {
            let src = self.staged.get(variant).with_context(|| {
                format!(
                    "vm '{}': chains cargo variant '{variant}' via `from_variant` but no built \
                     artifact was staged for it — build the variant first and pass it via \
                     `with_variant_artifacts`",
                    vm.name
                )
            })?;
            let abs = if src.is_absolute() { src.clone() } else { repo_root.join(src) };
            artifacts.push(Artifact::hash_file(repo_root, &abs)?);
        }
        for rel in [vm.kernel.as_str(), vm.rootfs.as_str(), vm.disk.as_str()] {
            if rel.is_empty() {
                continue;
            }
            let p = Path::new(rel);
            let abs = if p.is_absolute() { p.to_path_buf() } else { repo_root.join(p) };
            if abs.is_file() {
                artifacts.push(Artifact::hash_file(repo_root, &abs)?);
            }
        }
        if artifacts.is_empty() {
            anyhow::bail!(
                "vm '{}': no produced kernel/rootfs/disk artifact found on disk to seal \
                 (build them first — the heavy build is tunnr's)",
                vm.name
            );
        }
        Ok(artifacts)
    }
}

// ---------------------------------------------------------------------------
// Real container image builder — bollard (podman/Docker REST API), ZERO-SHELL.
// ---------------------------------------------------------------------------

/// **The MANIFEST digest out of an engine's `RepoDigests`** — the `sha256:…`
/// half of the first `<repo>@sha256:<digest>` entry, or `None` when the engine
/// reported none.
///
/// Pure, and deliberately outside the `image-build` feature so its behaviour is
/// provable without a socket. Two callers share it: the build path (which then
/// falls back to the image id, because a provenance record must say *something*
/// about what it built) and
/// [`BollardImageBuilder::manifest_digest`] (which must not, because its answer
/// becomes a pin).
#[must_use]
pub fn manifest_digest_of(repo_digests: Option<&[String]>) -> Option<String> {
    repo_digests?
        .first()
        // `repo@sha256:…` → take the digest half.
        .and_then(|rd| rd.rsplit_once('@'))
        .map(|(_, d)| d.to_string())
}

/// **The podman API socket URL the image builder connects to** — the pure
/// ladder behind `BollardImageBuilder::socket_url`, every input passed in.
///
/// `DOCKER_HOST` first; then the session's own `XDG_RUNTIME_DIR`; then the
/// runtime directory logind makes for the REAL uid of this process,
/// `/run/user/<uid>`. An empty variable counts as unset (an empty
/// `XDG_RUNTIME_DIR` would otherwise point at `/podman/podman.sock`).
///
/// The third rung used to be the literal `/run/user/1000` (kit-defects (3),
/// 2026-10-05): with no `XDG_RUNTIME_DIR` — a systemd system unit with `User=`,
/// an `ssh box cmd` with no login session — the builder looked in ANOTHER
/// account's runtime directory whenever the build user was not the desktop's
/// first user, and failed with EACCES or ENOENT naming a socket that was never
/// this process's. draupnir's `engine_socket_url_from` made the same move for
/// the container engine; this is the image builder's copy of the ladder, kept
/// here because the `image-build` feature does not link draupnir. Unlike
/// draupnir's, a missing uid is refused by name rather than pointed at the
/// system socket: a rootless build that silently talks to root's podman is not
/// the build that was asked for.
///
/// # Errors
///
/// None of the three is known — no `DOCKER_HOST`, no `XDG_RUNTIME_DIR`, and no
/// uid (no `/proc`).
pub fn podman_socket_url_from(
    docker_host: Option<&str>,
    xdg_runtime_dir: Option<&str>,
    uid: Option<u32>,
) -> Result<String> {
    let set = |v: Option<&str>| v.map(str::trim).filter(|v| !v.is_empty()).map(str::to_string);
    if let Some(h) = set(docker_host) {
        return Ok(h);
    }
    if let Some(xdg) = set(xdg_runtime_dir) {
        return Ok(format!("unix://{}/podman/podman.sock", xdg.trim_end_matches('/')));
    }
    match uid {
        Some(uid) => Ok(format!("unix:///run/user/{uid}/podman/podman.sock")),
        None => anyhow::bail!(
            "no podman API socket to connect to: DOCKER_HOST is unset, XDG_RUNTIME_DIR is unset, \
             and this process's uid could not be read from /proc/self/status — so there is no \
             runtime directory to look in. Set XDG_RUNTIME_DIR (a login session sets it; \
             `loginctl enable-linger <user>` keeps /run/user/<uid> for a service) or point \
             DOCKER_HOST at the socket (unix:///run/user/<uid>/podman/podman.sock)"
        ),
    }
}

/// **The REAL uid of this process**, read from `/proc/self/status` — no `id -u`,
/// no FFI. `None` off Linux or without `/proc`.
#[must_use]
pub fn real_uid() -> Option<u32> {
    real_uid_from_status(&std::fs::read_to_string("/proc/self/status").ok()?)
}

/// The real uid out of `/proc/<pid>/status` text: the first field of the `Uid:`
/// line (`real effective saved filesystem`). The REAL uid, because that is the
/// uid logind created `/run/user/<uid>` for.
fn real_uid_from_status(status: &str) -> Option<u32> {
    status
        .lines()
        .find_map(|l| l.strip_prefix("Uid:"))
        .and_then(|rest| rest.split_whitespace().next())
        .and_then(|uid| uid.parse().ok())
}

/// The production [`ImageBuilder`]: build a [`ContainerBuild`] over the
/// podman/Docker REST API via bollard (feature `image-build`) — no `podman build`
/// subprocess. It streams the descriptor's Containerfile + on-disk layer sources as
/// an in-memory tar build context to the engine, then inspects the built image for
/// its real content digest — the produced-artifact hash the provenance seals.
#[cfg(feature = "image-build")]
pub struct BollardImageBuilder {
    docker: bollard::Docker,
    rt: tokio::runtime::Runtime,
    /// Cargo-variant artifacts to stage into the build context: variant name → the
    /// produced artifact path on the host. A `from_variant` image layer is staged
    /// under its variant name so a variant-chained image build finds its payload.
    staged: BTreeMap<String, PathBuf>,
}

#[cfg(feature = "image-build")]
impl BollardImageBuilder {
    /// Resolve the podman API socket URL — [`podman_socket_url_from`] over this
    /// process's environment and real uid.
    fn socket_url() -> Result<String> {
        podman_socket_url_from(
            std::env::var("DOCKER_HOST").ok().as_deref(),
            std::env::var("XDG_RUNTIME_DIR").ok().as_deref(),
            real_uid(),
        )
    }

    /// Connect to the podman/Docker API socket. `Err` when the socket is absent.
    pub fn connect() -> Result<Self> {
        let url = Self::socket_url()?;
        let path = url.strip_prefix("unix://").unwrap_or(&url);
        anyhow::ensure!(
            std::path::Path::new(path).exists(),
            "podman API socket not found at {path} (enable with `systemctl --user enable --now podman.socket`)"
        );
        let docker = bollard::Docker::connect_with_unix(&url, 120, bollard::API_DEFAULT_VERSION)
            .with_context(|| format!("connect podman socket {url}"))?;
        let rt = tokio::runtime::Builder::new_multi_thread()
            .worker_threads(2)
            .enable_all()
            .build()
            .context("build tokio runtime for bollard image build")?;
        Ok(BollardImageBuilder { docker, rt, staged: BTreeMap::new() })
    }

    /// **What `[build.toolchain] image_sha256` pins, read off the engine** — the
    /// MANIFEST digest of `image` as 64 lowercase hex, no `sha256:` prefix.
    ///
    /// A thin door onto the inspection [`build`](ImageBuilder::build) already
    /// does: same socket, same `inspect_image`, same
    /// [`manifest_digest_of`] extraction. It exists because a declaration that
    /// must carry a digest had **no way to learn one** without shelling to
    /// `podman image inspect --format '{{.Digest}}'` — the exact reach-around
    /// the no-shelling law forbids, and the reason seven repos sat unpinned on
    /// 2026-09-10.
    ///
    /// Unlike `build`, this NEVER falls back to the image **id**: an id pasted
    /// where a manifest digest belongs is refused by
    /// [`CargoToolchain::validate`](crate::cargo_container::CargoToolchain::validate),
    /// so answering one here would only move the refusal later. An image the
    /// store holds with no repo digest (built locally, never pushed or pulled)
    /// is refused BY NAME, naming the fix.
    pub fn manifest_digest(&self, image: &str) -> Result<String> {
        let docker = &self.docker;
        let inspected = self
            .rt
            .block_on(async { docker.inspect_image(image).await })
            .with_context(|| format!("inspect image {image}"))?;
        let digest = manifest_digest_of(inspected.repo_digests.as_deref()).ok_or_else(|| {
            anyhow::anyhow!(
                "image {image} is in the store but carries no repo digest — it was built \
                 locally and never pushed or pulled, so there is no MANIFEST digest to pin. \
                 Mint it reproducibly (`--example mint_cargo_toolchain`) or pull it from a \
                 registry, then re-read"
            )
        })?;
        Ok(digest.trim_start_matches("sha256:").to_string())
    }

    /// Provide the cargo-variant artifacts to stage into the build context: a map
    /// from variant name → the produced artifact path on the host. A `from_variant`
    /// image layer (which renders `COPY ./<variant>` in the Containerfile) is then
    /// staged under `<variant>`, so a variant-chained image build (build holger,
    /// then image it) actually finds its payload instead of failing the COPY.
    pub fn with_variant_artifacts(mut self, staged: BTreeMap<String, PathBuf>) -> Self {
        self.staged = staged;
        self
    }

    /// Assemble the in-memory tar build context.
    ///
    /// Synthesis mode: the Containerfile as `Dockerfile` plus each layer's on-disk
    /// `source` file (best-effort — a missing file simply isn't tarred, so the
    /// engine build surfaces the honest COPY error).
    ///
    /// File mode (`containerfile = "…"`): the whole declared context directory,
    /// flattened to the archive root (`append_dir_all("")` drops the root entry),
    /// which is what a real Containerfile's `COPY` — including a multi-stage
    /// `COPY --from=build` — resolves against.
    fn tar_context(
        image: &ContainerBuild,
        repo_root: &Path,
        staged: &BTreeMap<String, PathBuf>,
    ) -> Result<Vec<u8>> {
        let mut builder = tar::Builder::new(Vec::new());
        // FILE MODE: the build input is a real Containerfile on disk, and its `COPY`
        // lines resolve against the declared context — so the context DIRECTORY is
        // what must be streamed, not a Containerfile plus a handful of layer files.
        // Tarring only the Dockerfile here would hand the engine a multi-stage build
        // whose every `COPY` misses, i.e. a wrong image or a late failure.
        if image.is_file_mode() {
            let ctx = image.context_dir();
            let ctx_abs = repo_root.join(&ctx);
            anyhow::ensure!(
                ctx_abs.is_dir(),
                "container '{}': build context '{ctx}' is not a directory under {}",
                image.name,
                repo_root.display()
            );
            builder
                .append_dir_all("", &ctx_abs)
                .with_context(|| format!("tar build context {}", ctx_abs.display()))?;
            return builder.into_inner().context("finish tar context");
        }
        let containerfile = image.containerfile();
        let bytes = containerfile.as_bytes();
        let mut header = tar::Header::new_gnu();
        header.set_size(bytes.len() as u64);
        header.set_mode(0o644);
        header.set_cksum();
        builder
            .append_data(&mut header, "Dockerfile", bytes)
            .context("tar Dockerfile")?;
        for layer in &image.layers {
            // A `from_variant` layer's payload is the artifact a cargo build produced;
            // it renders `COPY ./<variant>` in the Containerfile, so stage the staged
            // artifact into the context under `<variant>` (else the COPY has no source
            // and the engine build fails). This is the `variant_deps` contract honored.
            if let Some(variant) = &layer.from_variant {
                let src = staged.get(variant).with_context(|| {
                    format!(
                        "container '{}': layer chains cargo variant '{variant}' but no built \
                         artifact was staged for it — build the variant first and pass it via \
                         `with_variant_artifacts`",
                        image.name
                    )
                })?;
                let mut f = std::fs::File::open(src)
                    .with_context(|| format!("open staged variant artifact {}", src.display()))?;
                let len = f.metadata().map(|m| m.len()).unwrap_or(0);
                let mut header = tar::Header::new_gnu();
                header.set_size(len);
                header.set_mode(if layer.executable { 0o755 } else { 0o644 });
                header.set_cksum();
                builder
                    .append_data(&mut header, variant.as_str(), &mut f)
                    .with_context(|| format!("tar staged variant artifact {variant}"))?;
                continue;
            }
            if layer.source.is_empty() {
                continue;
            }
            let p = Path::new(&layer.source);
            let abs = if p.is_absolute() { p.to_path_buf() } else { repo_root.join(p) };
            if abs.is_file() {
                let mut f = std::fs::File::open(&abs)
                    .with_context(|| format!("open layer source {}", abs.display()))?;
                // Set the mode explicitly rather than letting `append_file` inherit the
                // host's. A source checked out 0644 would otherwise land 0644 in the
                // image however the layer is declared, and with the `RUN chmod` gone
                // this tar header is the only thing that makes the file executable.
                let len = f.metadata().map(|m| m.len()).unwrap_or(0);
                let mut header = tar::Header::new_gnu();
                header.set_size(len);
                header.set_mode(if layer.executable { 0o755 } else { 0o644 });
                header.set_cksum();
                builder
                    .append_data(&mut header, &layer.source, &mut f)
                    .with_context(|| format!("tar layer source {}", layer.source))?;
            }
        }
        builder.into_inner().context("finish tar context")
    }
}

#[cfg(feature = "image-build")]
impl ImageBuilder for BollardImageBuilder {
    fn build(&self, image: &ContainerBuild, repo_root: &Path) -> Result<BuiltImage> {
        use bollard::query_parameters::BuildImageOptionsBuilder;
        use futures::StreamExt;

        let tag = image
            .primary_tag()
            .map(|s| s.to_string())
            .unwrap_or_else(|| format!("{}:latest", image.name));
        let context = Self::tar_context(image, repo_root, &self.staged)?;
        // A recipe that states its minting instant (`ENV SOURCE_DATE_EPOCH=`) is built AT that
        // instant: podman's `timestamp` sets the config's `created` AND every layer file's
        // mtime, which no Containerfile line can reach. Without it the apt layer keeps the
        // packages' own mtimes and a pinned recipe never reproduces its pin (MEASURED
        // 2026-09-22, tunnr's kernel toolchain: 72d66255 vs ed482508, mtimes only).
        let epoch = source_date_epoch_of_cell(image, repo_root)?;
        let owned;
        let docker = match epoch {
            None => &self.docker,
            Some(epoch) => {
                owned = self.docker.clone().with_request_modifier(move |mut req| {
                    if let Some(uri) = reproducible_build_uri(&req.uri().to_string(), epoch) {
                        if let Ok(uri) = uri.parse() {
                            *req.uri_mut() = uri;
                        }
                    }
                    req
                });
                &owned
            }
        };
        self.rt.block_on(async {
            // `Dockerfile` for a synthesized cell (the name we tar it under); the
            // real file's context-relative path for a `containerfile = "…"` cell.
            let dockerfile = image.containerfile_in_context();
            let opts = BuildImageOptionsBuilder::default()
                .dockerfile(&dockerfile)
                .t(&tag)
                .rm(true)
                .build();
            let body = bollard::body_full(context.into());
            let mut stream = docker.build_image(opts, None, Some(body));
            while let Some(item) = stream.next().await {
                let info = item.map_err(|e| anyhow::anyhow!("build image {tag}: {e}"))?;
                if let Some(err) = info.error_detail {
                    anyhow::bail!("build image {tag}: {}", err.message.unwrap_or_default());
                }
            }
            // Inspect the built image for its real content digest + size.
            let inspected = docker
                .inspect_image(&tag)
                .await
                .map_err(|e| anyhow::anyhow!("inspect built image {tag}: {e}"))?;
            let digest = manifest_digest_of(inspected.repo_digests.as_deref())
                .or(inspected.id.clone())
                .unwrap_or_else(|| "sha256:unknown".to_string());
            let bytes = inspected.size.unwrap_or(0).max(0) as u64;

            // The declared DISTRIBUTABLE archive: stream the engine's image export
            // (docker-archive, what `docker save` writes — `podman load` reads it)
            // to the repo-root-relative path, hashing as it lands so the recorded
            // sha256 is of the bytes actually written, never a re-read.
            let archive = if image.archive.trim().is_empty() {
                None
            } else {
                let rel = image.archive.trim();
                let out = repo_root.join(rel);
                if let Some(parent) = out.parent() {
                    std::fs::create_dir_all(parent)
                        .with_context(|| format!("create archive dir {}", parent.display()))?;
                }
                let mut f = std::fs::File::create(&out)
                    .with_context(|| format!("create image archive {}", out.display()))?;
                let mut hasher = Sha256::new();
                let mut written: u64 = 0;
                let mut stream = docker.export_image(&tag);
                while let Some(chunk) = stream.next().await {
                    let chunk =
                        chunk.map_err(|e| anyhow::anyhow!("export image {tag}: {e}"))?;
                    std::io::Write::write_all(&mut f, &chunk)
                        .with_context(|| format!("write image archive {}", out.display()))?;
                    hasher.update(&chunk);
                    written += chunk.len() as u64;
                }
                if written == 0 {
                    anyhow::bail!(
                        "export image {tag}: the engine streamed 0 bytes — an empty archive is \
                         not a produced artifact"
                    );
                }
                Some(Artifact {
                    path: rel.to_string(),
                    bytes: written,
                    sha256: crate::provenance::hex(&hasher.finalize()),
                })
            };
            Ok(BuiltImage { image_ref: tag.clone(), digest, bytes, archive })
        })
    }

    /// The engine's own `/version`, read through [`engine_that_cannot_reproduce`]. An engine
    /// that does not answer is not excused: `None`, so it mints and compares.
    fn cannot_mint_reproducibly(&self) -> Option<String> {
        let version = self.rt.block_on(async { self.docker.version().await }).ok()?;
        let components: Vec<(String, String)> =
            version.components.unwrap_or_default().into_iter().map(|c| (c.name, c.version)).collect();
        engine_that_cannot_reproduce(&components)
    }

    /// Every declared tag inspected: `Some` when ALL of them name an image answering to `pin`.
    fn held(&self, image: &ContainerBuild, pin: &str) -> Result<Option<BuiltImage>> {
        let Some(primary) = image.primary_tag().map(str::to_string) else { return Ok(None) };
        self.rt.block_on(async {
            let mut answer = None;
            for t in &image.tags {
                // "No such image" and "the engine did not answer" both mean: mint.
                let Ok(inspected) = self.docker.inspect_image(t).await else { return Ok(None) };
                let mut ids: Vec<String> = inspected.id.iter().map(|i| crate::kernel::normalize_sha256(i)).collect();
                for rd in inspected.repo_digests.as_deref().unwrap_or_default() {
                    if let Some((_, d)) = rd.rsplit_once('@') {
                        ids.push(crate::kernel::normalize_sha256(d));
                    }
                }
                if !built_matches_pin(&ids, pin) {
                    return Ok(None);
                }
                if *t == primary {
                    // The record names the image by the PIN when the pin is one of its manifest
                    // digests (a couriered image answers to several; the first is arbitrary).
                    let by_pin = ids.iter().skip(inspected.id.iter().count()).any(|d| d == pin).then(|| format!("sha256:{pin}"));
                    let digest = by_pin
                        .or_else(|| manifest_digest_of(inspected.repo_digests.as_deref()))
                        .or(inspected.id.clone())
                        .unwrap_or_else(|| "sha256:unknown".to_string());
                    answer = Some(BuiltImage {
                        image_ref: primary.clone(),
                        digest,
                        bytes: inspected.size.unwrap_or(0).max(0) as u64,
                        archive: None,
                    });
                }
            }
            Ok(answer)
        })
    }

    /// The image id and every manifest digest the engine knows the built image by.
    fn identities(&self, built: &BuiltImage) -> Result<Vec<String>> {
        let tag = built.image_ref.clone();
        self.rt.block_on(async {
            let inspected = self
                .docker
                .inspect_image(&tag)
                .await
                .map_err(|e| anyhow::anyhow!("inspect built image {tag}: {e}"))?;
            let mut ids: Vec<String> = inspected.id.iter().map(|i| crate::kernel::normalize_sha256(i)).collect();
            for rd in inspected.repo_digests.as_deref().unwrap_or_default() {
                if let Some((_, d)) = rd.rsplit_once('@') {
                    ids.push(crate::kernel::normalize_sha256(d));
                }
            }
            Ok(ids)
        })
    }

    /// `repo:tag` for each of `tags`, onto the image `built` names.
    fn tag(&self, built: &BuiltImage, tags: &[String]) -> Result<()> {
        use bollard::query_parameters::TagImageOptions;
        self.rt.block_on(async {
            for t in tags {
                // The tag half is after the LAST ':' that follows the last '/', so a
                // registry port (`host:5000/img:tag`) is not taken for one.
                let slash = t.rfind('/').map_or(0, |i| i + 1);
                let (repo, tag) = match t[slash..].rfind(':') {
                    Some(i) => (&t[..slash + i], &t[slash + i + 1..]),
                    None => (t.as_str(), "latest"),
                };
                let opts = TagImageOptions { repo: Some(repo.to_string()), tag: Some(tag.to_string()) };
                self.docker
                    .tag_image(&built.image_ref, Some(opts))
                    .await
                    .map_err(|e| anyhow::anyhow!("tag {} as {t}: {e}", built.image_ref))?;
            }
            Ok(())
        })
    }

    /// Remove the tag only: `noprune`, not `force`, so an image another tag still names stays.
    fn untag(&self, reference: &str) -> Result<()> {
        use bollard::query_parameters::RemoveImageOptions;
        self.rt.block_on(async {
            let opts = RemoveImageOptions { force: false, noprune: true, ..Default::default() };
            self.docker
                .remove_image(reference, Some(opts), None)
                .await
                .map(|_| ())
                .map_err(|e| anyhow::anyhow!("untag {reference}: {e}"))
        })
    }
}

#[cfg(test)]
mod fixture_naming_tests {
    use super::{
        appliance_seed_fixture_requested, iso_out_stem, iso_output_path, FIXTURE_ISO_SUFFIX,
    };
    use crate::iso::IsoBuild;
    use std::path::Path;

    /// The property HO-1 is about: a fixture compose and a release compose of
    /// the SAME cell cannot produce a file at the same path. Before this, both
    /// wrote `.iso-out/<cell>.iso` and only the medium's CONTENTS said which
    /// one it was.
    #[test]
    fn a_fixture_medium_is_never_named_the_release_medium() {
        let cell = "gunnar-appliance-iso";
        let release = iso_out_stem(cell, false);
        let fixture = iso_out_stem(cell, true);
        assert_ne!(release, fixture, "the two composes must not share a file name");

        // The release arm is EXACTLY the historical name — a release compose is
        // byte-unchanged by this row, which is the half that must not move.
        assert_eq!(release, cell, "a release compose keeps the name it always had");
        assert_eq!(fixture, format!("{cell}{FIXTURE_ISO_SUFFIX}"));

        // And the suffix cannot be empty, or the whole guard is a no-op that
        // still passes the `assert_ne!` above only by accident.
        assert!(!FIXTURE_ISO_SUFFIX.is_empty(), "an empty suffix makes the two names identical");
    }

    /// The path function every caller shares — the forge masters to it and the
    /// buildgate expects a product at it — so they cannot disagree about which
    /// name a fixture run used.
    #[test]
    fn the_output_path_is_the_stem_under_iso_out() {
        let iso = IsoBuild { name: "appliance".into(), ..Default::default() };
        let root = Path::new("/repo");
        let got = iso_output_path(root, &iso);
        let expect_stem = iso_out_stem(&iso.name, appliance_seed_fixture_requested());
        assert_eq!(got, root.join(".iso-out").join(format!("{expect_stem}.iso")));

        // Both spellings of the stem are exercised against the same shape, so a
        // suffix that silently stopped being applied would be caught here even
        // when the test process is not itself a fixture run.
        assert_eq!(
            root.join(".iso-out").join(format!("{}.iso", iso_out_stem(&iso.name, true))),
            root.join(".iso-out").join("appliance-fixture.iso")
        );
        assert_eq!(
            root.join(".iso-out").join(format!("{}.iso", iso_out_stem(&iso.name, false))),
            root.join(".iso-out").join("appliance.iso")
        );
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::container::{ContainerBuild, ImageLayer};

    /// **The build request carries the recipe's instant** (2026-09-22). The compat `/build`
    /// bollard sends is rewritten to podman's `libpod/build` with `layers=true`,
    /// `timestamp=<epoch>` and the OCI format, keeping bollard's own parameters; any other
    /// request is untouched. Fails before the fix: there was no rewrite, and the kernel
    /// toolchain built from its pinned recipe answered caf41863 instead of 3f632569.
    #[test]
    fn a_recipe_with_an_instant_is_built_at_that_instant() {
        let got = reproducible_build_uri("unix://2f72756e/v1.41/build?dockerfile=Containerfile&t=x%3Alatest&rm=true", 1783900800)
            .expect("a compat build is rewritten");
        assert_eq!(
            got,
            "unix://2f72756e/v5.0.0/libpod/build?dockerfile=Containerfile&t=x%3Alatest&rm=true&layers=true&timestamp=1783900800&outputformat=application%2Fvnd.oci.image.manifest.v1%2Bjson"
        );
        assert_eq!(reproducible_build_uri("unix://2f72756e/v1.41/images/x/json", 1), None, "only the build is rewritten");
        assert_eq!(reproducible_build_uri("unix://2f72756e/v5.0.0/libpod/build?t=x", 1), None, "never twice");
    }

    /// **The instant comes from the recipe, once**: a file-mode cell whose Containerfile states
    /// `ENV SOURCE_DATE_EPOCH` answers it; one that does not, and every synthesized cell,
    /// answers `None`; a malformed value is refused by name, never dropped.
    #[test]
    fn the_instant_is_the_containerfiles_own_line() {
        let dir = tempfile::tempdir().unwrap();
        let cell = |file: &str| ContainerBuild { name: "c".into(), containerfile: file.into(), ..Default::default() };
        std::fs::write(dir.path().join("A"), "FROM x\nENV SOURCE_DATE_EPOCH=1783900800\nRUN true\n").unwrap();
        std::fs::write(dir.path().join("B"), "FROM x\nRUN true\n").unwrap();
        std::fs::write(dir.path().join("C"), "FROM x\nENV SOURCE_DATE_EPOCH=soon\n").unwrap();
        assert_eq!(source_date_epoch_of_cell(&cell("A"), dir.path()).unwrap(), Some(1783900800));
        assert_eq!(source_date_epoch_of_cell(&cell("B"), dir.path()).unwrap(), None);
        assert!(source_date_epoch_of_cell(&cell("C"), dir.path()).is_err());
        assert_eq!(source_date_epoch_of_cell(&appliance(), dir.path()).unwrap(), None, "a synthesized cell states no instant");
    }

    /// A fake [`ImageBuilder`] that returns a preset [`BuiltImage`] (a known digest)
    /// — the "fake builder returns a known artifact" test seam, no daemon.
    struct FakeImageBuilder {
        built: BuiltImage,
    }
    impl ImageBuilder for FakeImageBuilder {
        fn build(&self, _image: &ContainerBuild, _root: &Path) -> Result<BuiltImage> {
            Ok(self.built.clone())
        }
    }

    fn appliance() -> ContainerBuild {
        ContainerBuild {
            name: "tunnr-appliance".into(),
            base: "debian:bookworm-slim".into(),
            layers: vec![ImageLayer::from_variant("full", "/usr/local/bin/tunnr")],
            entrypoint: vec!["/usr/local/bin/tunnr".into()],
            tags: vec!["tunnr-appliance:0.1".into()],
            ..Default::default()
        }
    }

    /// A declared `archive` rides the artifact record as a SECOND artifact beside
    /// the image identity — and an undeclared one adds nothing. RED-when-broken:
    /// if `build_container` went back to `vec![b.to_artifact()]`, the archive
    /// would be produced on disk yet absent from the record, exactly the silent
    /// truncation `to_artifacts` exists to prevent.
    #[test]
    fn a_declared_archive_is_a_recorded_artifact() {
        let known = BuiltImage {
            image_ref: "tunnr-appliance:0.1".into(),
            digest: "sha256:2222222222222222222222222222222222222222222222222222222222222222"
                .into(),
            bytes: 4096,
            archive: Some(Artifact {
                path: ".container-out/tunnr.tar".into(),
                bytes: 12_345,
                sha256: "3".repeat(64),
            }),
        };
        let builder = FakeImageBuilder { built: known };
        let mut img = appliance();
        img.archive = ".container-out/tunnr.tar".into();
        let out = build_container(&builder, &img, "tunnr", Path::new(".")).unwrap();
        assert_eq!(out.artifacts.len(), 2, "image identity + the exported archive");
        assert_eq!(out.artifacts[1].path, ".container-out/tunnr.tar");
        assert_eq!(out.artifacts[1].bytes, 12_345);
        assert_eq!(out.artifacts[1].sha256, "3".repeat(64));
    }

    /// `archive` must stay inside the repo: absolute paths and `..` escapes are
    /// refused at validate time, before any engine runs.
    #[test]
    fn an_escaping_archive_path_is_refused() {
        let mut img = appliance();
        img.archive = "/tmp/evil.tar".into();
        assert!(img.validate().is_err(), "absolute archive path must be refused");
        img.archive = "../outside.tar".into();
        assert!(img.validate().is_err(), "`..` escape must be refused");
        img.archive = ".container-out/ok.tar".into();
        assert!(img.validate().is_ok(), "a sane relative path passes");
    }

    /// A builder that remembers what it was asked: the tags each build ran under, every
    /// `tag` and `untag` call — so a test can say which names moved.
    struct TagLedger {
        answers_to: Vec<String>,
        built_under: std::cell::RefCell<Vec<Vec<String>>>,
        tagged: std::cell::RefCell<Vec<String>>,
        untagged: std::cell::RefCell<Vec<String>>,
    }
    impl TagLedger {
        fn answering(ids: &[&str]) -> Self {
            TagLedger {
                answers_to: ids.iter().map(|s| s.to_string()).collect(),
                built_under: Default::default(),
                tagged: Default::default(),
                untagged: Default::default(),
            }
        }
        /// Every name that ends up pointing at the built image: a build's own tags, plus
        /// every `tag`, minus every `untag`.
        fn names_left(&self) -> Vec<String> {
            let mut all: Vec<String> = self.built_under.borrow().iter().flatten().cloned().collect();
            all.extend(self.tagged.borrow().iter().cloned());
            all.retain(|n| !self.untagged.borrow().contains(n));
            all.sort();
            all.dedup();
            all
        }
    }
    impl ImageBuilder for TagLedger {
        fn build(&self, image: &ContainerBuild, _root: &Path) -> Result<BuiltImage> {
            self.built_under.borrow_mut().push(image.tags.clone());
            Ok(BuiltImage {
                image_ref: image.primary_tag().unwrap_or("none").to_string(),
                digest: format!("sha256:{}", self.answers_to[0]),
                bytes: 1,
                archive: None,
            })
        }
        fn identities(&self, _built: &BuiltImage) -> Result<Vec<String>> {
            Ok(self.answers_to.clone())
        }
        fn tag(&self, _built: &BuiltImage, tags: &[String]) -> Result<()> {
            self.tagged.borrow_mut().extend(tags.iter().cloned());
            Ok(())
        }
        fn untag(&self, reference: &str) -> Result<()> {
            self.untagged.borrow_mut().push(reference.to_string());
            Ok(())
        }
    }

    /// An engine with a store (`holds`: what the declared tag answers to), a builder (`mints`:
    /// what a mint of the recipe answers to), and its own word on whether it can reproduce.
    struct Engine {
        holds: Vec<String>,
        mints: Vec<String>,
        lame: Option<String>,
        builds: std::cell::Cell<u32>,
        held_asked: std::cell::Cell<u32>,
    }
    impl Engine {
        fn new(holds: &[&str], mints: &[&str], lame: Option<&str>) -> Self {
            Engine {
                holds: holds.iter().map(|s| s.to_string()).collect(),
                mints: mints.iter().map(|s| s.to_string()).collect(),
                lame: lame.map(str::to_string),
                builds: Default::default(),
                held_asked: Default::default(),
            }
        }
    }
    impl ImageBuilder for Engine {
        fn build(&self, image: &ContainerBuild, _root: &Path) -> Result<BuiltImage> {
            self.builds.set(self.builds.get() + 1);
            Ok(BuiltImage { image_ref: image.primary_tag().unwrap_or("none").into(), digest: format!("sha256:{}", self.mints[0]), bytes: 1, archive: None })
        }
        fn identities(&self, _built: &BuiltImage) -> Result<Vec<String>> {
            Ok(self.mints.clone())
        }
        fn held(&self, image: &ContainerBuild, pin: &str) -> Result<Option<BuiltImage>> {
            self.held_asked.set(self.held_asked.get() + 1);
            Ok(built_matches_pin(&self.holds, pin).then(|| BuiltImage {
                image_ref: image.primary_tag().unwrap_or("none").into(),
                digest: format!("sha256:{pin}"),
                bytes: 1,
                archive: None,
            }))
        }
        fn cannot_mint_reproducibly(&self) -> Option<String> {
            self.lame.clone()
        }
    }
    const PIN: &str = "78eaa55a651d5bd76aae36db0f3307e267a92d2a547ee338a17822a103e0320e";
    const DRIFTED: &str = "ea2c07ca42bc0000000000000000000000000000000000000000000000000000";

    /// **An engine that cannot reproduce takes the pinned image it already HOLDS, and the record
    /// says so** (2026-10-13). MEASURED on a podman 4.9.3 builder: the pinned `78eaa55a` was in
    /// the store as `nordisk-kernel-toolchain:latest` (couriered), the step minted anyway, the
    /// older buildah answered `ea2c07ca`, and the compose stopped at its first step.
    ///
    /// **Fails before the fix**: `build_pinned` always builds, so the outcome is the
    /// "NOT the pinned one" failure and `builds` is 1.
    #[test]
    fn an_engine_that_cannot_reproduce_takes_the_held_pin_and_the_record_says_held() {
        let engine = Engine::new(&["3f632569f198", PIN], &[DRIFTED], Some("podman 4.9.3"));
        let out = build_container(&engine, &kernel_toolchain(PIN), "tunnr", Path::new(".")).unwrap();
        assert!(out.success, "{:?}", out.error);
        assert_eq!(engine.builds.get(), 0, "nothing was minted");
        assert_eq!(out.artifacts[0].path, "nordisk-kernel-toolchain:latest");
        assert_eq!(out.artifacts[0].sha256, PIN);
        assert_eq!(
            out.attestation.record.engine, "bollard — held, not minted on this engine (podman 4.9.3)",
            "a release tells a couriered pin from a minted one by the record"
        );
        // A declared archive is exported by a mint, and this engine's mint is never the pin.
        let mut c = kernel_toolchain(PIN);
        c.archive = ".container-out/t.tar".into();
        let out = build_container(&engine, &c, "tunnr", Path::new(".")).unwrap();
        assert!(!out.success && engine.builds.get() == 0);
        assert!(out.error.unwrap_or_default().contains("podman 5 builder"));
    }

    /// **(a) An engine that CAN reproduce is never excused by what its store holds**: the store
    /// holds the pin under the declared tag, the recipe changed and nobody re-pinned — the
    /// step still mints, and FAILS by name. This is the recipe-drift gate; the first cut of
    /// `held` (edda 158038d) switched it off on every warm box.
    ///
    /// **Fails at 158038d**: `held` answers first, `builds` is 0 and the outcome is a success
    /// over the OLD image.
    #[test]
    fn a_reproducing_engine_that_holds_the_pin_still_mints_and_a_changed_recipe_fails_by_name() {
        let engine = Engine::new(&["3f632569f198", PIN], &[DRIFTED], None);
        let out = build_container(&engine, &kernel_toolchain(PIN), "tunnr", Path::new(".")).unwrap();
        assert_eq!(engine.builds.get(), 1, "it minted");
        assert_eq!(engine.held_asked.get(), 0, "the store is not even asked");
        assert!(!out.success, "a recipe that no longer mints its pin is a FAILED build");
        let err = out.error.unwrap_or_default();
        assert!(err.contains("NOT the pinned one") && err.contains("ea2c07ca42bc") && err.contains("78eaa55a651d"), "{err}");
        assert_eq!(out.attestation.record.engine, "bollard", "and nothing is recorded as held");
        // The same engine, recipe unchanged: minted, compared, recorded as an ordinary mint.
        let engine = Engine::new(&[PIN], &["3f632569f198", PIN], None);
        let out = build_container(&engine, &kernel_toolchain(PIN), "tunnr", Path::new(".")).unwrap();
        assert!(out.success && engine.builds.get() == 1, "{:?}", out.error);
        assert_eq!(out.attestation.record.engine, "bollard");
    }

    /// **(b) An engine that cannot reproduce and does not hold the pin fails with the fix in the
    /// sentence** — courier the image, or use a podman 5 builder — never the bare "NOT the
    /// pinned one", which on such a box sends the reader to a recipe that did not change.
    /// Holding something ELSE under the tag is not holding the pin.
    #[test]
    fn an_engine_that_cannot_reproduce_and_does_not_hold_the_pin_fails_naming_the_fix() {
        for holds in [vec![], vec!["311bc06f5637"]] {
            let engine = Engine::new(&holds, &[DRIFTED], Some("podman 4.9.3"));
            let out = build_container(&engine, &kernel_toolchain(PIN), "tunnr", Path::new(".")).unwrap();
            assert!(!out.success);
            assert_eq!(engine.builds.get(), 0, "a mint that can only fail is not started");
            let err = out.error.unwrap_or_default();
            assert!(!err.contains("NOT the pinned one"), "{err}");
            for want in ["78eaa55a651d", "podman 4.9.3", "courier the pinned image", "nordisk-kernel-toolchain:latest", "podman save --format oci-archive", "podman 5 builder"] {
                assert!(err.contains(want), "`{want}` is missing from: {err}");
            }
        }
    }

    /// The engine's own `/version` decides, and only a recognisable podman below 5 is excused.
    #[test]
    fn only_a_podman_below_the_reproducible_libpod_is_an_engine_that_cannot_reproduce() {
        let v = |n: &str, v: &str| vec![("Conmon".to_string(), "conmon version 2.1.10".to_string()), (n.to_string(), v.to_string())];
        assert_eq!(engine_that_cannot_reproduce(&v("Podman Engine", "4.9.3")).as_deref(), Some("podman 4.9.3"));
        assert_eq!(engine_that_cannot_reproduce(&v("Podman Engine", "5.0.0")), None);
        assert_eq!(engine_that_cannot_reproduce(&v("Podman Engine", "5.7.0")), None);
        assert_eq!(engine_that_cannot_reproduce(&v("Engine", "24.0.7")), None, "not podman: not excused, it mints and compares");
        assert_eq!(engine_that_cannot_reproduce(&v("Podman Engine", "unknown")), None, "unreadable: not excused");
        assert_eq!(engine_that_cannot_reproduce(&[]), None);
        assert!(reproducible_build_uri("http://d/build", 1).unwrap().contains(&format!("/v{REPRODUCIBLE_LIBPOD_MAJOR}.0.0/libpod/build")), "one number, two readers");
    }

    fn kernel_toolchain(pin: &str) -> ContainerBuild {
        let mut c = crate::kernel::kernel_toolchain_container("");
        c.containerfile = String::new();
        c.base = "debian:bookworm-slim".into();
        c.expected_sha256 = pin.into();
        c
    }

    /// **A pinned image that the build does not reproduce moves NO tag, and the build fails by
    /// name** (2026-09-22). MEASURED by T16 on t14s: the `--build kernel` compose's first step
    /// built `nordisk-kernel-toolchain:latest`, the layer cache answered with the 2026-09-15
    /// rebuild `311bc06f`, and `:latest` moved to it while the kernel row pins `78eaa55a`.
    ///
    /// **Fails before the fix**: at edda `8a70bfe` `build_container` hands the declaration's own
    /// tags to the engine — the image is built UNDER `:latest` — and nothing compares it with
    /// any pin, so the outcome is a success and `:latest` names `311bc06f`.
    #[test]
    fn a_pinned_image_the_build_does_not_reproduce_moves_no_tag_and_fails_by_name() {
        const PIN: &str = "78eaa55a651d5bd76aae36db0f3307e267a92d2a547ee338a17822a103e0320e";
        const CACHED: &str = "311bc06f56379229e2f037cee44dea50a8d7b07bdffdfa51963abe37aa5872f8";
        let engine = TagLedger::answering(&[CACHED, "4c90eea4aaaa"]);
        let out = build_container(&engine, &kernel_toolchain(PIN), "tunnr", Path::new(".")).unwrap();
        assert!(!out.success, "an image that is not the pinned one is a FAILED build");
        let err = out.error.unwrap_or_default();
        assert!(err.contains("NOT the pinned one") && err.contains("311bc06f5637") && err.contains("78eaa55a651d"), "{err}");
        assert!(
            engine.names_left().is_empty(),
            "no name points at the unpinned image: {:?} (built under {:?})",
            engine.names_left(),
            engine.built_under.borrow()
        );
        assert!(!engine.built_under.borrow().iter().flatten().any(|t| t.ends_with(":latest")), "never built UNDER :latest");
    }

    /// The other half: an image that IS the pin (by manifest digest, or by image id) gets its
    /// declared tags, and the staging tag comes off again.
    #[test]
    fn a_pinned_image_that_matches_gets_its_tags_and_loses_the_staging_one() {
        const PIN: &str = "78eaa55a651d5bd76aae36db0f3307e267a92d2a547ee338a17822a103e0320e";
        for ids in [vec!["3f632569f198", PIN], vec![PIN]] {
            let engine = TagLedger::answering(&ids);
            let out = build_container(&engine, &kernel_toolchain(&format!("sha256:{PIN}")), "tunnr", Path::new(".")).unwrap();
            assert!(out.success, "{:?}", out.error);
            assert_eq!(engine.names_left(), vec!["nordisk-kernel-toolchain:latest".to_string()]);
            assert_eq!(out.artifacts[0].path, "nordisk-kernel-toolchain:latest", "the record names the declared tag");
        }
        // Unpinned: the old path exactly — built under its own tags, nothing else asked.
        let engine = TagLedger::answering(&["abc"]);
        let out = build_container(&engine, &kernel_toolchain(""), "tunnr", Path::new(".")).unwrap();
        assert!(out.success);
        assert_eq!(*engine.built_under.borrow(), vec![vec!["nordisk-kernel-toolchain:latest".to_string()]]);
        assert!(engine.tagged.borrow().is_empty() && engine.untagged.borrow().is_empty());
    }

    #[test]
    fn build_container_seals_over_the_produced_image_digest() {
        let known = BuiltImage {
            image_ref: "tunnr-appliance:0.1".into(),
            digest: "sha256:1111111111111111111111111111111111111111111111111111111111111111".into(),
            bytes: 4096,
                archive: None,
        };
        let builder = FakeImageBuilder { built: known.clone() };
        let img = appliance();
        let out = build_container(&builder, &img, "tunnr", Path::new(".")).unwrap();

        // The provenance seals over the PRODUCED image digest, not the descriptor.
        assert_eq!(out.kind, TargetKind::Container);
        assert_eq!(out.artifacts.len(), 1);
        assert_eq!(out.artifacts[0].path, "tunnr-appliance:0.1");
        assert_eq!(out.artifacts[0].sha256, "1".repeat(64), "sha256: prefix stripped, lowercased");
        assert_eq!(out.artifacts[0].bytes, 4096);
        // The descriptor's own content_id is retained AND is distinct from the
        // produced-artifact hash — spec hash vs artifact hash.
        assert_eq!(out.attestation.record.descriptor_id, img.content_id());
        assert_ne!(out.attestation.record.descriptor_id, out.artifacts[0].sha256);
        assert_eq!(out.attestation.record.schema, TARGET_PROVENANCE_SCHEMA);
        assert!(out.success);
    }

    #[test]
    fn target_attestation_round_trips_and_tamper_is_detected() {
        let known = BuiltImage {
            image_ref: "app:1".into(),
            digest: "sha256:2222222222222222222222222222222222222222222222222222222222222222".into(),
            bytes: 10,
                archive: None,
        };
        let out = build_container(
            &FakeImageBuilder { built: known },
            &ContainerBuild { name: "app".into(), base: "scratch".into(), tags: vec!["app:1".into()], ..Default::default() },
            "repo",
            Path::new("."),
        )
        .unwrap();

        // Round-trip through JSON keeps the digest + verifies.
        let att = out.attestation;
        assert!(!att.is_sealed(), "fresh attestation is unsealed — no faked seal");
        assert!(att.digest_matches_record().unwrap());
        let back = TargetAttestation::from_json(&att.to_json().unwrap()).unwrap();
        assert_eq!(back.digest, att.digest);
        assert!(back.digest_matches_record().unwrap());

        // TAMPER the produced-artifact hash → the digest no longer matches.
        let mut tampered = att;
        tampered.record.artifacts[0].sha256 = "0".repeat(64);
        assert!(!tampered.digest_matches_record().unwrap(), "tampered artifact breaks the seal digest");
    }

    #[test]
    fn build_vm_seals_over_the_produced_kernel_and_rootfs_hashes() {
        // Stage real kernel+rootfs files, then prove the provenance binds their sha256.
        let dir = tempfile::tempdir().unwrap();
        let root = dir.path();
        std::fs::create_dir_all(root.join("artifacts")).unwrap();
        std::fs::write(root.join("artifacts/bzImage"), b"KERNEL bytes").unwrap();
        std::fs::write(root.join("artifacts/rootfs.cpio.gz"), b"ROOTFS bytes").unwrap();

        let vm = VmBuild {
            name: "holger-appliance".into(),
            kernel: "artifacts/bzImage".into(),
            rootfs: "artifacts/rootfs.cpio.gz".into(),
            mem_mb: 512,
            cores: 2,
            ..Default::default()
        };
        let out = build_vm(&PrebuiltVmBuilder::new(), &vm, "tunnr", root).unwrap();
        assert_eq!(out.kind, TargetKind::Vm);
        assert_eq!(out.artifacts.len(), 2, "kernel + rootfs sealed");
        let kernel = out.artifacts.iter().find(|a| a.path == "artifacts/bzImage").unwrap();
        assert_eq!(kernel.sha256, provenance::hex(&Sha256::digest(b"KERNEL bytes")));
        assert!(out.attestation.digest_matches_record().unwrap());
        // Distinct from the descriptor's spec hash.
        assert_eq!(out.attestation.record.descriptor_id, vm.content_id());
    }

    #[test]
    fn prebuilt_vm_builder_errors_when_no_artifact_present() {
        let dir = tempfile::tempdir().unwrap();
        let vm = VmBuild {
            name: "ghost".into(),
            kernel: "missing/bzImage".into(),
            rootfs: "missing/rootfs".into(),
            ..Default::default()
        };
        let msg = failed_reason(build_vm(&PrebuiltVmBuilder::new(), &vm, "repo", dir.path()));
        assert!(msg.contains("no produced"), "honest reason, carried on the record: {msg}");
    }

    #[test]
    fn prebuilt_vm_builder_stages_and_seals_the_from_variant_artifact() {
        // The "build the app, then boot it" chain: a VM whose `from_variant` names a
        // cargo build seals THAT produced binary (the PID-1 that becomes /init) as a
        // sealed artifact, ahead of the kernel/rootfs it rides in.
        let dir = tempfile::tempdir().unwrap();
        let root = dir.path();
        std::fs::create_dir_all(root.join("artifacts")).unwrap();
        std::fs::write(root.join("artifacts/bzImage"), b"KERNEL bytes").unwrap();
        std::fs::write(root.join("artifacts/rootfs.cpio.gz"), b"ROOTFS bytes").unwrap();
        // the cargo variant's produced PID-1 binary, staged from target/.
        std::fs::create_dir_all(root.join("target/release")).unwrap();
        std::fs::write(root.join("target/release/wg-init"), b"PID1 APP BINARY").unwrap();

        let vm = VmBuild {
            name: "wg-appliance".into(),
            kernel: "artifacts/bzImage".into(),
            rootfs: "artifacts/rootfs.cpio.gz".into(),
            from_variant: Some("wg-pid1".into()),
            ..Default::default()
        };

        let staged: BTreeMap<String, PathBuf> =
            [("wg-pid1".to_string(), root.join("target/release/wg-init"))].into();
        let builder = PrebuiltVmBuilder::new().with_variant_artifacts(staged);
        let out = build_vm(&builder, &vm, "tunnr", root).unwrap();

        // kernel + rootfs + the staged from_variant PID-1 binary — all sealed.
        assert_eq!(out.artifacts.len(), 3, "kernel + rootfs + staged pid1");
        let pid1 = out
            .artifacts
            .iter()
            .find(|a| a.path.ends_with("wg-init"))
            .expect("the from_variant PID-1 binary is a sealed produced-artifact");
        assert_eq!(pid1.sha256, provenance::hex(&Sha256::digest(b"PID1 APP BINARY")));
        assert!(out.attestation.digest_matches_record().unwrap());

        // RED when broken: a `from_variant` with NOTHING staged is a hard error, never
        // a silently-skipped chain.
        let unstaged = PrebuiltVmBuilder::new();
        let msg = failed_reason(build_vm(&unstaged, &vm, "tunnr", root));
        assert!(
            msg.contains("no built artifact was staged"),
            "honest unsatisfied-chain reason: {msg}"
        );
    }

    #[test]
    fn build_container_seal_with_dev_sealer_is_verifiable() {
        // The seal binds the produced-artifact digest through the SAME ProvenanceSealer
        // seam build_variant uses. A tiny in-test sealer proves the wiring.
        struct FixedSealer;
        impl ProvenanceSealer for FixedSealer {
            fn seal(&self, digest: &[u8; 32]) -> Result<Seal> {
                Ok(Seal { alg: "test".into(), signer_cn: "t".into(), cms_der: digest.to_vec() })
            }
        }
        let known = BuiltImage { image_ref: "x:1".into(), digest: "sha256:abcd".into(), bytes: 1, archive: None };
        let out = build_container(
            &FakeImageBuilder { built: known },
            &ContainerBuild { name: "x".into(), base: "scratch".into(), tags: vec!["x:1".into()], ..Default::default() },
            "repo",
            Path::new("."),
        )
        .unwrap();
        let sealed = out.attestation.seal_with(&FixedSealer).unwrap();
        assert!(sealed.is_sealed());
        // The seal's bytes are exactly the digest that covers the artifact hash.
        assert_eq!(sealed.seal.as_ref().unwrap().cms_der, sealed.digest.to_vec());
    }

    /// The provenance engine label comes from the seam impl, not a hard-coded
    /// literal — so a real `tunnr` VM build is recorded as `tunnr`, not `prebuilt`.
    #[test]
    fn build_vm_uses_the_builders_engine_label() {
        struct FakeVmBuilder;
        impl VmImageBuilder for FakeVmBuilder {
            fn build(&self, _vm: &VmBuild, _root: &Path) -> Result<Vec<Artifact>> {
                Ok(vec![Artifact { path: "artifacts/bzImage".into(), bytes: 6, sha256: "ab".repeat(32) }])
            }
            fn engine(&self) -> &str {
                "tunnr"
            }
        }
        let vm = VmBuild {
            name: "appliance".into(),
            kernel: "artifacts/bzImage".into(),
            rootfs: "artifacts/rootfs.cpio.gz".into(),
            ..Default::default()
        };
        let out = build_vm(&FakeVmBuilder, &vm, "tunnr", Path::new(".")).unwrap();
        assert_eq!(out.attestation.record.engine, "tunnr", "engine label comes from the seam impl");
        assert_eq!(out.artifacts.len(), 1);
    }

    struct FailingImageBuilder;
    impl ImageBuilder for FailingImageBuilder {
        fn build(&self, _image: &ContainerBuild, _root: &Path) -> Result<BuiltImage> {
            anyhow::bail!("engine refused: no such base image")
        }
    }

    /// **Assert a target build reached a FAILED outcome, and hand back its reason.**
    ///
    /// Four tests used to do this with `.unwrap_err()` on the call, because a builder
    /// error was propagated. Each existed to prove that a missing artifact is never a
    /// false green, and under the record-keeping contract that proof gets *stronger*,
    /// not weaker: an `Err` carried no record at all, so "it failed honestly" could
    /// only ever be read off an error string. It is now read off the sealed record —
    /// `Outcome::Failed`, no artifacts — which is the thing the ledger actually
    /// stores. One helper rather than four copies, so the four cannot drift.
    fn failed_reason(out: Result<TargetBuildOutcome>) -> String {
        let out = out.expect("a build that reached an outcome has a record to file");
        assert!(!out.success, "a build that produced nothing must not report success");
        assert_eq!(
            out.attestation.record.outcome,
            Outcome::Failed,
            "and the SEALED record must say so — this is the field the ledger stores"
        );
        assert!(out.artifacts.is_empty(), "and must claim no artifact it did not produce");
        out.error.expect("a failed build carries its reason")
    }

    fn broken_container() -> ContainerBuild {
        ContainerBuild {
            name: "broken".into(),
            base: "scratch".into(),
            tags: vec!["broken:1".into()],
            ..Default::default()
        }
    }

    /// **The FAILED-outcome branch KEEPS its record.** This used to assert the
    /// opposite — that `build_container` propagates the builder's error — and the
    /// error it propagated took the already-captured `Outcome::Failed` attestation
    /// with it, so a red image build filed nothing at all while a red cargo build
    /// filed normally.
    ///
    /// RED against the old body: `built?` returned `Err` here, so `unwrap()` panics
    /// with the builder's message instead of yielding an outcome.
    #[test]
    fn a_failed_container_build_still_produces_a_record() {
        let out = build_container(&FailingImageBuilder, &broken_container(), "repo", Path::new("."))
            .expect("a build that reached a terminal outcome has a record to file");

        assert!(!out.success, "the outcome must say it failed");
        assert_eq!(
            out.attestation.record.outcome,
            Outcome::Failed,
            "and so must the sealed record — this is the field the ledger stores"
        );
        assert!(
            out.artifacts.is_empty(),
            "a failed build produced nothing; artifacts must not be invented"
        );
        // Without this the record would say a build failed and never say why, which
        // is the half of the account that makes it worth keeping.
        assert!(
            out.error.as_deref().unwrap_or_default().contains("no such base image"),
            "the builder's reason is carried, not dropped: {:?}",
            out.error
        );
        // The other side. A `capture_target_build` that answered `Failed` to
        // everything would pass every assertion above.
        let ok_builder = FakeImageBuilder {
            built: BuiltImage {
                image_ref: "tunnr-appliance:0.1".into(),
                digest: "sha256:2222222222222222222222222222222222222222222222222222222222222222"
                    .into(),
                bytes: 4096,
                archive: None,
            },
        };
        let good = build_container(&ok_builder, &appliance(), "tunnr", Path::new("."))
            .expect("a green build");
        assert!(good.success && good.error.is_none(), "a green build carries no error");
        assert_eq!(good.attestation.record.outcome, Outcome::Success);
        assert_eq!(good.artifacts.len(), 1, "and it does carry its artifact");
    }

    /// A descriptor that never reaches a builder is still an `Err`, and must be:
    /// there is no outcome and nothing to file. Without this, a
    /// `capture_target_build` that swallowed *everything* into a `Failed` record
    /// would look identical from outside, and `Ok` would stop meaning anything.
    #[test]
    fn a_descriptor_that_never_builds_is_still_a_hard_error() {
        let invalid = ContainerBuild { name: String::new(), ..broken_container() };
        let err = build_container(&FailingImageBuilder, &invalid, "repo", Path::new("."))
            .expect_err("an invalid descriptor never reached a builder");
        assert!(
            format!("{err:#}").contains("validate container descriptor"),
            "and it fails for THAT reason, not the builder's: {err:#}"
        );
    }

    /// The ISO peer of `build_vm_seals_over_the_produced_...`: a mastered `.iso`
    /// on disk is sealed as the produced artifact, and a chained `from_variant`
    /// PID-1 binary is sealed FIRST. RED-when-broken: an unstaged `from_variant`
    /// (or a missing ISO) is a hard error, never a false green.
    #[test]
    fn build_iso_seals_over_the_produced_iso_and_from_variant_payload() {
        use crate::iso::{IsoBuild, IsoFormat};

        let dir = tempfile::tempdir().unwrap();
        let root = dir.path();
        std::fs::create_dir_all(root.join(".iso-out")).unwrap();
        std::fs::write(root.join(".iso-out/korp-installer.iso"), b"ISO9660 bytes").unwrap();
        std::fs::create_dir_all(root.join("target/release")).unwrap();
        std::fs::write(root.join("target/release/korp-init"), b"KORP PID1 BINARY").unwrap();

        let iso = IsoBuild {
            name: "korp-installer".into(),
            kernel: "artifacts/bzImage".into(),
            rootfs: "artifacts/rootfs.cpio.gz".into(),
            volume_id: "KORP_DEMO".into(),
            format: IsoFormat::Hybrid,
            from_variant: Some("korp-pid1".into()),
            ..Default::default()
        };

        let staged: BTreeMap<String, PathBuf> =
            [("korp-pid1".to_string(), root.join("target/release/korp-init"))].into();
        let builder = crate::image_build::PrebuiltIsoBuilder::new().with_variant_artifacts(staged);
        let out = build_iso(&builder, &iso, "korp", root).unwrap();

        assert_eq!(out.kind, TargetKind::Iso);
        assert_eq!(out.artifacts.len(), 2, "the from_variant PID-1 + the mastered .iso");
        let pid1 = out.artifacts.iter().find(|a| a.path.ends_with("korp-init")).unwrap();
        assert_eq!(pid1.sha256, provenance::hex(&Sha256::digest(b"KORP PID1 BINARY")));
        let iso_art = out.artifacts.iter().find(|a| a.path.ends_with(".iso")).unwrap();
        assert_eq!(iso_art.sha256, provenance::hex(&Sha256::digest(b"ISO9660 bytes")));
        assert!(out.attestation.digest_matches_record().unwrap());
        assert_eq!(out.attestation.record.descriptor_id, iso.content_id());

        // RED: nothing staged for the from_variant chain → honest hard error.
        let unstaged = crate::image_build::PrebuiltIsoBuilder::new();
        let msg = failed_reason(build_iso(&unstaged, &iso, "korp", root));
        assert!(msg.contains("no built artifact was staged"), "{msg}");
    }

    /// **End-to-end REAL mastering in PURE RUST** — no external tool required, so
    /// this always runs (the whole point: the ISO path shells out to nothing).
    /// Masters a bootable UEFI ISO from a tiny staging tree carrying an
    /// `efiboot.img` ESP, seals it, and asserts the produced file carries the
    /// ISO-9660 `CD001` PVD magic and an El-Torito boot record — a real, on-disk,
    /// bootable-structure ISO, not a fabricated blob.
    ///
    /// # What these assertions used to be worth
    ///
    /// Five of the six boot-structure assertions below could not fail. The
    /// writer's catalog builder had a `(no BIOS, no EFI) => (lba 0, 4 sectors)`
    /// arm and then unconditionally stamped `0x88` bootable / `0x00`
    /// no-emulation, so the Boot Record VD, the El-Torito id, the validation
    /// header and the `0x55AA` key were emitted for ANY image — including one
    /// with no boot image at all. Only `catalog[65] == 0xEF` discriminated.
    ///
    /// Two things fixed that. The writer no longer emits El-Torito structures
    /// for an image with nothing bootable (see
    /// `an_iso_with_no_boot_image_is_not_presented_as_bootable`), so the five
    /// structural assertions now depend on the ESP having really resolved; and
    /// the assertions below now read the catalog's **recorded extent and sector
    /// count** and go and check the ESP bytes are actually there, rather than
    /// checking constants the writer types out either way.
    #[test]
    fn pure_rust_builder_masters_a_real_bootable_iso() {
        use crate::iso::{IsoBuild, IsoFormat};
        let dir = tempfile::tempdir().unwrap();
        let root = dir.path();
        // A minimal UEFI staging tree: the ESP the El-Torito EFI entry boots.
        // 8192 B is a deliberate 16 El-Torito virtual sectors — a size the
        // catalog must state exactly, and one no default can be mistaken for.
        std::fs::create_dir_all(root.join("iso-root")).unwrap();
        std::fs::write(root.join("iso-root/efiboot.img"), vec![0xEFu8; 8192]).unwrap();
        std::fs::write(root.join("iso-root/hello.txt"), b"korp basemap placeholder").unwrap();

        let iso = IsoBuild {
            name: "korp-data".into(),
            staging: "iso-root".into(),
            volume_id: "KORP_DATA".into(),
            format: IsoFormat::Uefi,
            ..Default::default()
        };
        let builder = PureRustIsoBuilder::new();
        let out = build_iso(&builder, &iso, "korp", root)
            .unwrap_or_else(|e| panic!("pure-rust master failed: {e:#}"));
        assert_eq!(out.kind, TargetKind::Iso);
        assert_eq!(out.attestation.record.engine, "pure-rust");
        let iso_art = out.artifacts.iter().find(|a| a.path.ends_with(".iso")).expect("mastered .iso");
        assert!(iso_art.bytes > 0, "the produced ISO is non-empty");

        let produced = root.join(".iso-out/korp-data.iso");
        assert!(produced.is_file(), "a REAL .iso was mastered at {}", produced.display());
        let bytes = std::fs::read(&produced).unwrap();
        // PVD "CD001" magic at sector 16.
        const S: usize = crate::iso_writer::SECTOR;
        assert_eq!(&bytes[16 * S + 1..16 * S + 6], b"CD001", "ISO-9660 PVD magic present");
        // El-Torito boot record VD at sector 17.
        assert_eq!(bytes[17 * S], 0, "El-Torito boot record VD type");
        assert!(bytes[17 * S + 7..17 * S + 30].starts_with(b"EL TORITO SPECIFICATION"), "El-Torito id");
        // Boot catalog validation entry key 0x55AA at its pointer.
        let cat_lba = u32::from_le_bytes([bytes[17 * S + 71], bytes[17 * S + 72], bytes[17 * S + 73], bytes[17 * S + 74]]) as usize;
        assert_eq!(bytes[cat_lba * S], 0x01, "El-Torito validation entry header");
        assert_eq!(bytes[cat_lba * S + 30], 0x55);
        assert_eq!(bytes[cat_lba * S + 31], 0xAA);
        assert_eq!(bytes[cat_lba * S + 65], 0xEF, "El-Torito EFI (0xEF) section present");

        // ── the catalog must describe the ESP THAT WAS STAGED ──────────────
        // Applied output: read the section entry's recorded sector count and
        // load RBA, then go and look at what is there. A catalog that named a
        // plausible LBA and a default sector count satisfied every assertion
        // above while pointing firmware at nothing.
        let cat = cat_lba * S;
        let efi_sectors = u16::from_le_bytes([bytes[cat + 102], bytes[cat + 103]]);
        assert_eq!(
            efi_sectors, 16,
            "the 8192 B ESP is exactly 16 El-Torito 512 B virtual sectors; {efi_sectors} means \
             the catalog is describing something other than the image that was staged"
        );
        let efi_lba = u32::from_le_bytes([
            bytes[cat + 104], bytes[cat + 105], bytes[cat + 106], bytes[cat + 107],
        ]) as usize;
        assert!(efi_lba >= 22, "the ESP cannot live inside the fixed descriptors (LBA {efi_lba})");
        assert_eq!(
            &bytes[efi_lba * S..efi_lba * S + 8192],
            &vec![0xEFu8; 8192][..],
            "the ESP bytes must be readable back at the extent the boot catalog points firmware \
             at — this is the assertion the old `(None, None) => (0, 4)` arm could never fail"
        );
        // The UEFI-only default/initial (80x86) entry names something real AND
        // honest: NOT the ESP (until 2026-08-28 it did, and SeaBIOS executed FAT
        // bytes as code — gunnar's "colour confetti", UpCloud's silence) and not
        // LBA 0 — the writer's one-sector BIOS refusal stub, which prints a
        // sentence and halts. See `iso_writer::bios_refusal_sector`.
        assert_eq!(bytes[cat + 32], 0x88, "default entry bootable");
        let def_lba =
            u32::from_le_bytes([bytes[cat + 40], bytes[cat + 41], bytes[cat + 42], bytes[cat + 43]]) as usize;
        assert_ne!(def_lba, efi_lba, "the default entry must not hand a BIOS the FAT ESP to execute");
        assert_ne!(def_lba, 0, "with no BIOS image the default entry must not fall back to LBA 0");
        assert_eq!(
            u16::from_le_bytes([bytes[cat + 38], bytes[cat + 39]]),
            1,
            "the refusal stub is one 512 B virtual sector"
        );
        assert_eq!(
            &bytes[def_lba * S..def_lba * S + 512],
            &crate::iso_writer::bios_refusal_sector()[..],
            "the default entry loads the UEFI-only refusal stub"
        );
        eprintln!(
            "MASTERED a real {}-byte bootable ISO in PURE RUST at {} (no xorriso, no subprocess)",
            iso_art.bytes, produced.display()
        );
    }

    /// **A declared `staging` tree that is not on disk is a hard, named error.**
    ///
    /// It used to be skipped in silence: `if staging.is_dir() { … }` with no
    /// `else`. Measured through the real forge on 2026-08-07 — `staging =
    /// "no-such-staging-dir"` mastered a valid 47 104-byte ISO with nothing in
    /// it and reported `BUILT … 1 artifact(s)`, exit 0, against 49 152 bytes for
    /// the correct build. The whole difference between "the ISO carries the
    /// payload" and "the ISO is empty" was 2 048 bytes and no verdict change at
    /// all, which is the worst shape a build failure can have: it is discovered
    /// by whoever boots the media.
    ///
    /// A declaration is a claim that the tree exists. Asserted on the applied
    /// output — the error text AND the absence of the file — because an ISO that
    /// was written and then reported as failed is still an ISO somebody can pick
    /// up.
    #[test]
    fn a_declared_staging_tree_that_is_missing_is_refused_not_silently_skipped() {
        use crate::iso::{IsoBuild, IsoFormat};
        let dir = tempfile::tempdir().unwrap();
        let root = dir.path();
        // A real payload EXISTS at the conventional place, so nothing else in
        // this build is broken — only the declared staging tree is absent.
        std::fs::create_dir_all(root.join("artifacts")).unwrap();
        std::fs::write(root.join("artifacts/bzImage"), b"BZIMAGE").unwrap();

        let iso = IsoBuild {
            name: "korp-ghost".into(),
            kernel: "artifacts/bzImage".into(),
            staging: "no-such-staging-dir".into(),
            volume_id: "KORP_GHOST".into(),
            format: IsoFormat::Bios,
            ..Default::default()
        };
        let msg = failed_reason(build_iso(&PureRustIsoBuilder::new(), &iso, "korp", root));
        assert!(
            msg.contains("no-such-staging-dir"),
            "the refusal must name the tree that was declared and is not there: {msg}"
        );
        assert!(
            msg.contains("staging"),
            "…and say it is the staging declaration that is unsatisfied: {msg}"
        );
        assert!(
            !root.join(".iso-out/korp-ghost.iso").exists(),
            "an ISO that could not carry its declared tree must not be on disk at all"
        );

        // GREEN control: the same descriptor with the tree actually present
        // masters, so this is a check on the missing tree and not a refusal of
        // every staging declaration.
        std::fs::create_dir_all(root.join("no-such-staging-dir")).unwrap();
        std::fs::write(root.join("no-such-staging-dir/data.bin"), b"REAL STAGED DATA").unwrap();
        build_iso(&PureRustIsoBuilder::new(), &iso, "korp", root)
            .unwrap_or_else(|e| panic!("a staging tree that IS there must master: {e:#}"));
        let bytes = std::fs::read(root.join(".iso-out/korp-ghost.iso")).unwrap();
        assert!(
            bytes.windows(16).any(|w| w == b"REAL STAGED DATA"),
            "the staged bytes must actually be inside the mastered ISO — an ISO that merely \
             exists is exactly the false green this test was written for"
        );
    }

    /// **Two descriptors that master different bytes must not share one
    /// `content_id`.**
    ///
    /// `IsoBuild::canonical_bytes` was taken over `master_args`, and
    /// `master_args` renders no argument for `rootfs` — under `xorriso` the
    /// rootfs rode in on the staging tree. The pure-Rust masterer places it
    /// itself (`w.add_file(&iso.rootfs, …)`), so `rootfs` changes the mastered
    /// image and did not change the id: two different inputs, one id, and a
    /// content-addressed cache that hands back the wrong ISO.
    ///
    /// Asserted the only way that means anything — MASTER both, prove the bytes
    /// differ, and only then require the ids to differ. An id test that compares
    /// two descriptors without ever building them cannot tell a field that
    /// matters from a field that does not.
    #[test]
    fn content_id_covers_every_declared_input_that_changes_the_mastered_bytes() {
        use crate::iso::{IsoBuild, IsoFormat};
        let dir = tempfile::tempdir().unwrap();
        let root = dir.path();
        std::fs::create_dir_all(root.join("artifacts")).unwrap();
        std::fs::write(root.join("artifacts/bzImage"), b"BZIMAGE-BYTES").unwrap();
        std::fs::write(root.join("artifacts/rootfs-a.cpio.gz"), b"ROOTFS-A-PAYLOAD").unwrap();
        std::fs::write(root.join("artifacts/rootfs-b.cpio.gz"), b"ROOTFS-B-PAYLOAD-DIFFERENT").unwrap();

        let base = IsoBuild {
            name: "korp-rootfs".into(),
            kernel: "artifacts/bzImage".into(),
            volume_id: "KORP_ROOTFS".into(),
            format: IsoFormat::Bios,
            ..Default::default()
        };
        let a = IsoBuild { rootfs: "artifacts/rootfs-a.cpio.gz".into(), ..base.clone() };
        let b = IsoBuild { rootfs: "artifacts/rootfs-b.cpio.gz".into(), ..base.clone() };

        let master = |iso: &IsoBuild, out: &str| -> Vec<u8> {
            let out = root.join(out);
            PureRustIsoBuilder::new()
                .with_output(out.clone())
                .build(iso, root)
                .unwrap_or_else(|e| panic!("master {}: {e:#}", out.display()));
            std::fs::read(&out).unwrap()
        };
        let iso_a = master(&a, "a.iso");
        let iso_b = master(&b, "b.iso");

        // The premise: `rootfs` really is an input to the produced bytes.
        assert_ne!(
            iso_a, iso_b,
            "the two rootfs payloads must reach the ISO, or this test is asserting nothing"
        );
        assert!(iso_a.windows(16).any(|w| w == b"ROOTFS-A-PAYLOAD"), "rootfs A is on its ISO");
        assert!(!iso_b.windows(16).any(|w| w == b"ROOTFS-A-PAYLOAD"), "…and only on its own");

        // …therefore the content ids must differ.
        assert_ne!(
            a.content_id(),
            b.content_id(),
            "two descriptors that master DIFFERENT bytes shared one content_id — a \
             content-addressed build keyed on this hands back the wrong ISO"
        );

        // The per-ISO customer boot-logo is an input too: it is exported as
        // KORP_BOOT_LOGO to the chained cargo build, so it changes the binary
        // that rides the ISO.
        let plain = IsoBuild { from_variant: Some("korp-pid1".into()), ..base.clone() };
        let marked = IsoBuild { boot_logo: Some("assets/customer.webp".into()), ..plain.clone() };
        assert_ne!(
            plain.content_id(),
            marked.content_id(),
            "a customer boot-logo bakes a different payload binary, so it cannot leave the id \
             unchanged — two customers' ISOs would be one cache entry"
        );

        // …and the id is still stable for a descriptor that did not change, so
        // this is a widening and not a randomization.
        assert_eq!(a.content_id(), a.clone().content_id());
    }

    /// A UEFI ISO whose staging tree lacks the `efiboot.img` ESP is an honest hard
    /// error — the writer refuses to fabricate a boot image, mirroring the old
    /// "no masterer ⇒ loud error, never a faked ISO" honesty contract.
    #[test]
    fn pure_rust_builder_errors_when_efi_boot_image_missing() {
        use crate::iso::{IsoBuild, IsoFormat};
        let dir = tempfile::tempdir().unwrap();
        let root = dir.path();
        std::fs::create_dir_all(root.join("iso-root")).unwrap();
        std::fs::write(root.join("iso-root/hello.txt"), b"no ESP here").unwrap();
        let iso = IsoBuild {
            name: "korp-noefi".into(),
            staging: "iso-root".into(),
            volume_id: "KORP".into(),
            format: IsoFormat::Uefi,
            ..Default::default()
        };
        let msg = failed_reason(build_iso(&PureRustIsoBuilder::new(), &iso, "korp", root));
        assert!(msg.contains("efiboot.img") || msg.contains("boot image"), "honest ESP reason: {msg}");
        assert!(!root.join(".iso-out/korp-noefi.iso").exists(), "no faked ISO written");
    }

    /// ★ RED-FIRST — **the rootless socket is the RUNNING uid's, never uid
    /// 1000's.** kit-defects (3): with no `DOCKER_HOST` and no
    /// `XDG_RUNTIME_DIR` (a systemd system unit with `User=`, an `ssh host cmd`
    /// without a login session) the builder looked in `/run/user/1000` — another
    /// account's runtime directory on any box whose build user is not the
    /// desktop's first user. The ladder is DOCKER_HOST, then XDG_RUNTIME_DIR, then
    /// `/run/user/<real uid>`; with none of the three it refuses by name.
    #[test]
    fn the_socket_follows_the_running_uid_not_uid_1000() {
        // DOCKER_HOST wins outright.
        assert_eq!(
            podman_socket_url_from(Some("unix:///srv/p.sock"), Some("/run/user/7"), Some(7)).unwrap(),
            "unix:///srv/p.sock"
        );
        // XDG_RUNTIME_DIR is the session's own answer.
        assert_eq!(
            podman_socket_url_from(None, Some("/run/user/4242"), Some(4242)).unwrap(),
            "unix:///run/user/4242/podman/podman.sock"
        );
        // No XDG_RUNTIME_DIR: the REAL uid's runtime dir — 111 is korp on oden.
        assert_eq!(
            podman_socket_url_from(None, None, Some(111)).unwrap(),
            "unix:///run/user/111/podman/podman.sock",
            "a uid-111 build must not look in uid 1000's runtime dir"
        );
        // An empty variable is unset, not the filesystem root.
        assert_eq!(
            podman_socket_url_from(Some(""), Some(""), Some(111)).unwrap(),
            "unix:///run/user/111/podman/podman.sock"
        );
        // Nothing to go on: refused by name, never a guessed uid.
        let e = podman_socket_url_from(None, None, None).unwrap_err().to_string();
        assert!(e.contains("XDG_RUNTIME_DIR") && e.contains("DOCKER_HOST"), "named refusal: {e}");
        assert!(!e.contains("/run/user/1000"), "no guessed uid in the refusal: {e}");
    }

    /// The real uid is the first field of `/proc/self/status`'s `Uid:` line.
    #[test]
    fn the_real_uid_is_read_off_the_status_text() {
        let status = "Name:\tx\nUmask:\t0002\nUid:\t111\t222\t333\t444\nGid:\t1\t1\t1\t1\n";
        assert_eq!(real_uid_from_status(status), Some(111));
        assert_eq!(real_uid_from_status("Name:\tx\n"), None);
        assert_eq!(real_uid_from_status("Uid:\tnope\n"), None);
    }

    /// The default container engine label stays `bollard`.
    #[test]
    fn build_container_default_engine_label_is_bollard() {
        let known = BuiltImage { image_ref: "x:1".into(), digest: "sha256:aa".into(), bytes: 1, archive: None };
        let out = build_container(
            &FakeImageBuilder { built: known },
            &ContainerBuild { name: "x".into(), base: "scratch".into(), tags: vec!["x:1".into()], ..Default::default() },
            "repo",
            Path::new("."),
        )
        .unwrap();
        assert_eq!(out.attestation.record.engine, "bollard");
    }

    /// ★ RED-FIRST — **the digest a pin is READ from, and the refusal when
    /// there is none.** [`manifest_digest_of`] is the whole of what
    /// [`BollardImageBuilder::manifest_digest`] answers with, lifted out of the
    /// socket so the behaviour that becomes a `[build.toolchain] image_sha256`
    /// line is provable without an engine.
    ///
    /// The case that matters is `None`. An image the store holds but never
    /// pushed or pulled carries NO `RepoDigests` — it has an id and nothing
    /// else. The build path is allowed to shrug and record the id, because a
    /// provenance record must say something about what it built; the pin path
    /// is NOT, because an id pasted where a manifest digest belongs is refused
    /// later by `CargoToolchain::validate` ("must be 64 lowercase hex … the
    /// MANIFEST digest"), and answering one here would only move the refusal to
    /// a place with less context. So `manifest_digest_of` returns `None` and the
    /// caller refuses BY NAME, naming the fix — it never falls back.
    #[test]
    fn a_missing_repo_digest_yields_no_pin_rather_than_a_plausible_one() {
        // The ordinary case: `repo@sha256:…` → the digest half.
        let d = manifest_digest_of(Some(&[
            "localhost/nordisk-cargo-toolchain@sha256:2cf9b012c9d2ffa42b447e5b0d127184e5a3f998dd2574d771477e9c8e366659".to_string(),
        ]));
        assert_eq!(
            d.as_deref(),
            Some("sha256:2cf9b012c9d2ffa42b447e5b0d127184e5a3f998dd2574d771477e9c8e366659")
        );
        // …and the bare hex a declaration carries is that, minus the prefix —
        // the exact shape `CargoToolchain::validate` demands.
        let hex = d.unwrap().trim_start_matches("sha256:").to_string();
        assert_eq!(hex.len(), 64, "{hex}");
        assert!(hex.chars().all(|c| c.is_ascii_digit() || ('a'..='f').contains(&c)), "{hex}");

        // A locally built image: the engine reports no repo digests at all.
        assert_eq!(manifest_digest_of(None), None, "no pin may be invented from nothing");
        assert_eq!(manifest_digest_of(Some(&[])), None, "an empty list is not a digest");

        // An id where a repo digest belongs has no `@`, so it is not silently
        // read as a digest either — this is the shape that actually happened.
        assert_eq!(
            manifest_digest_of(Some(&[
                "72abf41e32e24d1d0423598f4040b72bddaee5c5a62213edb188a513f19be669".to_string()
            ])),
            None,
            "an image id is not a manifest digest"
        );
    }

    /// #4 — a `from_variant` layer is staged into the build context under the
    /// variant name (matching the Containerfile's `COPY ./<variant>`), so a
    /// variant-chained image build actually finds its payload.
    #[cfg(feature = "image-build")]
    #[test]
    fn tar_context_stages_from_variant_artifacts() {
        use std::io::Read;
        let dir = tempfile::tempdir().unwrap();
        let root = dir.path();
        let art = root.join("target/release/tunnr");
        std::fs::create_dir_all(art.parent().unwrap()).unwrap();
        std::fs::write(&art, b"BINARY").unwrap();

        let img = appliance(); // layer: from_variant("full", "/usr/local/bin/tunnr")
        let mut staged = BTreeMap::new();
        staged.insert("full".to_string(), art.clone());

        let tar_bytes = BollardImageBuilder::tar_context(&img, root, &staged).unwrap();

        let mut ar = tar::Archive::new(&tar_bytes[..]);
        let mut names = Vec::new();
        let mut full_bytes = Vec::new();
        for entry in ar.entries().unwrap() {
            let mut e = entry.unwrap();
            let name = e.path().unwrap().to_string_lossy().to_string();
            if name == "full" {
                e.read_to_end(&mut full_bytes).unwrap();
            }
            names.push(name);
        }
        assert!(names.contains(&"Dockerfile".to_string()));
        assert!(names.contains(&"full".to_string()), "from_variant staged under variant name: {names:?}");
        assert_eq!(full_bytes, b"BINARY", "the staged bytes are the produced artifact");
    }

    /// #4 — an unstaged `from_variant` layer is an honest error, not a silently
    /// broken build context.
    #[cfg(feature = "image-build")]
    #[test]
    fn tar_context_errors_when_variant_not_staged() {
        let dir = tempfile::tempdir().unwrap();
        let img = appliance();
        let empty = BTreeMap::new();
        let err = BollardImageBuilder::tar_context(&img, dir.path(), &empty).unwrap_err();
        assert!(format!("{err:#}").contains("no built"), "honest error: {err:#}");
    }
}