Gunnar

nordisk/Skidbladnir

.nornir/pack-design.md

# pack — design

> **Class:** `design` · **Function axis:** `f1` (AIRGAP1). States only the shape we ship.

## What pack produces

`pack` gathers a product's artifacts (and, optionally, target-identical dependencies) into a
versioned **staging tree** — the input `Archive::pack` (f2) seals. It also records provenance.

## The staging tree

`pub struct Staging { root, version }` (`pack.rs:31`) — the builder.

| method | signature | what it does |
|---|---|---|
| `new` | `Staging::new(root, version) -> Staging` (`pack.rs:38`) | create an empty staging root |
| `add_artifact` | `add_artifact(src, rel) -> String` (`pack.rs:50`) | copy a file in under relative path `rel` |
| `add_component_spec` | `add_component_spec(spec) -> String` (`pack.rs:63`) | write `manifests/<name>.json` |
| `artifacts` | `artifacts() -> Vec<String>` (`pack.rs:73`) | enumerate staged files (sorted) |

## VERSION lives outside the bundle

`pack::write_version_file(beside_bundle, version) -> PathBuf` (`pack.rs:84`) writes a `VERSION`
file **next to** the bundle, not inside it — so bumping the shipped version never requires
repacking the archive.

## Provenance

`pack::pack(staging, product, built_in_container) -> PackManifest` (`pack.rs:95`) records a
`PackManifest { product, version, built_in_container, artifacts }` (`pack.rs:18`) by enumerating
the staging tree — the per-product record of what went in.

## Target-identical dependency capture

`pub trait ContainerBuild { build_into(product, target_triple, into) -> Vec<String> }`
(`pack.rs:107`) is the seam for capturing a component's deps *byte-alike with the destination* by
building it in a container. The real impl is `PodmanBuild` (`pack.rs:124`):

- `new(base_image)` (`pack.rs:139`), `step(run)` (`pack.rs:149`) — builder.
- `containerfile(product, target_triple) -> String` (`pack.rs:157`) — pure Containerfile render.
- `runtime_available() -> bool` — `podman --version` check (the lean/default path only).
- `build_into` — captures the deps into the staging subdir, via one of two engines:
  - **default (lean):** `podman build → create → copy /out → cleanup`; **honest bail** if podman is
    absent, never a faked capture.
  - **`pack-oci` feature:** the same build over draupnir's ONE OCI engine —
    `ContainerBoot::build_image` + `extract_path` (the `create`+`cp` twin), zero-shell, one engine
    constellation-wide. `copy_tree_contents` flattens the extract to the exact `cp /out/.` layout so
    both engines leave `into` byte-identical. See `msi-oci-routing.md`.

This is a **build-host-only** seam — it never runs at airgap-install time.

## Contract (inject-assert tests)

`pack_collects_staging_tree_and_version_file_is_outside` asserts the `VERSION` file lands outside
the bundle tree; `podman_build_renders_containerfile_and_is_honest_when_absent` asserts the exact
Containerfile bytes and the honest failure when podman is absent;
`copy_tree_contents_flattens_extracted_out_dir_into_staging` asserts the `pack-oci` engine's extract
flattens to the same relative paths the `podman cp /out/.` path produced (byte-identical `into`).