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

nordisk/edda

.nornir/for-idiots.md

edda — for beginners

No jargon. What this repo is and why it exists.

The one-sentence version

edda is a box of reusable tools that used to live inside the big nornir program but were taken out so other programs can use them too — without having to drag the whole of nornir along.

The name

In Norse mythology the Edda is the collected book of stories. Here it is the collected book of shared library crates: small, self-contained pieces of code that do one job well.

Why take them out of nornir?

Imagine nornir is a huge Swiss-army knife. korp and holger only wanted the scissors and the screwdriver, but to get them they had to carry the whole knife — every blade, the corkscrew, all of it. That is wasteful and tangles everything together.

So each genuinely-reusable tool was lifted into its own little crate in edda. Now a program adds just the scissors. nornir still works exactly as before, because it picks the tools back up under their old names — nothing it called changed.

What is in the box

"The test just sat there for seven minutes" (2026-08-30)

The boot proofs run a real machine and wait. If the machine never says anything, the proof waits out its whole budget — 420 seconds — and then reports a failure built out of guesses ("no boot signature on the disk"), which is a description of what the waiting found, not of what went wrong.

Two things changed so that stops happening.

The appliance is allowed to end the test. When our own software gives up it says so out loud, on the console: INSTALLER: FAILED hwdetect: no usable target disk. The proof now reads that line the moment it appears, stops, and puts the appliance's own sentence in the report. Same machine, same medium: 420.1 s → 3.8 s.

A machine we cannot possibly install on is refused before anything starts. Some cloud shapes take a disk image, not a bootable CD; some put their disk on a wire our medium has no driver for. Both are readable from what is already written down, so the door says no in 0.03 s and tells you which fact stopped it — instead of booting a machine that was never going to work.

And "it timed out" is now its own answer, spelled [TIMEOUT], with its own exit code. It is not a failure: a failure means we measured something and it was bad. A timeout means we measured nothing, and pretending those are the same thing is how a test that proves nothing gets mistaken for a test that passed.

"It boots on my laptop but the cloud shows a blank screen" (2026-08-30)

An .iso file is shaped like a CD. Every machine knows how to start a CD.

But most clouds do not offer you a CD drive. You upload the file and they turn it into the machine's hard disk. And a PC that starts from a hard disk looks at one place only — the very first 512 bytes — for two magic bytes, 55 AA. A plain .iso has zeros there. So the machine looks, finds nothing, and stops. No error, no output, nothing: we measured seven minutes of a completely empty serial log.

The fix has a name — a hybrid image. We put a tiny bit of boot code in the first 512 bytes (which an .iso leaves empty anyway, so the file does not get any bigger), and that code loads the same boot loader the CD path uses. One file, both ways in: burn it to a CD, write it to a USB stick, or upload it to a cloud as a disk image.

In the recipe it is one word, medium_boot = "hybrid". To check a file you were handed:

dd if=whatever.iso bs=1 skip=510 count=2 2>/dev/null | od -An -tx1
#  55 aa   → it can boot as a disk
#  00 00   → CD only; a cloud that imports it will show you a blank screen

The one rule to remember

Moving a tool never breaks anything. A tool can move into edda, get published, and the programs that used it keep working — they just point at the new home. Add alongside, never rip out. That is the whole philosophy: additive, no duplication, no surprises.

"Why is CONFIG_XXX in my kernel?" — for idiots

A kernel we build has about 820 switches turned on. Our recipes name 84 of them. So for nine switches out of ten the honest answer used to be "the kernel put it there, we don't know why" — 764 of 849 lines in kernel.why.toml said exactly that.

Kconfig, the kernel's own configuration language, has three ways for one switch to turn on another:

Follow those backwards far enough and you land on something you can actually point at. That is what the solver does, and it writes the walk into kernel.why.toml as a chain:

CONFIG_VT [declared] --select--> CONFIG_INPUT

Read it right to left: "CONFIG_INPUT is in your kernel because CONFIG_VT selects it, and CONFIG_VT is there because WE asked for it — the appliance's only console on a cloud VNC is the VGA text console." Take that one line out of the recipe and CONFIG_INPUT goes with it. That is not a guess; the test proves it by actually taking the line out and rebuilding the config.

Some chains are five links long:

CONFIG_BLK_DEV_INITRD --default--> CONFIG_RD_ZSTD --select--> CONFIG_DECOMPRESS_ZSTD
  --select--> CONFIG_ZSTD_DECOMPRESS --select--> CONFIG_ZSTD_COMMON --select--> CONFIG_XXHASH

"There is a zstd hash function in your kernel because your PID 1 rides an initramfs."

Four things the chain can end at, and they mean different things:

it ends at plain English
declared we asked for it. Ours to change.
base-config the kernel preset we started from already had it. Change the preset.
kconfig-default Linux gives it to everybody. Nothing to change.
arch it follows from building for x86_64.

43 of 765 have no chain, and we say so rather than making one up. 41 of them are questions the compiler answers, not the config file — CONFIG_CC_HAS_INT128 means "does your gcc understand __int128", and the only way to find out is to try it. That is why the same recipe gives CONFIG_GCC_VERSION=150200 on oden and 140200 inside the build container. The other two are one choice menu that declares no default and one empty string.

The practical payoff: we can now tell, before starting a 48-second container, that a recipe contradicts itself. Turn the serial port off for a machine that has none, and the "print the boot log to the serial port" line underneath it can no longer quietly go with it — the build refuses and names both lines.