build-space/src/lib.rs
//! # holger-build-space
//!
//! Holger's **build-space as data**, the consumer side of nornir's build-thing. The
//! declaration is `holger-recipe`'s Rust code ([`holger_recipe::declared`]); this crate
//! answers from it, projects it onto a [`BuildSurface`] a UI renders, and drives a row
//! through nornir's one [`assemble`] seam (validate, build, content-hash, attest, emit
//! build-info lineage).
//!
//! The rows are ONE product list (lane HOL-PRODUCTS): the four products, each its
//! `cargo install` line (`holger`, `holger-downstream`, `holger-cli`, `holger-ui`), and
//! what only an estate needs (the static appliance and metered servers, the front, the
//! signed wasm plugins, the installer media). Each row declares its linkage (`fat` /
//! `thin`) and each role names its normal case with `default = true`.
//!
//! ```
//! let set = holger_build_space::build_variant_set().unwrap();
//! assert_eq!(set.names(), vec!["holger", "holger-downstream", "holger-cli", "holger-ui", "mvp-server-musl", "mvp-server-metered", "mvp-server-musl-metered", "mvp-server-musl-metered-narrate", "holger-mvp-appliance", "holger-installer", "holger-installer-metered", "holger-appliance-iso", "plugin-allocation", "plugin-skidbladnir", "plugin-rust-toolchain", "estate-front-musl-metered", "estate-front-musl-metered-narrate", "holger-front-installer-metered", "holger-keep", "holger-front-keep", "downstream-musl", "holger-downstream-oneway-installer", "downstream-fetch-musl", "holger-downstream-installer"]);
//! // Every role names exactly one normal case.
//! assert!(set.check_defaults().is_empty());
//! ```
use std::path::{Path, PathBuf};
use anyhow::Result;
use nornir_build_thing::{
assemble, Assemble, AssembleCtx, Assembled, Attestation, BuildInfoRow, BuildInfoSink,
BuildProvenance, BuildSurface, BuildVariantSet, Builders, HostInfo, InMemBuildInfoSink,
Outcome, Toolchain,
};
/// The repo label stamped into the surface + provenance records.
pub const REPO: &str = "holger";
/// Holger's declared build-space as a typed [`BuildVariantSet`] — answered from
/// the CODE ([`holger_recipe::declared`]), the one declaration.
///
/// Until 0.2.0 this parsed an `include_str!` of a crate-local byte-copy of the
/// hand-written `.nornir/build-variants.toml` — the reverse of the law (code →
/// toml). The toml is now EMITTED by `cargo run -p holger-build-thing -- emit`
/// and held to the code by that crate's `toml_parity` test; nothing here reads it.
///
/// Stays `Result` so every existing caller keeps compiling unchanged.
pub fn build_variant_set() -> Result<BuildVariantSet> {
Ok(holger_recipe::declared())
}
/// Project holger's build-space onto the data-driven [`BuildSurface`] a UI renders
/// (one row per variant, each with its materialized `cargo` command, validity, and a
/// Build action).
pub fn build_surface() -> Result<BuildSurface> {
Ok(BuildSurface::from_set(REPO, &build_variant_set()?))
}
/// **Press the build-thing button** for one declared tier: validate the tier
/// against holger's build-space, then capture a content-addressed
/// build-provenance [`Attestation`] describing what building it produced.
///
/// This is holger *consuming* nornir-build-thing's provenance path. It is the
/// dep-light describe/attest half — it does **not** itself shell out to cargo;
/// the caller passes the `artifacts` a build already emitted (their paths under
/// `repo_root`) and whether that build succeeded. The record binds the exact
/// tier descriptor (verbatim), the materialized `cargo` argv + resolved feature/
/// profile/target axes, the VCS rev + working-tree freshness of `repo_root`, the
/// detected [`Toolchain`], the [`HostInfo`] (cores + memory), the outcome, and
/// the SHA-256 of every artifact — then folds it to a stable digest.
///
/// The heavy end-to-end path (dispatch on the tier's kind, spawn `cargo build`,
/// hash the produced executables, attest + emit build-info lineage in one shot) is
/// [`assemble_tier`] / [`build_tier`] over [`nornir_build_thing::assemble`], which
/// the nornir Build tab and `nornir build --variant <tier>` also drive.
/// `press_tier` is the seam a test or an offline tool uses to attest an artifact
/// built out of band.
///
/// The returned attestation is **unsealed** — `is_sealed()` is `false`. holger
/// never fabricates a signature; the real seal is znippy's detached-CMS signer,
/// applied via [`Attestation::seal_with`] behind nornir's `build-provenance`
/// feature.
pub fn press_tier(
name: &str,
repo_root: &Path,
outcome: Outcome,
artifacts: &[PathBuf],
) -> Result<Attestation> {
let set = build_variant_set()?;
let variant = set.require(name)?;
set.validate(variant)?;
BuildProvenance::capture(
variant,
REPO,
repo_root,
Toolchain::detect(),
HostInfo::detect(),
outcome,
artifacts,
)?
.attest()
}
/// **Press the build-thing button END TO END through the unified `assemble()`
/// seam** (nornir-build-thing design §f7) for one declared tier.
///
/// This REPLACES the former hand-rolled `build_and_press` (which shelled the
/// per-tier `cargo` argv itself via `build_variant`). A chosen tier now flows
/// through nornir's ONE polymorphic entry point,
/// [`nornir_build_thing::assemble`]: it validates the tier, **dispatches on its
/// [`ArtifactKind`](nornir_build_thing::ArtifactKind)** — a product is an `exe`, the
/// sold server a `service`, and both lower to the SAME cargo builder — actually builds
/// it, content-hashes the produced artifact(s), and emits the per-row **build-info
/// lineage** (`provenance` / `dep` / `artifact` rows, each artifact row carrying its
/// `content_id`) to `info`. It returns ONE content-addressed [`Assembled`] receipt
/// binding the produced output, the proof, and that lineage.
///
/// Where [`press_tier`] attests a PRE-BUILT artifact the caller supplies (the
/// dep-light describe/attest seam), `assemble_tier` is the real "press the button":
/// build THEN attest, in one shot. `builders` is the injected builder seam — the
/// production [`Builders::new`] for the real cargo-only path, or a fake in tests;
/// `exec_dir` is where the built binary / service lands (the `BinaryArtifact` /
/// `ServiceSpec` install path). Unknown tiers reject UP FRONT (via
/// [`BuildVariantSet::require`]) before any build, with the same tier-listing
/// message [`press_tier`] gives.
///
/// The receipt's proof is **unsealed** — holger never fabricates a signature.
pub fn assemble_tier(
name: &str,
repo_root: &Path,
exec_dir: &Path,
info: &dyn BuildInfoSink,
builders: &dyn Assemble,
) -> Result<Assembled> {
let set = build_variant_set()?;
// Up-front rejection with the tier-listing message, before any build work.
let variant = set.require(name)?.clone();
let mut ctx = AssembleCtx::new(repo_root, exec_dir, info, builders);
ctx.repo = REPO.to_string();
assemble(&set, &variant, &ctx)
}
/// The real cargo-only [`assemble_tier`]: build the tier through
/// [`nornir_build_thing::assemble`] with the production [`Builders`] (the cargo
/// builder; no image / vm), landing binaries under `<repo_root>/target/release`,
/// and collect the per-tier build-info lineage in-process. Returns the
/// [`Assembled`] receipt AND the captured [`BuildInfoRow`] lineage.
///
/// This is what `cargo run -p xtask build --variant <tier>` drives — holger's
/// first-party equivalent of `nornir build --variant <tier>`. It is **heavy and
/// opt-in**: `assemble` spawns `cargo`, so a `release` tier build can take minutes.
pub fn build_tier(name: &str, repo_root: &Path) -> Result<(Assembled, Vec<BuildInfoRow>)> {
let exec_dir = repo_root.join("target").join("release");
let sink = InMemBuildInfoSink::new();
let assembled = assemble_tier(name, repo_root, &exec_dir, &sink, &Builders::new())?;
Ok((assembled, sink.rows()))
}
#[cfg(test)]
mod tests {
use super::*;
const NAMES: [&str; 24] = [
"holger",
"holger-downstream",
"holger-cli",
"holger-ui",
"mvp-server-musl",
"mvp-server-metered",
"mvp-server-musl-metered",
"mvp-server-musl-metered-narrate",
"holger-mvp-appliance",
"holger-installer",
"holger-installer-metered",
"holger-appliance-iso",
"plugin-allocation",
"plugin-skidbladnir",
"plugin-rust-toolchain",
"estate-front-musl-metered",
"estate-front-musl-metered-narrate",
"holger-front-installer-metered",
"holger-keep",
"holger-front-keep",
"downstream-musl",
"holger-downstream-oneway-installer",
"downstream-fetch-musl",
"holger-downstream-installer",
];
/// The descriptor enumerates the one product list, in declaration order, and every
/// role names its normal case.
#[test]
fn enumerates_the_one_product_list() {
let set = build_variant_set().expect("parse holger build-space");
assert_eq!(set.names(), NAMES.to_vec());
assert!(
set.check_defaults().is_empty(),
"{:?}",
set.check_defaults()
);
// Coverage law: record the declaration as a test-matrix cell (reuse edda's
// emitter; no-op unless the `testmatrix` feature is on).
#[cfg(feature = "testmatrix")]
nornir_testmatrix::functional_status(
"holger-build-space",
"one_product_list_declared",
set.names() == NAMES.to_vec() && set.check_defaults().is_empty(),
"the four products + the estate's builds, each role naming its default",
);
}
/// Each product row builds its own crate's one bin with its defaults; the estate's
/// builds of the server are the same crate with their own feature sets.
#[test]
fn each_row_has_the_right_target_and_feature_combo() {
let set = build_variant_set().unwrap();
for p in ["holger", "holger-downstream", "holger-cli", "holger-ui"] {
let v = set.get(p).unwrap_or_else(|| panic!("no product row {p}"));
assert_eq!(v.package.as_deref(), Some(p), "{p}");
assert_eq!(v.bin.as_deref(), Some(p), "{p}");
assert!(
v.features.is_empty() && !v.no_default_features,
"{p}: `cargo install {p}`, its defaults"
);
}
// The static appliance: the server crate, its own feature set, cross-built for musl.
let musl = set.get("mvp-server-musl").expect("the appliance row");
assert_eq!(musl.package.as_deref(), Some("holger"));
assert_eq!(musl.bin.as_deref(), Some("holger-mvp"));
assert!(musl.no_default_features);
assert_eq!(musl.target.as_deref(), Some("x86_64-unknown-linux-musl"));
for f in [
"mvp-server",
"appliance",
"paired",
"auth-passkey",
"sign",
"allow-meter",
] {
assert!(
musl.features.iter().any(|x| x == f),
"mvp-server-musl names `{f}`: {:?}",
musl.features
);
}
// The sold server: the paid door and its own bin.
let sold = set.get("mvp-server-metered").expect("the sold row");
assert_eq!(sold.bin.as_deref(), Some("holger-mvp-metered"));
assert!(sold.features.iter().any(|x| x == "metered"));
}
/// No row group is mutually exclusive any more (the extras are features of the same
/// crates), and each estate bin requires its marker feature.
#[test]
fn no_exclusive_group_and_the_estate_bins_require_their_marker() {
let set = build_variant_set().unwrap();
assert!(set.exclusive.is_empty(), "{:?}", set.exclusive);
assert_eq!(
set.required.get("holger-mvp").map(Vec::as_slice),
Some(&["mvp-server".to_string()][..])
);
assert_eq!(
set.required.get("holger-mvp-metered").map(Vec::as_slice),
Some(&["mvp-server".to_string(), "metered".to_string()][..])
);
assert_eq!(set.required.len(), 2, "{:?}", set.required);
}
/// Every feature a row enables is one holger actually OFFERS, and every declared
/// variant validates against the set's rules (config-time sanity).
#[test]
fn every_row_validates_against_the_offered_feature_universe() {
let set = build_variant_set().unwrap();
for f in [
"sign",
"testmatrix",
"served",
"front",
"all-formats",
"online",
"federation",
"full",
] {
assert!(
set.offers.contains(f),
"holger offers the real feature '{f}'"
);
}
assert!(
!set.offers.contains("gui"),
"`gui` is holger-egui's, not holger's"
);
set.validate_all().expect("every row is internally valid");
}
/// The materialized cargo command lines are the argv nornir would run.
#[test]
fn rows_materialize_the_expected_cargo_commands() {
let set = build_variant_set().unwrap();
let holger = set.get("holger").unwrap().command_line();
assert!(
holger.starts_with("cargo build -p holger --bin holger"),
"{holger}"
);
assert!(
!holger.contains("--features") && !holger.contains("--no-default-features"),
"the product is its defaults: {holger}"
);
let musl = set.get("mvp-server-musl").unwrap().command_line();
for want in [
"-p holger",
"--bin holger-mvp",
"--no-default-features",
"--target x86_64-unknown-linux-musl",
"--profile release",
] {
assert!(musl.contains(want), "{want} missing: {musl}");
}
}
/// The reflected surface is what a UI renders: one row per declared variant, all
/// valid, each with a `nornir build --variant <name>` action. The count is the row
/// count: every media cell is claimed by a pointer variant of the same name.
#[test]
fn surface_reflects_valid_rows_with_build_actions() {
let surface = build_surface().unwrap();
assert_eq!(surface.repo, REPO);
assert_eq!(surface.variant_names(), NAMES.to_vec());
assert_eq!(surface.variants.len(), NAMES.len());
assert!(
surface.variants.iter().all(|v| v.valid),
"no invalid variant"
);
for (v, row) in surface.variants.iter().zip(surface.variant_names()) {
assert_eq!(v.build_action.cli, format!("nornir build --variant {row}"));
}
let j = surface.state_json();
assert_eq!(j["surface"], "build");
assert_eq!(j["variant_count"], NAMES.len());
assert_eq!(j["valid_count"], NAMES.len());
}
use nornir_build_thing::{
ArtifactKind, BuildOutcome, BuildProvenanceSink, JsonFileSink, PROVENANCE_SCHEMA,
};
/// A **fake cargo builder** for the `assemble()` seam: instead of shelling
/// `cargo`, it synthesizes a deterministic artifact file (`target/release/<bin>`,
/// bytes `ARTIFACT <tier>`) and captures REAL build-provenance over it — so the
/// whole `assemble` dispatch + content-hash + build-info lineage machinery runs
/// with no toolchain. Container / VM kinds are never reached (the rows driven here are
/// all cargo kinds).
struct FakeCargo;
impl Assemble for FakeCargo {
fn build_cargo(
&self,
set: &BuildVariantSet,
name: &str,
repo_root: &Path,
_sink: &dyn BuildInfoSink,
) -> Result<BuildOutcome> {
let v = set.require(name)?.clone();
let dir = repo_root.join("target/release");
std::fs::create_dir_all(&dir)?;
let bin = v
.bin
.clone()
.or_else(|| v.package.clone())
.unwrap_or_else(|| v.name.clone());
let art = dir.join(&bin);
std::fs::write(&art, format!("ARTIFACT {name}"))?;
let rec = BuildProvenance::capture(
&v,
REPO,
repo_root,
Toolchain {
rustc: "rustc 1.85.0".into(),
host: "x86_64-unknown-linux-gnu".into(),
cargo: "cargo 1.85.0".into(),
// `Toolchain` grew `image`/`image_sha256` (edda f0aff77) so a
// provenance record can name WHICH container built an
// artifact. This is a fake build in a test double, so it ran
// in no container and claims none.
..Default::default()
},
HostInfo {
cores: 8,
memory_bytes: 0,
},
Outcome::Success,
std::slice::from_ref(&art),
)?;
Ok(BuildOutcome {
variant: name.to_string(),
artifacts: vec![art],
attestation: rec.attest()?,
success: true,
})
}
}
/// **RED-WHEN-BROKEN — the rows validate + produce their artifacts via
/// `assemble()`.** Each is driven through the unified f7 [`assemble`] seam (with a
/// fake cargo builder, no toolchain): it must validate, dispatch on its declared
/// [`ArtifactKind`] (a product → `exe`, the sold server → `service`), produce a
/// content-addressed artifact, and emit a build-info lineage carrying provenance +
/// artifact rows. A `kind` out of coherence with its `embeds`/`member` is rejected
/// by `assemble`'s config gate and this test goes RED.
#[test]
fn rows_validate_and_assemble_produce_artifacts() {
let dir = tempfile::tempdir().unwrap();
let root = dir.path();
let exec = Path::new("/usr/local/bin");
let sink = InMemBuildInfoSink::new();
let fake = FakeCargo;
for (tier, kind) in [
("holger", ArtifactKind::Exe),
("holger-downstream", ArtifactKind::Exe),
("mvp-server-metered", ArtifactKind::Service),
] {
let a = assemble_tier(tier, root, exec, &sink, &fake)
.unwrap_or_else(|e| panic!("assemble tier '{tier}': {e:#}"));
// Dispatched on the declared kind — output + proof both agree.
assert_eq!(a.kind(), kind, "{tier}: output.kind() is the declared kind");
assert_eq!(a.attestation.kind(), kind, "{tier}: proof kind matches");
// Produced a content-addressed artifact.
assert!(
!a.artifact_content_ids().is_empty(),
"{tier}: assemble produced an artifact with a content_id"
);
// The build-info lineage carries the record ABOUT the build.
assert!(
a.lineage.iter().any(|r| r.kind == "provenance"),
"{tier}: lineage has provenance rows"
);
assert!(
a.lineage
.iter()
.any(|r| r.kind == "artifact" && !r.content_id.is_empty()),
"{tier}: lineage has a content-addressed artifact row"
);
// Content-addressed digest holds; unsealed (holger never fakes a seal).
assert!(
a.attestation.digest_matches_record().unwrap(),
"{tier}: digest verifies"
);
assert!(!a.attestation.is_sealed(), "{tier}: unsealed");
}
// Coverage law: record the assemble verdict as a test-matrix cell.
#[cfg(feature = "testmatrix")]
nornir_testmatrix::functional_status(
"holger-build-space",
"rows_assemble",
true,
"holger(exe) · holger-downstream(exe) · mvp-server-metered(service) validate + assemble() → artifacts + lineage",
);
}
/// A throw-away "repo" holding one fake produced artifact under
/// `target/release/<bin>`, so provenance capture can content-hash a real file
/// without a heavy release build.
fn fake_build(bin: &str, bytes: &[u8]) -> (tempfile::TempDir, PathBuf) {
let dir = tempfile::tempdir().expect("tempdir");
let out = dir.path().join("target/release");
std::fs::create_dir_all(&out).unwrap();
let art = out.join(bin);
std::fs::write(&art, bytes).unwrap();
(dir, art)
}
/// Pressing the `holger` row captures a provenance attestation that binds the
/// row descriptor + materialized argv + the artifact's content hash, folds to a
/// stable digest, is unsealed (holger never fakes a signature), and round-trips
/// through JSON with its digest intact.
#[test]
fn press_holger_row_captures_and_round_trips_provenance() {
let (dir, art) = fake_build("holger", b"ELF holger bytes");
let att = press_tier("holger", dir.path(), Outcome::Success, &[art])
.expect("press the holger row");
// WHAT: the verbatim `holger` row descriptor rode into the record.
assert_eq!(att.record.schema, PROVENANCE_SCHEMA);
assert_eq!(att.record.repo, REPO);
assert_eq!(att.record.variant.name, "holger");
assert_eq!(att.record.variant.package.as_deref(), Some("holger"));
assert_eq!(att.record.variant.bin.as_deref(), Some("holger"));
// INPUTS: the materialized argv matches the row's own cargo_args.
let row = build_variant_set().unwrap().get("holger").unwrap().clone();
assert_eq!(att.record.inputs.cargo_args, row.cargo_args());
assert_eq!(att.record.inputs.profile, "release");
assert_eq!(att.record.outcome, Outcome::Success);
// HOST: cores are probed (>=1 on any real machine).
assert!(att.record.host.cores >= 1, "host cores were probed");
// HASHES: the produced artifact is content-addressed + sized.
assert_eq!(att.record.artifacts.len(), 1);
let a = &att.record.artifacts[0];
assert_eq!(a.path, "target/release/holger");
assert_eq!(a.bytes, b"ELF holger bytes".len() as u64);
// SEAL: unsealed, but the digest verifies against the record.
assert!(!att.is_sealed(), "holger never fabricates a seal");
assert!(att.digest_matches_record().unwrap());
// ROUND-TRIP: JSON out → in preserves the digest and it still verifies.
let back = Attestation::from_json(&att.to_json().unwrap()).unwrap();
assert_eq!(back.digest, att.digest);
assert!(back.digest_matches_record().unwrap());
}
/// The captured record persists to a dep-light `JsonFileSink` ledger and
/// reloads as a full, re-parseable provenance entry — the warehouse-ledger
/// seam a leaf repo uses without nornir's Iceberg stack.
#[test]
fn pressed_record_persists_to_a_reloadable_ledger() {
let (dir, art) = fake_build("holger-downstream", b"downstream bytes");
let att = press_tier("holger-downstream", dir.path(), Outcome::Success, &[art]).unwrap();
let ledger_dir = tempfile::tempdir().unwrap();
let sink = JsonFileSink::new(ledger_dir.path());
sink.record(&att.record).unwrap();
sink.record(&att.record).unwrap();
let text = std::fs::read_to_string(sink.path()).unwrap();
assert_eq!(text.lines().count(), 2, "one JSON line per record");
let first = BuildProvenance::from_json(text.lines().next().unwrap()).unwrap();
assert_eq!(first.repo, REPO);
assert_eq!(first.variant.name, "holger-downstream");
}
/// Pressing a row holger doesn't declare is rejected up front, naming the real rows.
#[test]
fn pressing_an_unknown_tier_is_rejected() {
let err = press_tier("turbo", Path::new("."), Outcome::Success, &[])
.expect_err("unknown tier must reject");
let msg = err.to_string();
assert!(msg.contains("no build variant 'turbo'"), "{msg}");
assert!(
msg.contains("holger-downstream") && msg.contains("mvp-server-musl"),
"lists rows: {msg}"
);
}
/// The end-to-end [`assemble_tier`] rejects an unknown tier UP FRONT (via
/// `require`) with the exact same message [`press_tier`] gives — proving the
/// validation happens BEFORE any build, and the injected builder is never
/// invoked (no `target/` dir is created).
#[test]
fn assemble_tier_rejects_unknown_tier_before_building() {
let dir = tempfile::tempdir().unwrap();
let sink = InMemBuildInfoSink::new();
let fake = FakeCargo;
let err = assemble_tier("turbo", dir.path(), Path::new("/usr/bin"), &sink, &fake)
.expect_err("unknown tier must reject before building");
let msg = err.to_string();
assert!(msg.contains("no build variant 'turbo'"), "{msg}");
assert!(
msg.contains("holger-downstream") && msg.contains("mvp-server-musl"),
"lists rows: {msg}"
);
// Same up-front rejection message as the dep-light describe/attest seam.
let press_err = press_tier("turbo", Path::new("."), Outcome::Success, &[])
.expect_err("press_tier also rejects");
assert_eq!(
msg,
press_err.to_string(),
"same rejection message as press_tier"
);
// The fake builder was never reached — no build artifacts materialized.
assert!(
!dir.path().join("target").exists(),
"no build happened for an unknown tier"
);
}
/// HEAVY end-to-end: actually build the `holger-downstream` row THROUGH
/// `assemble()` against the real holger workspace, hash the produced binary, and
/// attest it. Ignored by default because `assemble` shells `cargo build` (minutes on
/// a cold cache).
///
/// Run it explicitly from the workspace root:
/// ```text
/// cargo test -p holger-build-space -- --ignored assemble_the_downstream_row_builds_and_attests
/// ```
#[test]
#[ignore = "heavy: assemble() shells `cargo build` for the holger-downstream row against the real workspace"]
fn assemble_the_downstream_row_builds_and_attests() {
// Repo root = the workspace dir above build-space/.
let repo_root = Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("build-space has a parent (workspace root)")
.to_path_buf();
let (assembled, lineage) = build_tier("holger-downstream", &repo_root)
.expect("build + assemble the holger-downstream row");
// The product is declared an `exe` kind — dispatch proves it.
assert_eq!(
assembled.kind(),
ArtifactKind::Exe,
"holger-downstream is an exe"
);
assert_eq!(assembled.attestation.kind(), ArtifactKind::Exe);
// The produced binary was discovered + content-hashed into the lineage.
assert!(
!assembled.artifact_content_ids().is_empty(),
"assemble produced a content-addressed artifact"
);
assert!(
lineage
.iter()
.any(|r| r.kind == "artifact" && !r.content_id.is_empty()),
"the build-info lineage carries a content-addressed artifact row"
);
// Content-addressed digest holds, and holger never fabricates a seal.
assert!(
assembled.attestation.digest_matches_record().unwrap(),
"digest verifies against the record"
);
assert!(
!assembled.attestation.is_sealed(),
"holger never fabricates a seal"
);
}
/// Workspace root = the dir above `build-space/`.
fn workspace_root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("build-space has a parent (workspace root)")
.to_path_buf()
}
/// Resolve the exact `cargo tree` dependency closure (`-e no-dev`, no dev-deps)
/// for `package` built with `features` and `no_default_features`.
/// Returns the full tree text so a test can assert a crate's presence/absence.
/// Pure metadata resolution — cargo does NOT compile anything, so this is fast
/// and box-safe (Law #8: no heavy build here).
fn cli_closure(package: &str, features: &str, no_default_features: bool) -> String {
let mut args = vec!["tree".to_string()];
// `holger-downstream` is a workspace of its own (its rows' `manifest_dir`).
if package == holger_recipe::variants::DOWNSTREAM {
args.extend([
"--manifest-path".to_string(),
format!(
"{}/Cargo.toml",
holger_recipe::variants::DOWNSTREAM_MANIFEST_DIR
),
]);
}
args.extend([
"-p".to_string(),
package.to_string(),
"-e".to_string(),
"no-dev".to_string(),
]);
if !features.is_empty() {
args.extend(["--features".to_string(), features.to_string()]);
}
if no_default_features {
args.push("--no-default-features".to_string());
}
let out = std::process::Command::new(env!("CARGO"))
.args(&args)
.current_dir(workspace_root())
.output()
.expect("run `cargo tree`");
assert!(
out.status.success(),
"cargo tree ({features}) failed: {}",
String::from_utf8_lossy(&out.stderr)
);
String::from_utf8_lossy(&out.stdout).into_owned()
}
/// Every Rust outbound-HTTP client crate an airgap tier is forbidden to link.
/// `reqwest`/`ureq` are holger's actual egress paths today (OIDC `/userinfo` +
/// `jwks_uri`, the `bench` GET-bombardment driver, and the `rust-remote`
/// pull-through upstream); the rest are the other well-known Rust HTTP clients,
/// denied so a FUTURE re-introduction of egress via a *different* client is
/// caught red-handed too — the zero-egress guarantee must not hinge on the one
/// client name holger happens to use now. `hyper`/`tonic`/`h2` are deliberately
/// ABSENT from this list: they are the INBOUND gRPC *serve* stack (every tier
/// legitimately links them), not an outbound client.
const FORBIDDEN_OUTBOUND_CLIENTS: &[&str] = &[
"reqwest",
"ureq",
"isahc",
"curl",
"surf",
"attohttpc",
"awc",
"http-client",
];
/// AIRGAP KEYSTONE — provably zero-egress. The server and the downstream with their
/// defaults (`cargo install holger`, `cargo install holger-downstream`) link NO
/// outbound HTTP client ([`FORBIDDEN_OUTBOUND_CLIENTS`]). `--features full` DOES link
/// reqwest + ureq, which proves the test discriminates rather than trivially passing.
/// Runs `cargo tree` — a metadata resolve, no compile.
#[test]
fn the_products_link_no_outbound_http_client() {
for package in ["holger", "holger-downstream"] {
let tree = cli_closure(package, "", false);
for client in FORBIDDEN_OUTBOUND_CLIENTS {
// Match `<crate> v` so a crate merely NAMED after one can't cause a
// false positive; cargo-tree prints `name vX.Y`.
assert!(
!tree.contains(&format!("{client} v")),
"`cargo install {package}` MUST NOT link outbound HTTP client '{client}' \
(zero-egress), but the cargo-tree closure contains it:\n{tree}"
);
}
}
let full = cli_closure("holger", "full", false);
assert!(
full.contains("reqwest v"),
"`--features full` is the online server and MUST link reqwest:\n{full}"
);
assert!(
full.contains("ureq v"),
"`--features full` MUST link ureq:\n{full}"
);
// Coverage law: record the zero-egress verdict as a test-matrix cell.
#[cfg(feature = "testmatrix")]
nornir_testmatrix::functional_status(
"holger-build-space",
"products_zero_egress",
true,
"holger/holger-downstream link no outbound HTTP client; --features full links reqwest+ureq",
);
}
/// Tampering with a pressed record after the fact breaks its digest — the
/// content-addressed provenance is a real integrity check.
#[test]
fn tampering_a_pressed_record_breaks_the_digest() {
let (dir, art) = fake_build("holger", b"holger bytes");
let mut att = press_tier("holger", dir.path(), Outcome::Success, &[art]).unwrap();
assert!(att.digest_matches_record().unwrap());
att.record.inputs.vcs_rev = "0".repeat(40);
assert!(
!att.digest_matches_record().unwrap(),
"mutation invalidates the digest"
);
}
}