Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Phase 204 — define image/disk assembly contract

At a glance

FieldValue
Phase familyPhase 2 — bootable image
Run commandmake phase 204
Underlying make target/scriptvm/phase2/verify-image-contract.sh
Runs onhost
Main proof/artifactVerifies the disk/image contract before loop devices, filesystems, or mounts.

Phase 204 is deliberately boring and important.

It does not create a disk image. It does not partition anything. It does not format filesystems. It does not mount anything. It does not use sudo.

It only verifies that this book page contains the contract for the next layer.

The reason is simple: before we ask Linux for loop devices, partitions, mounts, and filesystems, we want the target shape written down in human language. Disk-building mistakes are easy to make and annoying to debug. A contract phase lets us agree on the shape first.

The artifact names

The canonical root tree input is still:

artifacts/onix-root-tree/

The future raw disk image will be:

artifacts/onix-image/onix.raw

Temporary mount/work state for image assembly should live under:

artifacts/onix-image-work/

All of these paths are under artifacts/, so they are generated local build outputs and are gitignored.

Root tree vs disk image, again

The root tree is a directory on the host:

artifacts/onix-root-tree/

The disk image is a fake disk file:

artifacts/onix-image/onix.raw

The future Phase 205 job is to copy the root tree into a filesystem inside the disk image:

artifacts/onix-root-tree/
   │
   │ copied into the root partition
   ▼
artifacts/onix-image/onix.raw

That raw image can later be attached to QEMU as if it were a real disk.

Planned GPT partition table

The first ONIX image should use GPT.

GPT is the modern partition-table format used by UEFI systems. It lets one disk contain several named partitions. Names matter here because ONIX will mount partitions by label instead of by fragile device names.

The contract is:

#LabelFilesystemMount pointEarly purpose
1ONIX-ESPvfat/efiEFI System Partition for firmware-visible boot files later
2ONIX-BOOTvfat/bootkernel/initramfs/BLS/systemd-boot files later
3onix-rootxfs/generated ONIX root filesystem from artifacts/onix-root-tree/
4ONIX-PERSISTxfs/persistpersistent machine data such as homes and Nix store later

Proposed early sizes:

ONIX-ESP      512 MiB
ONIX-BOOT       1 GiB
onix-root       8 GiB minimum for the first image
ONIX-PERSIST    rest of the disk

Those sizes can change later, but the labels and mount roles are the important contract.

Why vfat for /efi and /boot

UEFI firmware understands the EFI System Partition as a FAT filesystem. In Linux tools that usually means vfat.

So /efi must be vfat.

For the first image, /boot is also planned as vfat because it keeps the boot partition simple and readable by the early boot tooling. Later we can revisit that if the real boot model needs a different split.

The first fstab marks /efi and /boot as nofail.

That is deliberate. The machine can reach userspace without those partitions mounted. During early bootstrap we may not yet have the final kernel-module and modprobe policy needed for vfat mounts after switch-root. A failed /efi or /boot mount should be visible in logs, but it should not force the whole boot into emergency mode.

Why xfs for / and /persist

The root and persist partitions are Linux-owned filesystems. They need normal Unix permissions, symlinks, device-node support, and good behavior for large trees.

So the contract uses xfs for:

/
/persist

The Phase 1 onix-filesystem package already emits the same policy in the default fstab template:

LABEL=ONIX-ESP      /efi      vfat  rw,relatime,noatime,nofail,x-systemd.device-timeout=10s
LABEL=ONIX-BOOT     /boot     vfat  rw,relatime,noatime,nofail,x-systemd.device-timeout=10s
LABEL=onix-root     /         xfs
LABEL=ONIX-PERSIST  /persist  xfs

That is the reason Phase 200 checks for mkfs.xfs.

Why labels instead of /dev/vda3

Inside Linux, disks appear with names like:

/dev/vda
/dev/sda
/dev/nvme0n1

Partitions appear as:

/dev/vda1
/dev/vda2
/dev/vda3

Those names depend on the virtual hardware, boot order, and driver timing. They are not the identity of the filesystem.

Filesystem labels are much more stable:

LABEL=onix-root
LABEL=ONIX-PERSIST

That is why the ONIX fstab contract mounts by label.

What gets copied into /

The root partition labeled onix-root receives the contents of:

artifacts/onix-root-tree/

That means this host file:

artifacts/onix-root-tree/usr/lib/os-release

becomes this file inside the future machine:

/usr/lib/os-release

And this host symlink:

artifacts/onix-root-tree/etc/os-release -> ../usr/lib/os-release

becomes:

/etc/os-release -> ../usr/lib/os-release

What does not get copied as real data

Some directories are mount points or runtime filesystems:

/dev
/proc
/sys
/run

They should exist as directories in the image, but their contents are created or mounted at boot. We do not copy host /dev into the image. We do not copy host /proc. Those are views of the running host kernel, not package payload.

/persist is also a mount point. The image will have an ONIX-PERSIST partition mounted there. Later ONIX can bind persistent paths from it:

/persist/home -> /home
/persist/nix  -> /nix

What Phase 204 checks

make phase 204 verifies:

  • this Phase 204 section exists
  • the future disk path is artifacts/onix-image/onix.raw
  • the source root tree path is artifacts/onix-root-tree/
  • all required labels are documented:
    • ONIX-ESP
    • ONIX-BOOT
    • onix-root
    • ONIX-PERSIST
  • all required mount points are documented:
    • /efi
    • /boot
    • /
    • /persist
  • the contract mentions both vfat and xfs
  • the current root tree still contains /usr/lib/os-release
  • the current root tree fstab still references the planned labels

This makes Phase 204 a safe checkpoint between “we can assemble files” and “we are about to create filesystems”.

What Phase 204 does not prove

Phase 204 does not prove booting.

At this point ONIX still does not have:

kernel package
initramfs policy
systemd package
systemd-boot installation
BLS entries
real first userspace path

So the next disk phase should be a non-booting skeleton first. That lets us verify partition creation and root-tree copy before mixing in boot complexity.

The immediate sequence after this contract is:

Phase 205 -> create the non-booting disk/root skeleton
Phase 206 -> install the systemd-boot/BLS skeleton