skills/numericalos-build-iso/SKILL.md
back to source
---
name: numericalos-build-iso
description: Build NumericalOS bootable media - a hybrid ISO that boots from optical or USB, pairing a kernel with the graph-driven initramfs. Use when numericalos-target-classifier routed here, or when the user asks for a NumericalOS ISO, USB stick, bootable image, or bare-metal media. Builds the initramfs first, then wraps kernel plus initramfs in a BIOS/UEFI-bootable image.
---
# Build NumericalOS bootable media
This is the only skill in the family that produces something a physical machine
can boot. It is also the one with the most host prerequisites and the most ways
to produce an artifact that looks right and does not boot.
**Prerequisite:** `numericalos-target-classifier` routed here, which means it
confirmed an ISO builder (`xorriso`/`genisoimage`) and a kernel image are
present. If it did not, do not proceed on hope.
## What NumericalOS does not ship
**A kernel.** By design - "smallest bootstrap" scopes to userspace. You are
pairing NumericalOS with a kernel the user already has or fetches. Say this
plainly; a user expecting a self-contained OS image should hear that this is a
userspace that rides someone else's kernel.
## Step 1 - build the initramfs
Follow `numericalos-build-initramfs` completely, including its Step 5 inspection
and its `NUMOS_LIB` path warning. Do not shortcut it - a bad initramfs inside a
correct ISO fails at exactly the point that is hardest to debug.
You need: `numericalos-initramfs-$ARCH.cpio.gz`.
## Step 2 - obtain a kernel
Three sources, in order of preference:
1. **The host's own kernel** - already matched to the arch, already known-good:
```sh
ls -l /boot/vmlinuz-$(uname -r) # x86
ls -l /boot/Image /boot/Image.gz # arm64
```
2. **A distribution package** - `linux-image-*` on Debian/Ubuntu, `kernel` on
Fedora. Extract without installing if the user prefers.
3. **Upstream build** - correct but slow, and out of scope for this skill. Say so
rather than starting a kernel build inside a media-build task.
**The kernel must match `$ARCH`.** An x86_64 kernel with an aarch64 initramfs
produces a machine that panics in a way that reads like a NumericalOS bug and is
not one.
## Step 3 - lay out the ISO tree
```sh
set -eu
ISO=$(mktemp -d)
mkdir -p "$ISO"/boot/grub
cp /boot/vmlinuz-$(uname -r) "$ISO/boot/vmlinuz"
cp numericalos-initramfs-$ARCH.cpio.gz "$ISO/boot/initramfs.cpio.gz"
cat > "$ISO/boot/grub/grub.cfg" <<'EOF'
set timeout=3
set default=0
menuentry "NumericalOS" {
linux /boot/vmlinuz console=tty0 NUMOS_STATE=/etc/numos.state
initrd /boot/initramfs.cpio.gz
}
menuentry "NumericalOS (serial console)" {
linux /boot/vmlinuz console=ttyS0,115200 NUMOS_STATE=/etc/numos.state
initrd /boot/initramfs.cpio.gz
}
EOF
```
**The serial entry is not optional decoration.** `numericalos-verify-boot` runs
QEMU headless and reads the serial console; without this entry you cannot see the
`numos:` output that tells you whether the boot worked. Keep it.
**On the kernel cmdline.** `NUMOS_STATE=...` on the cmdline is *not*
automatically an environment variable in `/init` - the kernel passes unrecognized
`key=value` pairs to init's environment on most setups, but do not rely on it.
The robust approach is that `bootstrap.sh` defaults it, or the initramfs sets it.
Verify which mechanism is actually in play rather than assuming the cmdline
worked; a silent unset produces `numos: HALT: NUMOS_STATE is unset`.
## Step 4 - build a hybrid image
Hybrid means the same file boots from optical media and from a USB stick written
with `dd`.
```sh
grub-mkrescue -o numericalos-$ARCH.iso "$ISO" \
-- -volid NUMERICALOS
```
`grub-mkrescue` wraps `xorriso` and handles BIOS and UEFI El Torito layout, which
is fiddly to get right by hand. If it is unavailable, `xorriso` directly:
```sh
xorriso -as mkisofs \
-o numericalos-$ARCH.iso \
-isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin \
-c boot/boot.cat -b boot/isolinux.bin \
-no-emul-boot -boot-load-size 4 -boot-info-table \
-eltorito-alt-boot -e boot/efiboot.img -no-emul-boot -isohybrid-gpt-basdat \
"$ISO"
```
The `xorriso` path needs isolinux/EFI stubs staged in the tree. If they are not
present, say so and prefer `grub-mkrescue` rather than producing an ISO that
silently lacks a boot sector.
## Step 5 - verify the image structurally, before claiming anything
```sh
ls -lh numericalos-$ARCH.iso
sha256sum numericalos-$ARCH.iso
file numericalos-$ARCH.iso # expect: ISO 9660 ... (bootable)
```
`file` reporting `(bootable)` is a real check - an ISO without a boot sector says
so here, and that is the cheapest possible catch.
## Step 6 - writing to USB (only if the user asks)
**This destroys the target device.** Never run it without the user naming the
device explicitly, and never infer the device yourself.
```sh
lsblk -o NAME,SIZE,MODEL,TRAN # let the USER identify the device
sudo dd if=numericalos-$ARCH.iso of=/dev/sdX bs=4M status=progress oflag=sync
```
Before running: show the user `lsblk` output, state which device you are about to
overwrite and its size and model, and get explicit confirmation of that device.
Writing to the wrong `/dev/sdX` destroys a disk. If there is any ambiguity, stop
and ask.
## Reporting
State: ISO path, size, sha256, target arch, kernel source and version, whether
`file` reported it bootable, and whether it has been booted (it has not, unless
`numericalos-verify-boot` ran).
"Bootable" from `file` means a boot sector exists. It does not mean NumericalOS
boots. Keep those two claims separate in what you tell the user.
## Related skills
- `numericalos-target-classifier` - run first
- `numericalos-build-initramfs` - stage one of this skill
- `numericalos-verify-boot` - run this next; it is the only skill that may claim a boot