OCI network mode — the airgap --network none wire (Skidbladnir's #1 blocker)
Why. Skidbladnir's airgap container route needs a container that reaches no network —
podman run --network none. jera'sContainerSpecalready renders that choice (net: NetMode,network_arg()→--network none), but draupnir owns the ONE OCI engine and its create body carriedport_bindings+bindsand nonetwork_mode— so jera could express the airgap but it never reached the livepodman create. This threads the net mode onto the create body, additively, soNetMode::Noneactually cuts the container off from egress at the real run.
The knob (a net field on BootSpec, NetMode::Default = unchanged)
Every draupnir::BootSpec is built through a constructor + builders — no consumer (nornir/korp/knut/tunnr/jera/Skidbladnir) constructs it by raw struct literal (audited: the only raw BootSpec {…} literals in the constellation are each repo's own local BootSpec type). So a new field is safe as long as the four constructors default it. net defaults to NetMode::Default, which renders no --network flag and leaves the create body byte-for-byte identical to a spec that never named a net mode.
pub enum NetMode { Default, None, Host, Bridge } // Default = no --network flag
impl NetMode {
pub fn oci_value(self) -> Option<&'static str>; // None | "none" | "host" | "bridge"
pub fn is_default(self) -> bool; // parity guard
pub fn from_oci_value(v: Option<&str>) -> Self; // the cross-repo wire (jera passes this)
}
pub struct BootSpec { /* …, */ pub net: NetMode }
impl BootSpec { pub fn with_net(self, net: NetMode) -> Self; } // builder
NetMode mirrors jera's ContainerSpec::NetMode field-for-field. jera passes its rendered value across the repo boundary as the OCI string ("none"/"host"/ "bridge") via NetMode::from_oci_value(jera_spec.net.oci_value()) — no cross-repo variant coupling. net is container-only: validate() rejects a non-default net on KVM/Redfish (parity with the cmd/ports container-only guards).
The wire (rides the existing bind delegate chain)
The create body already had a *_with_binds delegate chain; net rides it with one more *_and_net layer, so the bind-free / net-free entries stay byte-identical.
| net-free (unchanged) | net-aware (new) | note |
Engine::create_body_with_binds |
Engine::create_body_with_binds_and_net |
pure body builder; None net ⇒ identical body (HostConfig.network_mode stays None) |
Engine::create_and_start_with_binds |
Engine::create_and_start_with_binds_and_net |
create+start with HostConfig.network_mode |
| — | Boot::boot / boot_with_binds read spec.net |
both boot paths thread spec.net.oci_value() onto the create body |
Because both boot paths read spec.net, every downstream entry — Boot::boot, boot_with_binds, run_to_completion, run_to_completion_with_binds — honours the net mode automatically. A consumer just sets spec.net.
jera ContainerSpec { net: NetMode::None } draupnir (ONE OCI engine, bollard)
┌──────────────────────────────────────┐ ┌───────────────────────────────────┐
│ to_draupnir(): │ │ boot / boot_with_binds read spec.net│
│ d.net = NetMode::from_oci_value( │ ───────▶ │ → create_and_start_with_binds_and │
│ self.net.oci_value()) // "none" │ │ _net(…, Some("none")) │
│ run_to_completion_with_binds(&d, …) │ │ → HostConfig.network_mode="none" │
└──────────────────────────────────────┘ │ → podman create --network none │
(was: expressed but dropped at the run) └───────────────────────────────────┘
The jera handoff (one line)
jera's ContainerSpec::to_draupnir() builds the draupnir::BootSpec that BOTH its boot leg (DraupnirContainerBootRunner) and its run-to-completion leg (run_to_completion_with_binds) consume. One added line carries the net mode through both:
// in jera ContainerSpec::to_draupnir()
d.net = draupnir::NetMode::from_oci_value(self.net.oci_value());
After that line, NetMode::None reaches the live podman run, and Skidbladnir can un-gate its airgap container route (drop the --network none fallback to a direct podman subprocess).
Status
-
draupnir: landed — builds default (no bollard) +
--features backend-oci; RED-when-broken engine testcreate_body_sets_network_mode_none_and_default_is_byte_identical- testmatrix cell
net_mode_renders_oci_value_and_is_container_onlygreen.
- testmatrix cell
-
jera: one-line
to_draupnir()change pending (above) — then the net flows. -
Skidbladnir: un-gate the airgap route once jera's line lands.