Skip to content

Building

Astromesh OS is built with mkosi and tested with QEMU. This page covers building the image locally. There are two paths — a Docker container or a WSL2 + KVM dev-loop — but in both cases the real gate is CI.

The entire build toolchain — mkosi, QEMU/KVM, dm-verity, the UKI tooling — is Linux-only. You cannot build the image natively on Windows or macOS. On those hosts you build inside Linux, either a privileged Debian container (Docker) or a WSL2 Debian instance with KVM.

Build inside a privileged Debian trixie container. Bind-mount the source tree, install the toolchain, build the runtime .deb first, then build the image:

Terminal window
docker run --rm -it --privileged -v "$PWD":/work -w /work debian:trixie bash
# inside the container:
apt-get update && apt-get install -y mkosi qemu-system-x86 git python3 python3-pip
# build the runtime .deb first (see .github/workflows/phase0-ci.yml), then:
PHASE0_MODE=stub mkosi build

The --privileged flag is required because mkosi needs loop devices to assemble the rootfs. PHASE0_MODE=stub builds with a local stub provider so the boot-to-agent check can run without a real frontier API key.

For fast iteration without waiting on CI (and without the TCG flakiness of GitHub-hosted runners), reproduce the boot/update gate locally in WSL2 with KVM.

Terminal window
wsl --install -d Debian --no-launch
# enable systemd + drvfs metadata, then apply:
wsl -d Debian -u root -- bash -lc "printf '[boot]\nsystemd=true\n\n[automount]\noptions=metadata\n' > /etc/wsl.conf"
wsl --shutdown
# install the same toolset CI uses:
wsl -d Debian -u root -- bash -lc "apt-get update && apt-get install -y mkosi systemd-ukify systemd-boot systemd-boot-efi mtools dosfstools ca-certificates qemu-system-x86 qemu-utils ovmf curl rsync git python3"
# verify KVM is exposed (needs nested virtualization, default on Win11):
wsl -d Debian -u root -- ls -l /dev/kvm

If /dev/kvm is missing, add nestedVirtualization=true under [wsl2] in %UserProfile%\.wslconfig and run wsl --shutdown.

The runtime .deb rarely changes — it depends only on runtime.pin. Fetch the latest CI build once into dist/ (run on the Windows side, where gh is authenticated):

Terminal window
gh run download -n astromesh-deb -D dist

Run as root in WSL (mkosi needs loop devices). The source of truth stays on D:\; the harness rsyncs into a native ext4 path (~/astromesh-build) and builds there, because drvfs cannot host a mkosi rootfs build:

Terminal window
wsl -d Debian -u root -- bash /mnt/d/monaccode/astromesh-os/tests/local/dev-loop.sh update
TargetWhat it does
buildBuild the image only
bootSingle boot, asserting immutability and health
updateFull A/B v1→v2 update gate (default)
inspectCompare the UKI roothash against the on-disk verity PARTUUIDs
cleanClean build artifacts

The astromesh runtime baked into the image is pinned in runtime.pin:

# runtime.pin
ASTROMESH_REF=<commit SHA of monaccode/astromesh>

To move the image to a new runtime, edit ASTROMESH_REF to a new commit SHA of monaccode/astromesh, then re-run CI. The image is reproducible from that exact ref, and CI fails if it cannot resolve it. Because the runtime .deb depends only on runtime.pin, a bump is the trigger for rebuilding it.

  • Architecture — what the build produces and why (verity, A/B, the 500 MB ceiling).
  • Roadmap — the phase gates that each build must clear.