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

nordisk/holger

docs/v2/07-build-thing.md

7. Build Thing

cargo install builds one product. The Build Thing builds everything Holger ships, including what needs signing, media or the ONE box: the estate's boxes, the front, the signed wasm plugins and the installer media. It runs as cargo xtask build in a clone, and as dwarves buildthing on the dwarves forge, which also signs.

Where the rows are declared

The MVP suite

The MVP suite is Holger's one product list: the four products and what only an estate needs (.nornir/SUITES.md).

part rows on main
the four products holger (the server's default), holger-downstream, holger-cli (the client's default), holger-ui (built from the holger-ui checkout beside)
the server, as the estate runs it mvp-server-musl (bin holger-mvp, the static appliance payload)
metered (sold through the ONE box) mvp-server-metered, mvp-server-musl-metered, mvp-server-musl-metered-narrate (a DEV boot log)
the estate front estate-front-musl-metered (crate holger-estate-front) and its -narrate row
the three signed wasm plugins plugin-allocation (holger-plugin-allocation-wasm), plugin-skidbladnir (holger-handler-skidbladnir-wasm), plugin-rust-toolchain (holger-handler-rust-toolchain-wasm)
installer media holger-installer, holger-installer-metered, holger-front-installer-metered, holger-appliance-iso, holger-mvp-appliance (container), holger-keep, holger-front-keep
the downstream's media downstream-musl (one-way, on holger-downstream-oneway-installer), downstream-fetch-musl (bin holger-downstream-fetch, on holger-downstream-installer)

A medium never carries a scan worker staged from another tree: the forge door builds this tree's worker and stages it again ([sandbox-stage-stale]).

There are no other suites: ORIGINAL and OPEN were folded into this list on 2026-10-30 (.nornir/SUITES.md "History").

The one-way downstream appliance

The one-way downstream boots as an OS-less appliance without pairing (server/cli/src/pid1.rs, holger-downstream --features pid1). Its PID 1:

Keys and trust come only from the medium, never from the stick.

The command

cargo xtask build --suite MVP --list                       # every row and its exact build line
cargo xtask build --suite MVP --variant mvp-server-musl    # build one row

--list prints delivery 'MVP' — N variant(s):, then per row its name, role, kind and bin, and under it the exact cargo command, or (not a cargo build — built by the <kind> builder). A row that is not a cargo build is skipped by build, with the verb to use instead (for example cargo xtask installer-iso --variant <name> --kernel <bzImage>).

The full usage (the shared parser, nornir-build-thing suite_cli):

cargo xtask build [--suite <name>] [--variant <name>] [--list] [--attest] [--param k=v]...
                  [--opt name=on|off]... [--build <path|repo/variant>]...
                  [--features a,b] [--no-default-features] [--all-features]

--features with no --variant (for example cargo xtask build --suite MVP --features full) lists and builds the four products with those features. With a --variant, --attest and --opt, a row is built by its name with its own features.

A row is its cargo install line

A row adds the pinned container, remapped paths and a signature, never a feature (holger-recipe/src/twins.rs). On main the rule holds for the four products (twins::PRODUCTS): holger, holger-downstream, holger-ui and holger-cli, each held to one cargo install --locked --path … line.

The signed wasm plugins

How a release is built

Holger's crates.io releases are built by dwarves, the build server, and carry SLSA v1.0 provenance at Build L2: the isolation of L3 is built, its unforgeability is not (below). Each release has one signed statement that names every artifact by its sha256.

pipeline the Runa pipeline release-build on the dwarves forge's own step runner, run as the account relbuild
builder id https://git.gunnar.rs/nordisk/dwarves/slsa/builders/dwarves-server/v1
build type https://git.gunnar.rs/nordisk/dwarves/slsa/buildtypes/cargo-release/v1
format in-toto Statement v1, predicate SLSA Provenance v1, in a DSSE envelope (.intoto.jsonl, one line)
signature ECDSA P-256 over the DSSE PAE, on the owner's YubiKey (PIV slot 9c, PIN once, touch always); the key was made on the device and never leaves it
release key slsa/release-signing-key.pub, key id d24d3785e8281bcd37e8f2c40a3a85e8152d3ce2200a5c929a1a5ca636e8249e (the sha256 of its SubjectPublicKeyInfo DER); the YubiKey's attestation of the key is beside it
attestations attestations/<release>.intoto.jsonl

