Epic: #113 · Milestone: MC9 — Ship
Why
cmd_pi_image today writes a Linux cmdline.txt and a /bin/sh script as /sbin/init, then touches an empty kernel image when none exists. Users need a single .img to flash with dd/Raspberry Pi Imager and a first boot that "just works".
Tasks
- Layout: p1
bootFAT32 512 MB (config.txt,bcm2712*.dtb,kernel_2712.img→ symlink-free A/B viaos_prefix/[tryboot]sections in config.txt — the Pi 5 bootloader supportstrybootfor fallback), p2rootAext2/homefs, p3rootB, p4data(library, cache, settings) grown to disk size at first boot. - Root contents:
init, the TV shell, fonts, icons atlas, web remote assets, root CA bundle, default settings, licences (firmware blob licences kept separate and shown in About). - Image assembly with host tools only (
mkfs-free: our owntools/mkext2.pyexists; FAT32 writer needed — small Python or Home host tool), deterministic (SOURCE_DATE_EPOCH), checksummed, ≤ 256 MB compressed. - First boot: detect unprovisioned data partition → format/grow, generate hostname (
homeos-tv-<serial4>), machine ID, start onboarding. docs/INSTALL_RPI5.mdrewritten to the measured reality (banner removed only when the HIL install test passes).
Acceptance
build.sh pi-image --target=rpi5 --profile=tv on the Mac and in CI yields identical images; flashing + first boot on the rig reaches onboarding (capture) in ≤ 60 s including provisioning.
Size: M · Depends on: #54, #66, #92 · Blocks: #109, #111 (install test)