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

nordisk/Skidbladnir

.nornir/guide.md

Skíðblaðnir — guide (the whole workflow)

Class: guide · audience: an operator or product author driving Skidbladnir end to end. The what is in design.md; the why in reasoning.md. Per-function how-tos are the *-guide.md chapters.

f0 — one deployment across the gap (the whole session)

The end-to-end flow spans several functions. It splits at the gap: everything up to and including archive runs on the internet side; everything from fetch runs on the secure side with zero network.

# ── internet side (build host) ──────────────────────────────────────────
# 1. pack a staging tree into a signed tar.zst bundle + per-entry SHA-256 manifest
nornir airgap pack ./staging --out lake.tar.zst

# …carry lake.tar.zst across the gap — a disk, a one-way diode, a courier…

# ── secure side (target): ZERO network ──────────────────────────────────
# 2. verify-on-receipt against the manifest (nothing changed in transit)
nornir airgap fetch  lake.tar.zst
# 3. provision the machine (kvm | terraform | redfish); --run to actually drive it
nornir airgap unfold kvm my-vm
# 4. dependency-ordered start → systemd over D-Bus, no shell
nornir airgap start  lake.spec.toml

The declarative spine is a LakeSpec (lib.rs:129): a version, the ComponentSpecs, and one PkiSource. From it, LakeSpec::start_waves() (lib.rs:137) computes the bring-up order — you declare the products and their after edges once; ordering is derived.

As a library, the same session in Rust:

use skidbladnir::{archive::TarZstd, pack, fetch, layout::Layout, install, service::Backend, product};

let eng = TarZstd::new();
// internet side
let staging = pack::Staging::new(&staging_root, "20260704")?;
let manifest = eng.pack(staging.root(), "20260704", &bundle_path)?;   // Archive::pack

// secure side — verify BEFORE touching anything
let bundle = fetch::fetch_and_verify(&bundle_path, &eng)?;            // fetch.rs:74
let layout = Layout::dated(data_dir, log_dir, software_dir);
let spec   = product::ComponentSpec::holger();
let outcome = install::install_product(&spec, &layout, &bundle_path, &eng,
                                       Backend::SystemdSystem, "/")?;  // install.rs:88
install::apply(&outcome)?;                                            // runs [enable, start]

f1…f10 — per-function how-tos

Each function has its own guide chapter with its CLI/API and worked examples:

Feature flags you choose at build time

The lean core is always available. Turn capabilities on per build (Cargo.toml:91):

feature adds default
pki real ed25519 SelfGenerated certs on
znippy REAL znippy-core archive engine (seal/read/verify/delta via ../znippy) off
vault Vault PKI HTTP client off
redfish Redfish BMC provisioning off
terraform drive the terraform binary off
machine / kvm / container VM/container lifecycle control off
matrix the live multi-OS install matrix (implies kvm) off

A build with none of these still packs, archives (tar+zstd), verifies, lays out, installs over systemd/OpenRC, orchestrates, and emits events — fully offline.