The steps:

  1. Sources. Every repository the release reaches is checked out by its full commit id into a fresh run directory and made read-only. The commits are the release spec (the provenance's externalParameters).
  2. Fetch. In a sealed container with the network on and the sources read-only: cargo vendor --locked of every Cargo.lock. The forge checks every vendored crate against the lock's checksum.
  3. Build. One fresh sealed ci4 container per step, by digest, never pulled: --network=none, a read-only root, all capabilities dropped, no-new-privileges, no $HOME, no key, no shared cache, no shell; one cargo command each. cargo package --locked --offline --no-verify per crate in dependency order; binaries with cargo build --release --locked --offline. Outputs go to out/.
  4. Provenance. dwarves writes the statement outside every container, from the run's record: the sha256 of every file in out/, every repository at its commit, the image digest, every lock and crate checksum, the pipeline and its steps, and in runDetails.builder.version the runner's account (relbuild), uid, binary and sha256.
  5. Signature. The owner signs the statement on the YubiKey on his own machine, after reading what it names, and sends the bundle back with dwarves release-build attach <run-id> <bundle>. The server verifies it against the release key and the run's statement before it stores it.
  6. Publish. Each crate is packaged again from the same sources in the same image and uploaded only if its sha256 is the attested subject's; the crates.io index's checksum must then match too.
dwarves release-build request --name <release> --steps-from publish-<release>.sh --pins <pins> --lock <repo>/Cargo.toml
dwarves history                               # queued, building, waiting for signature, signed, failed
dwarves release-build show <run-id>           # the statement, every artifact and its sha256
dwarves release-build attach <run-id> release.intoto.jsonl

The media, the app archives and the binaries

dwarves' run attests the crates it packs. A release ships more, and one more statement names all of it by sha256 (lane HOL-NCSC): every medium the forge composes (.iso-out/<variant>.iso: the four estate media, the installers, the appliance ISO), each app medium's app archive (korp's live app <variant>.live-app.znippy and its record <variant>.live.toml, what a box takes into its live slot) and every product binary (<out>/bin/<row>/<bin>):

cargo xtask release-attest --release holger-<version> --out <dwarves out/>   # UNSIGNED, into target/release-attest/holger-<version>/
cargo xtask release-attest --check <statement.json>                            # before signing any statement: every shipped name is a subject

An artifact the release ships that is not there is refused by name ([release-attest-missing]); a statement that leaves one out is refused at the check ([release-attest-incomplete]). The rows not in the statement (the appliance container image, the wasm guests inside the app archives) are named with why in xtask/src/release_attest.rs (NOT_IN_STATEMENT); a row the declaration gains is attested or named, or cargo xtask test is red. The release manager signs this statement with the release key like dwarves' one, and it lands beside it in attestations/<release>-media.intoto.jsonl.

How to verify a release

You need the release key, the bundle and the artifact.

curl -fsSLO https://static.crates.io/crates/holger/holger-2.0.1.crate

# dwarves (checks the builder and the build type too)
dwarves release-build verify --key slsa/release-signing-key.pub \
  --bundle attestations/holger-2.0.1.intoto.jsonl --artifact holger-2.0.1.crate

# or cosign 2.x / 3.x (the releases are key-signed and not in Rekor)
cosign verify-blob-attestation --key slsa/release-signing-key.pub \
  --signature attestations/holger-2.0.1.intoto.jsonl \
  --type https://slsa.dev/provenance/v1 --insecure-ignore-tlog=true holger-2.0.1.crate

# the key id
openssl pkey -pubin -in slsa/release-signing-key.pub -outform DER | sha256sum

slsa/README.md has the same check with plain openssl. Holger can require these releases on the way in: see chapter 8.

Why it is Build L2, not L3

Measured against SLSA v1.0's Build track (lane HOL-NCSC, 2026-10-31):

A verifier's policy should therefore ask for level 2 of this builder (builders: [(id: "https://git.gunnar.rs/nordisk/dwarves/slsa/builders/dwarves-server/v1", level: 2)]) until the gaps above close.

Moved from v1

Still in v1