holger-recipe/tests/every_shape_stays_slim.rs
//! **Every shape stays slim** (Holger v2.0, phase H2; A31 §4.4, owner 2026-10-25).
//!
//! One table, one test: each build shape Holger ships is held to a crate-count bound
//! AND to a list of crates it must never link. The count catches a feature that rode
//! back into a shape; the names catch the one crate that matters even when the count
//! has room (a renderer, a wasm runtime, an estate crate in the plain server).
//!
//! The trees are `cargo tree -e normal --prefix none`, distinct lines with ` (*)`
//! trimmed, the same count `cargo_install_holger.rs` has always used. A row shape
//! takes its package, features and target from the declared row
//! (`holger_recipe::variants::variants()`), never a copy of them.
//!
//! A forbidden name ending in `*` is a prefix (`monetize*`).
use std::collections::BTreeSet;
use std::path::{Path, PathBuf};
use std::process::Command;
#[path = "support/known_crates.rs"]
mod known_crates;
use holger_recipe::variants::variants;
fn repo_root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.to_path_buf()
}
/// Where a shape's features come from.
#[derive(Clone, Copy)]
enum From {
/// `cargo tree -p <package>` with these extra arguments.
Args(&'static str, &'static [&'static str]),
/// A declared Build Thing row, by name.
Row(&'static str),
}
struct Shape {
label: &'static str,
from: From,
/// At most this many distinct crates.
bound: usize,
/// Never these crates (a trailing `*` is a prefix).
forbidden: &'static [&'static str],
}
/// The renderer of the estate board. The server serves the board as DATA (JSON and
/// `AdminService.Topology`'s rows); holger-ui draws it. No server build links it (H2 #1).
const BOARD_RENDERER: &str = "nordisk-estate-svg";
/// The wasm runtime. Only the sold rows run a wasm guest; the open rows' meter is built in
/// (`allow-meter`, H2 #2).
const WASM_RUNTIME: &[&str] = &["wasmtime*", "cranelift*"];
const OPEN_SERVER_ROW: &[&str] = &["monetize*", BOARD_RENDERER, "wasmtime*", "cranelift*"];
/// The OpenPGP verifier (rpgp): only `maven-pgp` builds it, never `online` (H2 #3).
const PGP: &str = "pgp";
/// The estate crates: only an estate row links them (H2's estate cut, server-lib `estate`).
const ESTATE: &[&str] = &["nordisk-twin", "appliance-grow", "disk-snapshot"];
/// The intake chain's crates: `jobs` is the job queue alone and carries none (H2 #3).
const INTAKE_CHAIN: &[&str] = &[
"holger-provenance",
"holger-sandbox",
"holger-inspect",
"modgunn",
];
/// The airgap rule (docs/HOLGER-V2.md): the default and the downstream build link no
/// outbound client. A source or an upstream is configured, never linked by default.
const OUTBOUND: &[&str] = &[
"reqwest",
"ureq",
"hyper-rustls",
"isahc",
"curl",
"attohttpc",
"surf",
"instant-acme",
"hyper-tls",
"native-tls",
"openssl",
];
/// OWNER RULE 7: the public site / SEO (IndexNow is gunnar-public-site's module), MCP and the
/// upstream pull modules.
const NEVER_OPEN: &[&str] = &[
"holger-front",
"holger-public-site",
"gunnar-public-site",
"rmcp",
"rmcp-macros",
"holger-mcp",
"gunnar-mcp",
"holger-world-connector",
"holger-nexus-connector",
"holger-artifactory-connector",
"holger-agent-lib",
];
/// The open upstream row: the default, nothing sold, no renderer, no wasm host, no outbound client.
const UPSTREAM_ROW: &[&str] = &[
"monetize*",
BOARD_RENDERER,
"wasmtime*",
"cranelift*",
"reqwest",
"ureq",
"hyper-rustls",
"isahc",
"curl",
"attohttpc",
"surf",
"instant-acme",
"hyper-tls",
"native-tls",
"openssl",
"holger-front",
"holger-public-site",
"gunnar-public-site",
"rmcp",
"rmcp-macros",
"holger-mcp",
"gunnar-mcp",
"holger-world-connector",
"holger-nexus-connector",
"holger-artifactory-connector",
"holger-agent-lib",
// H2's estate cut (HCUT): the twin, the grow plane, the disk snapshot.
"nordisk-twin",
"appliance-grow",
"disk-snapshot",
];
/// The open downstream row: the upstream's list, and no scanner, no PGP and no APK re-signer
/// (it trusts the upstream's signed scan evidence; `downstream_shape.rs` names the families).
const DOWNSTREAM_ROW: &[&str] = &[
"monetize*",
BOARD_RENDERER,
"wasmtime*",
"cranelift*",
"reqwest",
"ureq",
"hyper-rustls",
"isahc",
"curl",
"attohttpc",
"surf",
"instant-acme",
"hyper-tls",
"native-tls",
"openssl",
// Not `holger-sandbox`: the downstream's bundle verification runs in a job over it
// (lane HOL-SANDBOX-TRAIT).
"modgunn",
"holger-inspect",
"holger-scan-worker",
"holger-intel",
"pgp",
"rsa",
"nordisk-pairing",
"boringtun",
"korp-installer",
"holger-front",
"holger-public-site",
"gunnar-public-site",
"rmcp",
"rmcp-macros",
"holger-mcp",
"gunnar-mcp",
"holger-world-connector",
"holger-nexus-connector",
"holger-artifactory-connector",
"holger-agent-lib",
"nordisk-twin",
"appliance-grow",
"disk-snapshot",
];
/// The command-line tool's row: tonic's client half and nothing of the server, the offline
/// tools (the bundle crate, the courier without its connectors), no outbound client of its
/// own, nothing sold and none of the open server's surfaces.
/// The tool's bound (its row measured, plus room for a patch release).
const CLI_BOUND: usize = 250;
const CLI_ROW: &[&str] = &[
"holger-server-lib",
"axum",
"monetize*",
BOARD_RENDERER,
"wasmtime*",
"cranelift*",
"reqwest",
"ureq",
"hyper-rustls",
"isahc",
"curl",
"attohttpc",
"surf",
"instant-acme",
"hyper-tls",
"native-tls",
"openssl",
"holger-front",
"holger-public-site",
"gunnar-public-site",
"rmcp",
"rmcp-macros",
"holger-world-connector",
"holger-nexus-connector",
"holger-artifactory-connector",
];
const SHAPES: &[Shape] = &[
// The four products' rows (HOL-PRODUCTS), each its `cargo install` line; the console's
// is held in holger-ui (`ui_row_equals_cargo_install.rs`). Measured on dwarf 2026-10-30
// with the twelve formats: holger-cli 240, holger 344, holger-downstream 328.
Shape {
label: "row holger-cli",
from: From::Row("holger-cli"),
bound: CLI_BOUND,
forbidden: CLI_ROW,
},
// Lane HOL-SANDBOX-TRAIT: holger 349 -> 359, holger-downstream 328 -> 339 (the job runner,
// holger-sandbox in the downstream, Heimdall's heimdall-core and its older crypto line).
Shape {
label: "row holger",
from: From::Row("holger"),
bound: 365,
forbidden: UPSTREAM_ROW,
},
Shape {
label: "row holger-downstream",
from: From::Row("holger-downstream"),
bound: 345,
forbidden: DOWNSTREAM_ROW,
},
// `cargo install holger --features online`: the pull client, and no OpenPGP (H2 #3): 381.
// +10 since lane HOL-SANDBOX-TRAIT (386 -> 396, oden 2026-10-30): every bundle and scan is a
// job, so the intake build links holger-job-runner and Heimdall's heimdall-core v0.1.0, whose
// ed25519-dalek 2 / sha2 0.10 / getrandom 0.2 line sits beside the newer ones.
// +12 since lane hol-312fix (field test F2, 400 -> 412 on build-1 2026-11-03): `online`
// brings `jobs` (jera and the APK pull-through), the queue every pull-through runs on;
// without it `online` loaded its sources and never pulled.
Shape {
label: "default + online",
from: From::Args("holger", &["--features", "online"]),
bound: 415,
forbidden: &[
"monetize*",
BOARD_RENDERER,
PGP,
ESTATE[0],
ESTATE[1],
ESTATE[2],
],
},
// server-lib's job queue alone: no intake chain rides in with it (H2 #3).
Shape {
label: "server-lib jobs alone",
from: From::Args(
"holger-server-lib",
&["--no-default-features", "--features", "jobs"],
),
bound: 250,
forbidden: INTAKE_CHAIN,
},
// server-lib's own default (with `online`): no estate crate (the twin, the grow plane,
// the disk snapshot). 362 at the estate cut (HCUT, builder-64 2026-10-26).
Shape {
label: "server-lib default",
from: From::Args("holger-server-lib", &[]),
bound: 365,
forbidden: ESTATE,
},
// server-lib with every feature on: no feature of the server library reaches the renderer.
// 602 since HOL-NCSC's PKCS#11 evidence signer (094aa8cd, feature `pkcs11`, default off):
// +4, cryptoki, cryptoki-sys, libloading (the module is loaded at runtime) and secrecy
// (the PIN). Measured 598 just before it (3bf1996f), with bzip2 + libbz2-rs-sys from
// HOL-NCSC's deny-bzip2 already in.
Shape {
label: "server-lib, all features",
from: From::Args("holger-server-lib", &["--all-features"]),
// +5 with lane HOL-HEIMDALL-02's `heimdall` (608, oden 2026-10-31): nordisk-heimdall-
// container and -plain 0.2.0, landlock, enumflags2 and its derive. +17 with
// HOL-HEIMDALL-04's `heimdall-wasm` (625, oden 2026-11-01): nordisk-heimdall-run and
// -wasm, wasmtime's component macros and wit-bindgen, and toml 0.8's line.
bound: 630,
forbidden: &[BOARD_RENDERER],
},
// The static appliance (the suite's main and twin, the ISO and installer payload): 406.
Shape {
label: "row mvp-server-musl",
from: From::Row("mvp-server-musl"),
// +5 with the appliance's `heimdall` (lane HOL-HEIMDALL-03; 420 on oden 2026-10-31):
// nordisk-heimdall-container and -plain, landlock, enumflags2 and its derive.
bound: 425,
forbidden: OPEN_SERVER_ROW,
},
// The sold MVP server row. 485 before H2.
// +4 since lane HOL-SANDBOX-TRAIT (488 -> 492): the job runner and heimdall-core.
Shape {
label: "row mvp-server-metered",
from: From::Row("mvp-server-metered"),
// +5 with `heimdall` (lane HOL-HEIMDALL-03; 501 on oden 2026-10-31).
bound: 505,
forbidden: &[BOARD_RENDERER],
},
];
/// The `cargo tree` arguments of a shape.
fn tree_args(from: From) -> Vec<String> {
let mut a: Vec<String> = ["tree", "-e", "normal", "--prefix", "none"]
.map(String::from)
.to_vec();
match from {
From::Args(package, extra) => {
a.extend(["-p".to_string(), package.to_string()]);
a.extend(extra.iter().map(|s| s.to_string()));
}
From::Row(name) => {
let all = variants();
let v = all
.iter()
.find(|v| v.name == name)
.unwrap_or_else(|| panic!("no declared row `{name}`"));
let package = v
.package
.clone()
.unwrap_or_else(|| panic!("row `{name}` names no package"));
if let Some(dir) = &v.manifest_dir {
a.extend(["--manifest-path".to_string(), format!("{dir}/Cargo.toml")]);
}
a.extend(["-p".to_string(), package]);
if v.no_default_features {
a.push("--no-default-features".into());
}
if !v.features.is_empty() {
a.extend(["--features".to_string(), v.features.join(",")]);
}
if let Some(t) = &v.target {
a.extend(["--target".to_string(), t.clone()]);
}
}
}
a
}
/// Pure: the distinct crates of a `--prefix none` listing.
fn crates(tree: &str) -> BTreeSet<String> {
tree.lines()
.map(|l| l.trim_end_matches(" (*)").trim())
.filter(|l| !l.is_empty())
.map(String::from)
.collect()
}
/// Pure: the forbidden crates a listing holds.
fn forbidden_in(crates: &BTreeSet<String>, forbidden: &[&str]) -> Vec<String> {
let names: BTreeSet<&str> = crates
.iter()
.filter_map(|l| l.split_whitespace().next())
.collect();
let mut hits = Vec::new();
for f in forbidden {
match f.strip_suffix('*') {
Some(prefix) => hits.extend(
names
.iter()
.filter(|n| n.starts_with(prefix))
.map(|n| n.to_string()),
),
None if names.contains(f) => hits.push(f.to_string()),
None => {}
}
}
hits
}
#[test]
fn every_shape_stays_slim() {
let mut bad = Vec::new();
for s in SHAPES {
let args = tree_args(s.from);
let out = Command::new(env!("CARGO"))
.current_dir(repo_root())
.args(&args)
.output()
.expect("run cargo tree");
if !out.status.success() {
bad.push(format!(
"{}: cargo {} failed:\n{}",
s.label,
args.join(" "),
String::from_utf8_lossy(&out.stderr)
));
continue;
}
let tree = String::from_utf8_lossy(&out.stdout);
let set = crates(&tree);
assert!(
set.len() > 50,
"{}: an empty tree proves nothing:\n{tree}",
s.label
);
println!("{}: {} crates (bound {})", s.label, set.len(), s.bound);
if set.len() > s.bound {
bad.push(format!(
"{}: {} crates, over the bound of {}: something rode back into this shape",
s.label,
set.len(),
s.bound
));
}
let hits = forbidden_in(&set, s.forbidden);
if !hits.is_empty() {
bad.push(format!(
"{}: links {hits:?}, which this shape must never link",
s.label
));
}
}
assert!(bad.is_empty(), "a shape is not slim:\n{}", bad.join("\n"));
}
#[test]
fn a_forbidden_prefix_and_a_forbidden_name_are_found_and_nothing_else() {
let set = crates(
"holger v2.0.0\nmonetize-embed v0.1.0 (*)\nnordisk-estate-svg v0.1.0\ntokio v1.47.1\n",
);
assert_eq!(
forbidden_in(&set, &["monetize*", BOARD_RENDERER]),
vec!["monetize-embed", "nordisk-estate-svg"]
);
assert!(forbidden_in(&set, &["wasmtime", "pgp"]).is_empty());
assert!(
WASM_RUNTIME.iter().all(|w| OPEN_SERVER_ROW.contains(w)),
"the open rows forbid the whole wasm runtime"
);
for row in [UPSTREAM_ROW, DOWNSTREAM_ROW] {
assert!(
OUTBOUND.iter().all(|c| row.contains(c)),
"the airgap rule: an open row forbids every outbound client"
);
assert!(
OPEN_SERVER_ROW.iter().all(|c| row.contains(c)),
"an open row forbids what every open server row forbids"
);
assert!(
NEVER_OPEN.iter().all(|c| row.contains(c)),
"owner rule 7: an open row forbids the public site, MCP, IndexNow and the pull modules"
);
}
}
/// OWNER RULE 7 (2026-10-26): the downstream, and the upstream's default, never carry the
/// public site / SEO, MCP, IndexNow (gunnar-public-site's module) or an upstream pull module, by name.
#[test]
fn the_open_rows_forbid_the_public_site_mcp_indexnow_and_the_pull_modules_by_name() {
let set = crates("holger-front v2.0.0\nholger-public-site v2.0.0\nrmcp v0.8.0\ngunnar-public-site v0.1.0\nholger-world-connector v2.0.0\nholger-nexus-connector v2.0.0\nholger-artifactory-connector v2.0.0\nholger-agent-lib v2.0.0\ntokio v1.47.1\n");
for row in [UPSTREAM_ROW, DOWNSTREAM_ROW] {
assert_eq!(
forbidden_in(&set, row).len(),
8,
"an open row lets through {:?}",
set.iter()
.filter(|c| !forbidden_in(&crates(c), row).iter().any(|_| true))
.collect::<Vec<_>>()
);
}
}
/// ★ A32-17: every name these shapes forbid is a crate that exists, or a named crates.io
/// or future crate with its reason (`tests/support/known_crates.rs`).
#[test]
fn every_forbidden_name_is_a_crate_that_exists() {
known_crates::sibling_pins_hold();
let known = known_crates::known_crates();
let mut names: Vec<&str> = Vec::new();
for s in SHAPES {
names.extend_from_slice(s.forbidden);
}
let unknown = known_crates::unknown(&names, &known);
assert!(
unknown.is_empty(),
"a shape forbids crates that exist nowhere: {unknown:?}"
);
}