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

nordisk/heimdall

docs/v2/kernel-fit.md

Fitting the node kernel: the process

This is the step-by-step way to fit a tunnr node's kernel to the programs it runs, using Heimdall's evidence and Build Thing. The kernel keeps every option the evidence needs, and it drops an option only when coverage shows the code never ran and a full rerun stays green. Each drop has a receipt. Nothing is typed by hand.

The reference for every file, schema and verb is kernel-tooling.md. The seccomp side (how a program's own filter and the node's floor are made) is in tunnr's idiot guide and node-floors.md. State of the code: heimdall main d568179, tunnr main 2026-11-08.

The rule

Let R be the calls the row's programs made (Heimdall's probe), P the calls their code can reach (Mímir's possible sets), and opts(c) the kernel options that gate call c in the row's kernel tree (Build Thing's syscall map). Let N_rt be the options Heimdall's runner needs (heimdall kernel needs --json).

Need = opts(R) ∪ N_rt                         generated as CONFIG_X=y rows
Keep = opts(R ∪ P) ∪ N_rt                      never offered as a veto

veto(X) ⇔ X ∉ Keep
        ∧ X ∉ protect.list
        ∧ cov(X) = 0          no code of X ran during the row's full suite
        ∧ suite(kernel − X) = green

The possible set protects but never adds: a call no run made does not become a =y row, but its options can never be dropped. Coverage is per fragment: a coverage report counts only for the merged fragment whose digest it names.

Step by step

All commands run in the toolchain container. <row> is a node row, for example rusternetes-node.

  1. Measure the programs. Record each program's calls on the probe medium: scripts/kernel-suite.sh <row> <suite> heimdall <out>, and approve the profiles (scripts/heimdall-approve.py). Pods: heimdall profile probe-oci. The PID 1 tree: scripts/kernel-suite.sh <row> bridge log (heimdall profile probe-log). Details: SECCOMP.md.
  2. Trace the code. heimdall graph src <workspace> --bin <program> --out <dir> writes <program>.possible.json (P). Commit it with its source.json.
  3. Save the join. tunnr-build-thing kernel needs <row> --save writes .nornir/profiles/<row>/kernel-needs.json: every measured call joined to the options that gate it. tunnr compiles the file in (crates/tunnr-build-thing/src/joined.rs), so the row's CONFIG_X=y rows are generated from it.
  4. Add the runner's needs. For a row that runs Heimdall jobs: heimdall kernel needs --json > .nornir/kernel-runtime-needs/<name>/heimdall.json, then tunnr-build-thing kernel requires-from-needs <row> --needs <file>.
  5. Check. cargo xtask evidence <row> --check refuses [stale-needs], [kernel-unexplained] and [hand-typed-kernel-row]. tunnr-build-thing preflight --strict and measure gate refuse [stale-join], [kernel-lacks-generated] and any veto line without a matching receipt.
  6. Measure the kernel. tunnr-build-thing kernel coverage <row> --run <suite> composes the row's media with the gcov probe (kernel-probe <row> --probe gcov composes the same medium on its own), runs the row's full suite on KVM and writes .nornir/kernel-coverage/<row>/<sha12>.json. Run the failure paths too (the protect list covers what a green suite cannot reach: block errors, watchdog, NVMe reset).
  7. Pick and drop. tunnr-build-thing kernel candidates <row> lists veto-ready options; kernel what-if <row> --drop CONFIG_X shows what a drop takes away. Then tunnr-build-thing kernel veto <row> CONFIG_X --run <suite>: the full suite runs again on the vetoed kernel. Green writes an applied receipt (.nornir/kernel-vetoes/<row>/receipts/<date>-CONFIG_X.json) and the list line; red writes a refused receipt and changes nothing. One option per run.
  8. Build. dwarves buildthing <row>. The vetoes land as CONFIG_X=n rows in the next merged fragment; tunnr-build-thing kernel vetoes <row> shows each line BACKED or UNBACKED.

Any change to the fragment (a new generated row, a new preset line) makes the coverage stale: run step 6 again before the next veto.

Where tunnr stands (2026-11-08)

Item State
Kernel row tunnr-kernel-k8s 6.12.104, x86_64
Join, rusternetes-node saved: 169 measured calls → 14 options
Join, rusternetes-node-aya saved: 18 calls → 5 options
Runner needs .nornir/kernel-runtime-needs/tunnr-kernel-sandbox/heimdall.json, on the sandbox row
Coverage, rusternetes-node 2026-11-04, suite bridge: 992 symbols, 309 ran, 54 never ran, 629 not measured
Coverage, rusternetes-node-aya suite aya: 1028 symbols, 322 ran, 64 never ran
Vetoes none active. CONFIG_MOUSE_PS2_BYD was dropped on 2026-11-06 when CONFIG_IO_URING changed the fragment; it comes back only through a new coverage run and veto
Protect list .nornir/kernel-vetoes/rusternetes-node/protect.list

Next for tunnr: run coverage on the current fragment (step 6), then the veto loop (step 7).

Who does what

Part Home Licence
The probe, the profiles, the runner's needs, heimdall graph src heimdall AGPL-3.0
The possible set (cargo-mimir-dump, mimir-exec) nornir (Mímir)
The join, the fragment, coverage, vetoes, receipts edda nornir-build-thing, tunnr tunnr-build-thing MIT
The schemas of every file edda nordisk-kernel-evidence MIT

The homes share files, never code: Build Thing never links Heimdall.