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

nordisk/Skidbladnir

.nornir/ron-orchestration-design.md

Skidbladnir RON orchestration + steering modules (2026-07-26, Rickard)

Make Skidbladnir usable two ways: (1) declarative RON that describes a provisioning flow (an interpreter runs it), and (2) direct/custom library calls (imperative escape hatch). The RON steps dispatch to pluggable steering modules packaged/verified in znippy.

Two layers (the difference)

// A: [ BootOS(backend: Redfish, os: Ubuntu), InstallApp(spec: "korp") ]
// B: [ BootOS(backend: Redfish, os: Talos),  Configure(target: Talos, ...) ]

Data-driven, reusable, versionable, shareable, no-code. A new scenario = a new RON, not new Rust.

Vocabulary + interpreter

Typed Step enum over what Skidbladnir/draupnir already own: BootOS{backend, os} (Redfish/KVM/OCI), Seed{config} (NoCloud), InstallApp{spec}, Configure{target, params}, Federate{registry}, … A RON interpreter (like runa_exec) maps each Step → a steering module call. The library stays the imperative core; the RON layer is a thin declarative interpreter on top.

Steering modules (the execution/plugin model)

Each Step dispatches to a steering module = a pluggable capability. The seam is a SteeringModule trait; modules are registered by name and packaged in znippy (blake3-verified, self-contained); Skidbladnir "plucks" the needed module from the znippy at runtime (reuses the two-znippy root-of-trust → tamper-evident, offline, appliance-friendly).

Module kinds — START NATIVE, add WASM later (behind the same trait)

Terraform = the "support-all-clouds" bridge

Skidbladnir natively drives Redfish/KVM/OCI (draupnir). A TerraformApply steering module bridges cloud providers it doesn't natively speak — pragmatic (Terraform isn't pure-Rust, but it's the meet-every-provider escape, like ContainerBackend supporting non-Rust runtimes). znippy carries the tf binary + configs so the appliance stays self-contained. (Alternative to stay pure-Rust: native cloud SDKs.)

Reuse / relation

Skidbladnir already has the imperative core (ServiceSpec + 11 init renderers + Redfish/KVM/OCI via draupnir + airgap + NoCloud seed + federation). This adds the RON vocabulary + interpreter + the SteeringModule seam + znippy packaging on top. Same RON-as-description language as .runa.ron (jera) and rel2graph's .runa.ron emitter — the constellation converges on RON for declarative flows.

Generalization → mikronova/rover (WASM = Python control, even Mars) 🚀

The engine (declarative flow + SteeringModule seam + znippy-packaged modules) is brandable. Same core, different product:

Why WASM flips from optional→essential for the rover: the very thing that's friction for Skidbladnir — WASM modules can only call host-granted capabilities, not raw I/O — becomes the SAFETY MODEL:

So: same SteeringModule trait, module-kind DEFAULT flips per branding (native ↔ wasm). Build the trait right once (native for Skidbladnir now), and the WASM/Python-control rover is a branding + a wasm module-kind

Companion docs (the as-built reference)

This file is the original design capture. The verified, as-built reference for what actually ships lives in two siblings — read these for ground truth:

Status

Phase 1 SHIPPED (nornir 78deafc): the SteeringModule trait + name→module Registry (native_default) + the run_flow RON interpreter + native modules — KvmBoot and InstallApp REAL, the rest honest mocks. Verified in ron-schema.md / custom-objects.md. NOT YET IMPLEMENTED (still design-only, flagged in the companions): znippy-packaged modules + runtime "pluck from znippy"; the WASM module-kind + capability-import interface; a CLI runner (skidbladnir run flow.ron). The design listed a TalosConfig module; the code ships a generic Configure step/module instead.