holger-recipe/tests/downstream_shape.rs
//! **The downstream is the smallest secure build** (Holger v2.0 H3, A31 §6, owner 2026-10-25).
//!
//! `cargo install holger-downstream` is the on-prem, air-gapped gateway (its own crate since
//! HOL-PRODUCTS, 2026-10-30, over the server's `downstream` feature): it takes packages from
//! ONE upstream Holger (a signed bundle or a sync), verifies them, and serves the build
//! systems. It runs no scanner, fetches no new trust, renders no board and pulls from no
//! internet registry, so none of those crates may be linked, and it is smaller than the
//! server's default build (the airgap rule).
//!
//! The trees are `cargo tree -e normal --prefix none`, distinct lines with ` (*)`
//! trimmed, the same count `cargo_install_holger.rs` uses for the default.
use std::collections::BTreeSet;
use std::path::{Path, PathBuf};
use std::process::Command;
#[path = "support/known_crates.rs"]
mod known_crates;
fn repo_root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.to_path_buf()
}
/// The crates the downstream must never link, by family. A trailing `*` is a prefix.
const FORBIDDEN: &[(&str, &[&str])] = &[
("the wasm host", &["wasmtime*", "cranelift*"]),
(
"a scanner (it trusts the upstream's signed scan evidence)",
// Not `holger-sandbox`: since lane HOL-SANDBOX-TRAIT the downstream verifies every
// bundle that comes in in a job (holger-job-runner over holger-sandbox), and no
// scanner rides with it.
&[
"modgunn",
"holger-inspect",
"holger-scan-worker",
"holger-intel",
"spdx",
],
),
(
"PGP (no new trust is fetched or parsed here)",
&["pgp", "rpgp"],
),
(
"the estate",
&[
"nordisk-pairing",
"boringtun",
"korp-installer",
"nordisk-twin",
"appliance-grow",
"disk-snapshot",
],
),
(
"the board renderer (the server serves board DATA)",
&["nordisk-estate-svg"],
),
("money", &["monetize*", "lettre"]),
(
"an internet pull client",
&[
"reqwest",
"ureq",
"hyper-rustls",
"isahc",
"curl",
"attohttpc",
"surf",
"instant-acme",
"hyper-tls",
"native-tls",
"openssl",
],
),
(
"an upstream pull module (Nexus, Artifactory, the public registries)",
&[
"holger-world-connector",
"holger-nexus-connector",
"holger-artifactory-connector",
"holger-agent-lib",
],
),
// IndexNow has no crate of its own: it is gunnar-public-site's `indexnow` module (A32-17).
(
"the public site / SEO / IndexNow",
&["holger-front", "holger-public-site", "gunnar-public-site"],
),
("MCP", &["rmcp", "rmcp-macros", "holger-mcp", "gunnar-mcp"]),
("the APK re-signer", &["rsa"]),
];
/// The estate crates (H2's estate cut, HCUT 2026-10-26): the twin over mutual TLS
/// (Skidbladnir `nordisk-twin`), the grow plane (edda `appliance-grow`) and the admin
/// view's disks (edda `disk-snapshot`). server-lib links them only with its default-off
/// `estate` feature, which the estate rows (`paired`, `appliance`, `grow-runtime`,
/// `metered`) turn on; neither the default (upstream) build nor the downstream has them.
const ESTATE: &[&str] = &["nordisk-twin", "appliance-grow", "disk-snapshot"];
/// The crates no build with `downstream` may link, whatever else is on: the airgap rule
/// (an internet pull client), money, the wasm host and PGP.
const NEVER_WITH_DOWNSTREAM: &[(&str, &[&str])] = &[
(
"an internet pull client",
&[
"reqwest",
"ureq",
"hyper-rustls",
"isahc",
"curl",
"attohttpc",
"surf",
"instant-acme",
"hyper-tls",
"native-tls",
"openssl",
],
),
("money", &["monetize*", "lettre"]),
("the wasm host", &["wasmtime*", "cranelift*"]),
("PGP", &["pgp", "rpgp"]),
];
fn tree(args: &[&str]) -> BTreeSet<String> {
tree_of("holger", args)
}
/// `--manifest-path` of a package that is a workspace of its own (`holger-downstream`).
fn manifest_of(package: &str) -> Vec<&'static str> {
if package == "holger-downstream" {
vec!["--manifest-path", "holger-downstream/Cargo.toml"]
} else {
Vec::new()
}
}
fn tree_of(package: &str, args: &[&str]) -> BTreeSet<String> {
let mut a = vec!["tree"];
a.extend(manifest_of(package));
a.extend(["-p", package, "-e", "normal", "--prefix", "none"]);
a.extend_from_slice(args);
let out = Command::new(env!("CARGO"))
.current_dir(repo_root())
.args(&a)
.output()
.expect("run cargo tree");
assert!(
out.status.success(),
"cargo {} failed:\n{}",
a.join(" "),
String::from_utf8_lossy(&out.stderr)
);
let set: BTreeSet<String> = String::from_utf8_lossy(&out.stdout)
.lines()
.map(|l| l.trim_end_matches(" (*)").trim())
.filter(|l| !l.is_empty())
.map(String::from)
.collect();
assert!(
set.len() > 50,
"an empty tree proves nothing: cargo {}",
a.join(" ")
);
set
}
/// Pure: the forbidden crates a listing holds, each with the family it belongs to.
fn forbidden_in(set: &BTreeSet<String>) -> Vec<String> {
forbidden_by(set, FORBIDDEN)
}
fn forbidden_by(set: &BTreeSet<String>, forbidden: &[(&str, &[&str])]) -> Vec<String> {
let names: BTreeSet<&str> = set
.iter()
.filter_map(|l| l.split_whitespace().next())
.collect();
let mut hits = Vec::new();
for (family, crates) in forbidden {
for f in *crates {
let found: Vec<&str> = match f.strip_suffix('*') {
Some(prefix) => names
.iter()
.copied()
.filter(|n| n.starts_with(prefix))
.collect(),
None => names.iter().copied().filter(|n| n == f).collect(),
};
hits.extend(found.into_iter().map(|n| format!("{n} ({family})")));
}
}
hits
}
/// `cargo install holger-downstream`.
fn downstream() -> BTreeSet<String> {
tree_of("holger-downstream", &[])
}
#[test]
fn the_downstream_links_no_scanner_no_wasm_no_estate_no_renderer_and_no_internet_client() {
let hits = forbidden_in(&downstream());
assert!(
hits.is_empty(),
"the downstream build links crates it must never link:\n {}",
hits.join("\n ")
);
}
/// Smaller than the server's default tree, and bounded (`cargo tree -e normal`, distinct
/// lines; 339 and 356 on main after HOL-MVP-FORMATS and WG-PSK, 2026-10-30). It drops the
/// scanners (modgunn, sandbox, inspect, intel, spdx, zip) and adds sync and its rules.
const DOWNSTREAM_TREE_BOUND: usize = 342;
#[test]
fn the_downstream_is_smaller_than_the_default_and_bounded() {
let (down, default) = (downstream().len(), tree(&[]).len());
println!("downstream: {down} crates; default: {default} crates");
assert!(down < default, "the downstream links {down} crates, the default {default}: the downstream must stay smaller");
assert!(down <= DOWNSTREAM_TREE_BOUND, "the downstream links {down} crates, above its bound {DOWNSTREAM_TREE_BOUND}: what rode in?");
}
fn estate_in(set: &BTreeSet<String>) -> Vec<String> {
forbidden_by(set, &[("the estate", ESTATE)])
}
/// The default (upstream) `cargo install holger` links none of the estate crates (H2's
/// estate cut): the twin, the grow plane and the disk snapshot are the estate rows' only.
#[test]
fn the_default_links_no_estate_crate() {
let hits = estate_in(&tree(&[]));
assert!(
hits.is_empty(),
"the default `cargo install holger` links estate crates:\n {}",
hits.join("\n ")
);
let hits = estate_in(&downstream());
assert!(
hits.is_empty(),
"`cargo install holger-downstream` links estate crates:\n {}",
hits.join("\n ")
);
}
/// The estate rows still link every estate crate: the cut moved them behind a feature,
/// it did not drop them from the estate (the main, the twin, the sold box).
#[test]
fn the_estate_rows_still_link_the_estate() {
let rows: Vec<_> = holger_recipe::variants::variants()
.into_iter()
.filter(|v| {
v.package.as_deref() == Some("holger")
&& v.features.iter().any(|f| f == "paired" || f == "appliance")
})
.collect();
assert!(
rows.len() >= 4,
"the estate rows (paired or appliance) are declared: {:?}",
rows.iter().map(|v| &v.name).collect::<Vec<_>>()
);
for v in rows {
let mut a = Vec::new();
if v.no_default_features {
a.push("--no-default-features".to_string());
}
a.extend(["--features".to_string(), v.features.join(",")]);
let set = tree(&a.iter().map(String::as_str).collect::<Vec<_>>());
let names: BTreeSet<&str> = set
.iter()
.filter_map(|l| l.split_whitespace().next())
.collect();
for c in ESTATE {
assert!(
names.contains(c),
"the estate row `{}` no longer links `{c}`",
v.name
);
}
}
}
/// **The H4 sync link pulls no HTTP client** (B1): server-lib's `sync` alone, and the
/// downstream with it, reach the upstream over the tonic/hyper the server already has,
/// never ureq or reqwest.
#[test]
fn the_sync_link_links_no_http_client() {
let out = Command::new(env!("CARGO"))
.current_dir(repo_root())
.args([
"tree",
"-p",
"holger-server-lib",
"-e",
"normal",
"--prefix",
"none",
"--no-default-features",
"--features",
"sync",
])
.output()
.expect("run cargo tree");
assert!(
out.status.success(),
"cargo tree -p holger-server-lib --features sync: {}",
String::from_utf8_lossy(&out.stderr)
);
let set: BTreeSet<String> = String::from_utf8_lossy(&out.stdout)
.lines()
.map(|l| l.trim_end_matches(" (*)").trim().to_string())
.filter(|l| !l.is_empty())
.collect();
assert!(
set.iter().any(|l| l.starts_with("holger-sync ")),
"the tree carries the sync protocol"
);
assert!(
set.iter().any(|l| l.starts_with("tonic ")),
"the sync link's transport is tonic"
);
let hits = forbidden_by(
&set,
&[("an HTTP client", &["ureq", "reqwest", "hyper-rustls"])],
);
assert!(
hits.is_empty(),
"server-lib `sync` links:\n {}",
hits.join("\n ")
);
let hits = forbidden_by(&downstream(), &[("an HTTP client", &["ureq", "reqwest"])]);
assert!(
hits.is_empty(),
"the downstream (with sync) links:\n {}",
hits.join("\n ")
);
}
/// The verbs the on-prem gateway needs are in it: the bundle import, the intake chain's
/// wire verb (quarantine, release, approvals) and the audit log; the export is not.
#[test]
fn the_downstream_carries_import_intake_and_audit_and_not_export() {
let m: toml::Value = toml::from_str(
&std::fs::read_to_string(repo_root().join("server/cli/Cargo.toml")).unwrap(),
)
.unwrap();
let f = |name: &str| -> Vec<String> {
m["features"][name]
.as_array()
.unwrap_or_else(|| panic!("no feature `{name}`"))
.iter()
.map(|v| v.as_str().unwrap().to_string())
.collect()
};
let down = f("downstream");
// The small core (H2's server-lib `downstream`, through `downstream-core`), the
// gateway's rules, intake, the bundle verbs, sync (the quarantine of an invalidated
// package) and the build systems' formats: the twelve MVP formats, server/cli's own
// passthroughs (HOL-MVP-FORMATS 2026-10-30; `tests/mvp_formats.rs` pins the set).
for want in [
"downstream-core",
"server-lib/gateway",
"intake",
"bundle-verbs",
"sync",
"sign",
"fmt-rust",
"fmt-maven",
"fmt-python",
"fmt-docker",
"fmt-go",
] {
assert!(
down.iter().any(|d| d == want),
"`downstream` must name `{want}`: {down:?}"
);
}
assert_eq!(
f("downstream-core"),
["server-lib/downstream"],
"the small core is server-lib's `downstream` (H2)"
);
for never in [
"supply-chain",
"secure_update",
"online",
"mirror",
"maven-pgp",
"wasm-plugins",
"paired",
"metered",
] {
assert!(
!down.iter().any(|d| d == never),
"`downstream` must not name `{never}`: {down:?}"
);
}
}
#[test]
fn a_forbidden_prefix_and_name_are_found_with_their_family() {
let set: BTreeSet<String> = [
"holger v2.0.0",
"wasmtime-environ v1.0.0",
"modgunn v0.0.8",
"tokio v1.47.1",
]
.iter()
.map(|s| s.to_string())
.collect();
assert_eq!(
forbidden_in(&set),
vec![
"wasmtime-environ (the wasm host)".to_string(),
"modgunn (a scanner (it trusts the upstream's signed scan evidence))".to_string()
]
);
}
// ══ OWNER RULE 7 (2026-10-26, owner-decisions-1026 item 7): "airgap = the DOWNSTREAM builds" ══
//
// The downstream may never contain the public site / SEO, MCP, IndexNow or any internet pull
// client, by crate AND by feature. Its ONE allowed outbound is its configured upstream Holger.
/// The holger and server-lib features no downstream build may turn on: the internet pull
/// modules (`online`: the crates.io/PyPI/npm/Maven Central/OCI/Go pull-through clients;
/// `migration`: the Nexus/Artifactory pull; `intel-fetch`/`intel-refresh`: the advisory
/// fetch; `pull-apk`: the APK pull-through; `maven-pgp`: Central's `.asc`) and the public
/// site (`front`, `public-site`).
const FORBIDDEN_FEATURES: &[(&str, &[&str])] = &[
(
"holger",
&[
"online",
"migration",
"intel-fetch",
"maven-pgp",
"front",
"full",
],
),
(
"holger-server-lib",
&[
"online",
"migration",
"intel-refresh",
"pull-apk",
"maven-pgp",
"front",
],
),
];
/// Pure: the forbidden features a `cargo tree -e features` listing turns on.
fn forbidden_features_in(listing: &str) -> Vec<String> {
let mut hits = BTreeSet::new();
for line in listing.lines() {
let line = line.trim_end_matches(" (*)").trim();
let Some((pkg, rest)) = line.split_once(" feature \"") else {
continue;
};
// `online" (command-line)`: the name ends at its closing quote.
let feat = rest.split('"').next().unwrap_or(rest);
for (p, feats) in FORBIDDEN_FEATURES {
if pkg == *p && feats.contains(&feat) {
hits.insert(format!("{pkg}/{feat}"));
}
}
}
hits.into_iter().collect()
}
fn features_of(args: &[&str]) -> String {
features_of_package("holger", args)
}
fn features_of_package(package: &str, args: &[&str]) -> String {
// `-e features,no-dev -i holger-server-lib`: the features holger and server-lib turn on
// in the build (dev-dependencies left out), one `<pkg> feature "<f>"` line each.
let mut a = vec!["tree"];
a.extend(manifest_of(package));
a.extend([
"-p",
package,
"-e",
"features,no-dev",
"-i",
"holger-server-lib",
"--prefix",
"none",
]);
a.extend_from_slice(args);
let out = Command::new(env!("CARGO"))
.current_dir(repo_root())
.args(&a)
.output()
.expect("run cargo tree");
assert!(
out.status.success(),
"cargo {} failed:\n{}",
a.join(" "),
String::from_utf8_lossy(&out.stderr)
);
String::from_utf8_lossy(&out.stdout).into_owned()
}
#[test]
fn the_guard_names_the_public_site_mcp_indexnow_and_the_pull_modules() {
let set: BTreeSet<String> = [
"holger-front v2.0.0",
"holger-public-site v2.0.0",
"rmcp v0.8.0",
"gunnar-public-site v0.1.0",
"holger-world-connector v2.0.0",
"holger-agent-lib v2.0.0",
"reqwest v0.12.28",
"tokio v1.47.1",
]
.iter()
.map(|s| s.to_string())
.collect();
let hits = forbidden_in(&set);
for name in [
"holger-front",
"holger-public-site",
"rmcp",
"gunnar-public-site",
"holger-world-connector",
"holger-agent-lib",
"reqwest",
] {
assert!(
hits.iter().any(|h| h.starts_with(&format!("{name} ("))),
"the downstream guard does not name `{name}`: {hits:?}"
);
}
let listing = "holger feature \"online\" (command-line)\nholger-server-lib feature \"front\" (*)\nholger-server-lib feature \"intake\"\n";
assert_eq!(
forbidden_features_in(listing),
["holger-server-lib/front", "holger/online"]
);
}
#[test]
fn a_downstream_with_the_public_site_turned_on_is_refused_by_name() {
let hits = forbidden_in(&tree(&[
"--no-default-features",
"--features",
"downstream,front",
]));
// (the server crate's `downstream` feature is what `holger-downstream` turns on)
assert!(
hits.iter().any(|h| h.starts_with("holger-front (")),
"downstream + front links the public page and the guard passes: {hits:?}"
);
}
#[test]
fn a_downstream_with_the_internet_pull_turned_on_is_refused_by_feature() {
let hits = forbidden_features_in(&features_of_package(
"holger-downstream",
&["--features", "online"],
));
assert!(hits.iter().any(|h| h.ends_with("/online")), "`holger-downstream --features online` turns the pull-through on and the guard passes: {hits:?}");
}
#[test]
fn the_downstream_turns_on_no_forbidden_feature() {
let hits = forbidden_features_in(&features_of_package("holger-downstream", &[]));
assert!(hits.is_empty(), "the downstream turns on {hits:?}");
}
/// The downstream's ONE outbound: the gRPC client of its configured upstream Holger (the
/// sync pull over tonic, on hyper). Allowed by name; nothing else that dials out is linked.
const ALLOWED_OUTBOUND: &[(&str, &str)] = &[
(
"tonic",
"the gRPC client of the one configured upstream Holger (the sync pull)",
),
("hyper", "tonic's HTTP/2 transport to that upstream"),
("hyper-util", "tonic's connector to that upstream"),
];
#[test]
fn the_only_outbound_the_downstream_links_is_its_upstream_holger_line() {
let set = downstream();
let names: BTreeSet<&str> = set
.iter()
.filter_map(|l| l.split_whitespace().next())
.collect();
for (c, why) in ALLOWED_OUTBOUND {
assert!(
names.contains(c),
"`{c}` is allowed ({why}) but no longer linked: drop the allowance"
);
assert!(
!FORBIDDEN.iter().any(|(_, cs)| cs.contains(c)),
"`{c}` is both allowed and forbidden"
);
}
}
/// The online UPSTREAM carries the pull clients and the public site only behind their
/// default-off features: its DEFAULT build (`cargo install holger`) carries none of them.
#[test]
fn the_upstreams_default_build_carries_none_of_them_either() {
let mut hits = forbidden_by(&tree(&[]), FORBIDDEN);
// The default is the upstream: it scans (modgunn, the sandbox) and keeps the estate
// crates the downstream also still links; those families are the downstream's own.
hits.retain(|h| !h.contains("a scanner") && !h.contains("the estate"));
hits.extend(forbidden_features_in(&features_of(&[])));
assert!(
hits.is_empty(),
"`cargo install holger` (the default) carries:\n {}",
hits.join("\n ")
);
}
/// ★ A32-17: every name the guards forbid is a crate that exists (holger or a sibling's
/// package, a crate in one of their locks) or a named crates.io or future crate with its
/// reason (`tests/support/known_crates.rs`). A guard naming a crate that does not exist
/// proves nothing.
#[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 (_, crates) in FORBIDDEN.iter().chain(NEVER_WITH_DOWNSTREAM) {
names.extend_from_slice(crates);
}
names.extend_from_slice(ESTATE);
let unknown = known_crates::unknown(&names, &known);
assert!(
unknown.is_empty(),
"the downstream guard names crates that exist nowhere: {unknown:?}"
);
}
/// ★ OX-HOLF: the guard holds in a checkout of holger with NO sibling beside it (a lone
/// clone, a CI tree without gunnar-ui): every forbidden name is known from holger's own
/// packages and locks, or is a named crates.io, sibling or future crate with its reason.
#[test]
fn every_forbidden_name_is_known_without_any_sibling_checked_out() {
let holger_only = known_crates::known_under(&known_crates::holger_root(), 5);
assert!(
holger_only.len() > 300,
"holger alone names {} crates",
holger_only.len()
);
let mut names: Vec<&str> = Vec::new();
for (_, crates) in FORBIDDEN.iter().chain(NEVER_WITH_DOWNSTREAM) {
names.extend_from_slice(crates);
}
names.extend_from_slice(ESTATE);
let unknown = known_crates::unknown(&names, &holger_only);
assert!(
unknown.is_empty(),
"without the siblings the downstream guard cannot tell these names exist: {unknown:?}"
);
}
/// ★ OX-HOLF: a pinned sibling crate is checked whenever its sibling is checked out: a
/// checkout without the crate fails the guard, and an absent sibling is reported, not silent.
#[test]
fn a_pinned_sibling_crate_is_checked_when_its_sibling_is_there() {
use std::collections::BTreeSet;
let pins = [("gunnar-public-site", "gunnar-ui", "the public site")];
let renamed = |_: &str| Some(BTreeSet::from(["gunnar-site".to_string()]));
let (wrong, trusted) = known_crates::pin_problems(&pins, renamed);
assert_eq!(
(wrong.len(), trusted.len()),
(1, 0),
"a sibling that renamed the crate passes: {wrong:?}"
);
let present = |_: &str| Some(BTreeSet::from(["gunnar-public-site".to_string()]));
assert_eq!(known_crates::pin_problems(&pins, present), (vec![], vec![]));
let (wrong, trusted) = known_crates::pin_problems(&pins, |_| None);
assert!(
wrong.is_empty() && trusted == ["gunnar-public-site (../gunnar-ui is not checked out)"],
"{trusted:?}"
);
}
/// ★ HCUT: every estate component that takes holger-server-lib names `estate` (or a feature
/// that implies it: `metered`, `grow-runtime`): the front's twin test booted a server
/// without the twin when a merge dropped it (front:default and front:metered went red).
#[test]
fn estate_edges_name_the_estate() {
for manifest in ["holger-estate-front/Cargo.toml", "estate-tests/Cargo.toml"] {
let m: toml::Value =
toml::from_str(&std::fs::read_to_string(repo_root().join(manifest)).unwrap()).unwrap();
let mut edges = Vec::new();
for table in ["dependencies", "dev-dependencies"] {
if let Some(d) = m.get(table).and_then(|t| t.get("holger-server-lib")) {
edges.push((table, d.clone()));
}
}
assert!(!edges.is_empty(), "{manifest} takes holger-server-lib");
for (table, d) in edges {
let f: Vec<&str> = d
.get("features")
.and_then(|f| f.as_array())
.map(|a| a.iter().filter_map(|v| v.as_str()).collect())
.unwrap_or_default();
assert!(
f.iter()
.any(|x| ["estate", "metered", "grow-runtime"].contains(x)),
"{manifest} [{table}] holger-server-lib must name `estate`: {f:?}"
);
}
}
}
/// ★ HCON-7 (owner rule 7: no MCP in the default and the downstream builds, by name): the MCP
/// family names the REAL MCP crates, and each is known whatever siblings are checked out:
/// `rmcp` (+ `rmcp-macros`), the Rust MCP SDK on crates.io that nornir's `mcp` feature links
/// and an online upstream's MCP door would use; `gunnar-mcp`, gunnar-ui's MCP connector; and
/// `holger-mcp`, Holger's MCP door when it is written. `rust-mcp-schema` was no MCP door: it
/// reached nornir's lock only through the mistralrs LLM runtime, so the guard was red on any
/// checkout without nornir beside it, and it named nothing an MCP door would link.
#[test]
fn the_mcp_family_names_the_real_mcp_crates() {
let mcp = FORBIDDEN
.iter()
.find(|(why, _)| *why == "MCP")
.map(|(_, c)| *c)
.expect("the MCP family");
assert_eq!(
mcp,
known_crates::MCP,
"the guard's MCP names are the real ones"
);
let nothing_checked_out = std::collections::BTreeSet::new();
assert!(
known_crates::unknown(mcp, ¬hing_checked_out).is_empty(),
"each MCP name is known with its reason, sibling or none: {:?}",
known_crates::unknown(mcp, ¬hing_checked_out)
);
}