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

nordisk/Skidbladnir

.nornir/orchestrate-guide.md

orchestrate — guide

Class: guide · audience: a techie bringing up a lake of products. The what is in orchestrate-design.md.

Compute the bring-up order

use skidbladnir::{orchestrate, product::ComponentSpec};

let products: Vec<_> = [ComponentSpec::nornir_server(), ComponentSpec::holger()]
    .iter().map(|c| c.as_product()).collect();

let waves = orchestrate::start_waves(&products)?;   // Vec<Vec<String>>
// e.g. [["nornir-server"], ["holger"]] — holger's `after nornir-server` puts it in a later wave

Or straight from a whole-lake spec:

use skidbladnir::{config::LakeSpec, pki::PkiSource, product::ComponentSpec};

let lake = LakeSpec {
    version: "20260704".into(),
    pki: PkiSource::SelfGenerated,
    products: vec![ComponentSpec::nornir_server(), ComponentSpec::holger()],
};
let waves = lake.start_waves()?;   // lib.rs:137

Start each wave (concurrency within a wave)

for wave in waves {
    std::thread::scope(|s| {
        for product in &wave {
            s.spawn(move || { /* install::apply / start this product */ });
        }
    });
}

Products in the same wave have no ordering constraint between them, so start them concurrently; wave i+1 only begins once wave i is up.

Tear down in reverse

let order = orchestrate::teardown_order(&products)?;   // dependents before their dependencies

Emit telemetry to the warehouse

use skidbladnir::events::{JsonlSink, EventEmitter, op, phase, status};

let sink = JsonlSink::under_warehouse(&warehouse_root)?;   // <root>/airgap_events.jsonl
let em = EventEmitter::new("run-2026-07-04", &sink);
em.emit("holger", op::INSTALL, phase::START, status::RUNNING, "unpacking", &["nornir-server".into()]);
// … later …
em.emit("holger", op::START, phase::END, status::OK, "healthy", &[]);

Read them back (sorted by seq) with skidbladnir::events::read_jsonl(&path)?.

Gotchas