crates/nornir-build-thing/src/kconfig/base.config
# nordisk VANILLA base fragment — merged over `make ARCH=x86_64 defconfig`.
#
# Every `CONFIG_` line here is a MEASURED constellation lesson, not taste; the
# comment above each names where it was paid for. This file is the ONE authority
# for the `vanilla` hardening profile of the `[[build.kernel]]` kind (see
# `crate::kernel`); the `hardened` profile layers `hardened.config` ON TOP of it,
# so a hardened kernel never loses a boot-contract option.
#
# Merge semantics: later lines win (`crate::kernel::merge_kconfig`), then the
# result is applied via `scripts/kconfig/merge_config.sh -m` + `make olddefconfig`
# inside the build container, and every `=y` below is canary-checked to have
# SURVIVED olddefconfig — an option that silently degrades refuses the build.
# ── boot contract (tunnr `variants/buildroot/board/tunnr/linux.fragment`) ──
# UEFI/OVMF direct boot + serial console: the harness asserts serial markers.
CONFIG_EFI_STUB=y
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
# A VGA TEXT console beside the serial one. MEASURED 2026-08-28 on UpCloud (the
# customer's cloud, SeaBIOS, VNC as the ONLY console): the installed appliance
# went dark after `korp-boot-bios: handing over to the kernel` and nobody could
# say where, because the nuked preset had vetoed CONFIG_VT — the kernel had no
# screen to print on. Locally the same disk served fine over serial. The
# medium's and the installed disk's cmdlines say `console=tty0 console=ttyS0`
# (tty0 first, ttyS0 last = /dev/console) precisely so both consoles carry the
# printk stream; this is the kernel half of that promise. ~30 KB. No FB/DRM.
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
CONFIG_VGA_CONSOLE=y
# A kernel that can SAY what killed it. tinyconfig leaves PRINTK off, so every
# serial log this constellation has ever read carried zero kernel lines — no
# `Linux version`, no oops, no panic text — and on UpCloud 2026-08-28 a boot
# that stopped after the loader's hand-over could not be told apart from one
# that hung in userspace. ~100 KB; `console=tty0 console=ttyS0` already names
# where it goes.
CONFIG_PRINTK=y
# devtmpfs auto-mounted so PID 1 finds /dev/console and /dev/net/tun.
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# initramfs (cpio) boot.
CONFIG_BLK_DEV_INITRD=y
# EVERY CPU the host gives. MEASURED 2026-08-29 on UpCloud (STARTER-2xCPU): the
# dashboard band read `cpu 1x AMD EPYC 7542` on a two-vCPU plan — tinyconfig
# leaves CONFIG_SMP off and NR_CPUS=1, so the appliance ran gunnar-server on
# one core and left the other idle for a day. An appliance kernel that ignores
# CPUs it was paid for breaks the zero-cpu-wait law. SMP=y alone does NOT give
# x86_64's NR_CPUS default (64): tinyconfig's base carries NR_CPUS=1 and
# olddefconfig only lifts it to the SMP range floor — MEASURED CONFIG_NR_CPUS=2
# on the nuked base (lane K, 2026-08-29): enough for STARTER-2xCPU, one core
# short of every bigger plan. So the ceiling is PINNED here (64 = the arch
# default, covers the whole roster; cost is per-cpu bookkeeping, not code).
# Not derived from target.cpu: that fact is a chip name, not a vCPU count.
CONFIG_SMP=y
CONFIG_NR_CPUS=64
# ── the appliance's CONFIGURATION CHANNEL is /proc/cmdline ─────────────────
# MEASURED 2026-08-27, the first end-to-end probe boot of a `nuked` kernel
# (design §7 step 3/4, which nuked.config's own honesty note says had never been
# run): the appliance came up through korp-boot, chainloaded \vmlinuz, drew the
# splash, ran the root-of-trust — and then REFUSED:
#
# INSTALLER: policy: require_signed=YES (no korp.require_signed= token …)
# INSTALLER: selftest archive-root-blake3: FAIL — read /system/app.znippy:
# No such file or directory
#
# while the cmdline korp-boot printed one screen earlier read
# `korp.znippy=/app.znippy korp.entry=gunnar korp.require_signed=1`. Every single
# `korp.*` token was invisible, because `tinyconfig` leaves `CONFIG_PROC_FS` off
# and `/proc/cmdline` is the ONE channel those tokens can arrive on: Linux drops
# any boot parameter with a `.` in its name before init's envp
# (`init/main.c`, `unknown_bootoption`), which is why `korp_installer::cmdline`
# mounts procfs and reads the file. `mount_proc` is non-fatal by design, so a
# kernel with no procfs does not crash — it silently falls through to korp's
# COMPILED defaults, which is how a medium that declares its payload at
# `/app.znippy` goes looking for `/system/app.znippy`.
#
# That failure was documented on 2026-08-03 as a missing `mount_proc` and fixed
# there. It came back through the other half — the kernel — and this is the line
# that closes it in the place the canary can see: base.config is canary-checked,
# so a future preset that drops procfs is REFUSED BY NAME instead of booting a
# deaf appliance. It is the same class as `BINFMT_ELF` in
# `deps-tinyconfig.config` ("the canary CANNOT catch this one … which is exactly
# why the probe-boot step exists") and it is the third member of that list.
CONFIG_PROC_FS=y
# ── /sys: the INSTALLER finds its medium and its target disk by walking it ──
# MEASURED 2026-08-27 on the RELEASED gunnar-installer-…-nowg.iso, booted
# under OVMF on a customer's cloud (UpCloud). The installer's first serial line
# was `mounts proc=MISSING sys=MISSING dev=OK`; `iso9660::read_from_installer_
# volume` walks /sys/class/block for the CD001 magic and `hwdetect` walks
# /sys/block for a disk, both saw an empty directory, and the run died with
# `FAILED payload: /run/installer/BOOTX64.EFI: No such file or directory` — a
# line that blamed a medium that was complete. The kernel.config beside the
# donor's bzImage read `# CONFIG_SYSFS is not set`: tinyconfig leaves sysfs
# off, and the procfs line above (found the same day, on the LIVE medium) did
# not cover it because a live medium never looks for a disk. Same class, same
# place: canary-checked here so no preset can drop it silently, and named in
# `kernel::PID1_BOOT_CONTRACT` so a consumer resolving a donor kernel refuses a
# built .config without it before any medium is composed on it.
CONFIG_SYSFS=y
# ── the payload is EXEC'D OUT OF MEMORY, so memfd is a boot contract ────────
# MEASURED 2026-08-27, the probe boot immediately after the procfs line above
# opened the cmdline channel. Everything worked: the appliance read its own
# `korp.*` tokens, drew ITS OWN splash (gunnar's, not korp's — the entry name had
# been unreadable), and the root-of-trust passed the whole chain over the real
# payload —
#
# INSTALLER: selftest startup root=273bbdde6dce archive-root-blake3=PASS
# signature-over-root=PASS rollback-floor=PASS per-chunk-blake3=PASS
# INSTALLER: exec argv: ["gunnar", "serve", "--no-grpc", "--admin-key", …]
# INSTALLER: FAILED exec: memfd_create: ENOSYS: Function not implemented
#
# korp-start never writes the verified app to a filesystem: it seals it into an
# anonymous file and `fexecve`s that (the whole point — the bytes that were
# verified are the bytes that run, with nothing on disk in between). `tinyconfig`
# leaves `CONFIG_MEMFD_CREATE` off, and `CONFIG_EXPERT=y` (minimal.config) is what
# makes it droppable at all, so the appliance verified its payload perfectly and
# then had no way to execute it. Same class as `BINFMT_ELF` in
# `deps-tinyconfig.config`: not a smaller kernel, a brick.
CONFIG_MEMFD_CREATE=y
# ── the appliance's DATA ROOT is RAM, and RAM-backed means tmpfs ────────────
# MEASURED 2026-08-27, the probe boot immediately after the memfd line above let
# the payload run. gunnar's own voice, so PID 1 IS the verified appliance:
#
# INSTALLER: data: no korp-data partition — /data is RAM-backed
# gunnar: opening the control store /data/control.redb: I/O error:
# Function not implemented (os error 38)
#
# The medium DECLARES its data root as RAM (`korp_installer`'s own line, and
# gunnar's live ISO has no data partition by design — its whole state dies with
# the boot). On Linux a writable RAM-backed filesystem is tmpfs, and tmpfs is
# `CONFIG_SHMEM`; `tinyconfig` leaves both off, so `/data` was the initramfs
# ramfs, which implements a strict subset of the file operations a real store
# needs. The argument is not about redb: a product whose declaration says the data
# root is RAM must be given a kernel that HAS one.
#
# SHMEM is also memfd's proper backing store (`shmem_file_setup`), so the line
# above stops depending on the ramfs fallback happening to work.
CONFIG_SHMEM=y
CONFIG_TMPFS=y
# ── a single-writer store takes an EXCLUSIVE LOCK, so flock is a boot contract
# MEASURED 2026-08-27, twice. With procfs, memfd and tmpfs in place the appliance
# ran its own binary and got one layer further:
#
# gunnar: opening the control store /data/control.redb: I/O error:
# Function not implemented (os error 38)
#
# and the same line survived adding tmpfs, which is what ruled the filesystem out
# as the cause. ENOSYS is a syscall that is NOT COMPILED IN, and `redb`'s file
# backend opens with `libc::flock(fd, LOCK_EX | LOCK_NB)` as its FIRST act
# (`redb-2.6.3/src/tree_store/page_store/file_backend/unix.rs:37`) — the exclusive
# lock that makes "one writer" true. `tinyconfig` leaves `CONFIG_FILE_LOCKING`
# off (EXPERT-gated), and flock without it is ENOSYS.
#
# It is a contract and not a preference: every nordisk appliance that keeps state
# keeps it in a single-writer embedded store, and a kernel that cannot express an
# exclusive lock cannot run one. Read it beside `korp_installer`'s own line about
# the second writer whose lock a running server would lose.
CONFIG_FILE_LOCKING=y
# ── an async service needs the poll/notify family, and std needs futex ──────
# MEASURED 2026-08-27, the probe boot after the flock line above. The store OPENED
# — gunnar logged its own engine choice, so this is the appliance talking, on the
# thin build's declared engine:
#
# INFO gunnar::store: the storage engine for this process
# event="store.engine_selected" engine="znippy" compiled=znippy
# gunnar: building the git runtime: Function not implemented (os error 38)
#
# ENOSYS again, one layer up: building the tokio runtime. `tinyconfig` drops the
# whole EXPERT-gated syscall family this needs —
#
# # CONFIG_EPOLL / EVENTFD / TIMERFD / SIGNALFD / FUTEX / POSIX_TIMERS is not set
#
# `epoll` is the reactor, `eventfd` is how a worker is woken, `futex` is what every
# `std` mutex and `Once` compiles to on Linux (not optional for a Rust binary at
# all), and `timerfd`/`signalfd`/POSIX timers are what a service's timeouts and its
# graceful shutdown are made of. This is one line of argument, not six
# preferences: every nordisk appliance is an async Rust service, so this family is
# as much a boot contract as `BINFMT_ELF`.
#
# COST, measured over the five additions in this block: the `nuked` bzImage went
# 1 725 440 B (as declared, which never booted) → 1 807 360 B with everything up to
# flock. Under 5% for a kernel that runs the product.
CONFIG_EPOLL=y
CONFIG_EVENTFD=y
CONFIG_TIMERFD=y
CONFIG_SIGNALFD=y
CONFIG_FUTEX=y
CONFIG_POSIX_TIMERS=y
# ── userspace WireGuard (boringtun) data plane — DECLARED, not baked ───────
# `CONFIG_TUN=y` stood here and was the flagship of the whole preset-only
# problem (owner, 2026-08-29): *"a `nuked` kernel built for `wireguard=off`
# still carries CONFIG_TUN=y, because the fragment says so and no feature ever
# said why."* It is gone from the donor's shape (lane AE, 2026-08-30). The
# consumer that opens `/dev/net/tun` declares it — gunnar's
# `[[build.installer.options]] wireguard` — so `--opt wireguard=off` produces a
# kernel with no TUN at all and `--opt wireguard=on` pulls it back in BY
# CLOSURE, named in `kernel.why.toml` with the feature that asked. That
# round trip is the bijection the model promises; a fragment line could only
# ever have been half of it.
#
# CONFIG_WIREGUARD stays deliberately absent everywhere (nuked.config vetoes it,
# the appliance-boot contract requires the `=n`): the appliance does WireGuard
# in userspace, and "userspace WG needs ZERO modules and stops caring which
# kernel was packed".
# ── virtio (the install record, 2026-08) ───────────────────────────────────
# virtio-net is the ONLY NIC an appliance initramfs can drive: distro kernels
# carry E1000 as =m and an initramfs has no modules — QEMU's default NIC would be
# dead metal (e1000 ⇒ no eth0 ⇒ PID-1 exits ⇒ panic). Measured on distro-oem:
# VIRTIO_NET=y, E1000=m.
CONFIG_VIRTIO=y
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_NET=y
# The installer's disk lane: measured VIRTIO_BLK/PCI=y builtin on the oem
# bzImage; the target disk is `vda`, and a medium attached as a virtio copy is
# `vdb` (SATA/AHCI are =m on distro kernels, so under one of those the medium must
# ride virtio too). A kernel that carries ATA_PIIX/SATA_AHCI + BLK_DEV_SR built in
# reads the cdrom itself as `sr0` — the gunnar appliance donors do, via tunnr's
# own fragment (3f7c2ff) — and korp-installer >= 0.1.14 asks `sr*` first.
CONFIG_VIRTIO_BLK=y
# ── media + ESP filesystems (the install record's FAT/cp437 lesson) ────────
# ISO-9660 builtin (distro kernels ship it =m; the =m shape is exactly what an
# initramfs cannot load — same wall as AHCI above).
CONFIG_ISO9660_FS=y
# vfat for the ESP. MEASURED 2026-08-13, first boot of a real installed disk:
# `FAT-fs (vda1): IO charset iso8859-1 not found` → EINVAL, because the distro
# default is CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" while CONFIG_NLS_ISO8859_1
# is =m. cp437 is FAT's own codepage; every name we write is ASCII. Both NLS
# tables are built in here so NEITHER the named charset nor the inherited
# default can be a loadable module that is not there.
CONFIG_VFAT_FS=y
CONFIG_FAT_DEFAULT_IOCHARSET="cp437"
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ISO8859_1=y