scripts/iso-image.sh arma un ISO 9660/El Torito isohybrid usando el xorriso construido por hammer (recipes/xorriso.toml, dogfooding): GRUB core El Torito (grub-mkimage -O i386-pc-eltorito + iso9660) carga kernel + initramfs del ISO; el rootfs del producto va como cpio.gz (live de RAM). scripts/iso-boot-test.sh valida E2E in-VM: SeaBIOS->GRUB 2.14->Linux 6.16.12->/init (marcador HAMMER-ISO-LIVE-OK)->arje-zero PID1->hammerd->netup(DHCP)->sshd escuchando. El producto entero corre desde el medio live, sin tocar disco. ISO 113M. SDD 13 E5 marcado primer corte. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
8.8 KiB
SDD 13 — Release engineering (Etapa E): imagen auto-booteable, instalador, mirror, upgrades
Estado: EN CURSO. Etapa E del roadmap (
docs/10-roadmap.md, memoria distro-completion-roadmap). Es trabajo nuevo: las Etapas A–D dejaron un sistema que (A) se orquesta, (B) bootea de disco particionado real, (C) tiene userland Rust-nativo y (D) atesta confianza. Falta convertir eso en algo que un tercero instale y arranque sin el host de dev: una imagen que bootea sola (sinqemu -kernel), un instalador a disco físico, un mirror del store y un camino de upgrade.
1. El gap concreto que abre la Etapa E
Hasta B3 la imagen de disco (scripts/disk-image.sh, GPT con /, /store, /var/lib/hammer
dedicados) no se auto-bootea: el drive-rebuild.py la arranca con qemu -kernel <bzImage> -append root=/dev/vdaN — el kernel lo carga QEMU, no la imagen. Eso sirve para verificar el
auto-alojamiento, pero un disco así no arranca en hardware real ni en una VM "normal": le falta un
bootloader en el propio disco.
2. Decisión de bootloader: GRUB BIOS (i386-pc) ahora, UEFI después
- GRUB BIOS (i386-pc) — ELEGIDO para el primer corte. QEMU trae SeaBIOS built-in ⇒ un disco con
GRUB en el MBR + una BIOS boot partition (GPT type
ef02) bootea conqemu-system-x86_64 -drive file=imgsin firmware extra ni-kernel. Se instala en userspace, sin root ni loop:grub-mkimagearmacore.imgygrub-bios-setupescribeboot.img→MBR +core.img→partición BIOS sobre el fichero imagen (es I/O de bloques, no necesita montar). Los módulos de GRUB y el kernel van en/bootde la partición root, quemke2fs -dpuebla desde un directorio. - UEFI (EFI-stub + ESP FAT) — diferido. El kernel ya trae
CONFIG_EFI_STUB=y, pero (a) el host de dev no tiene firmware OVMF usable para verificar el arranque, y (b) poblar un ESP FAT sin root exigemtools(ausente). Camino futuro cuando haya OVMF + mtools (o se construyan): ESP conEFI/BOOT/BOOTX64.EFI= el bzImage (EFI-stub), cmdline porloader.conf/UKI.
3. Layout de la imagen instalable (GPT, BIOS)
/dev/vda1 BIOS boot (ef02, ~2 MiB, sin fs) core.img de GRUB
/dev/vda2 / ext4 [hammer-root] + /boot/bzImage + /boot/grub
/dev/vda3 /store ext4 [hammer-store] artefactos CAS (inmutable)
/dev/vda4 /var/lib/hammer ext4 [hammer-state] estado mutable (journal, overlays)
Cadena de arranque: SeaBIOS → MBR (boot.img) → core.img (BIOS boot part) → lee (hd0,gpt2)/boot/grub/ grub.cfg → linux /boot/bzImage root=/dev/vda2 rdinit=/sbin/init → wrapper /sbin/init monta
vda3/vda4 → exec /usr/bin/arje-zero (PID 1). El wrapper es el mismo puente de B3 (arje todavía no lee
fstab/cards de montaje por sí mismo).
scripts/install-image.sh produce esta imagen. Reusa la técnica de particionado sin-root de B3
(sfdisk + mke2fs -d bajo unshare -r + dd conv=sparse,notrunc) y añade la BIOS boot partition, el
kernel en /boot, los módulos GRUB en /boot/grub/i386-pc y los pasos grub-mkimage/grub-bios-setup.
4. Piezas pendientes de la Etapa E (orden tentativo)
- E1 — imagen auto-booteable (GRUB BIOS): ✅ primer corte (
scripts/install-image.sh). Bootea en QEMU sin-kernelhasta la shell de arje-zero. Hoy empaqueta el builder rootfs (con toolchain); un product rootfs lean es refinamiento. - E2 — instalador a disco físico: un
hammer install <device>que particiona un disco real y vuelca las tres particiones + GRUB (la misma lógica, apuntando a/dev/sdXen vez de un fichero). - E3 — mirror del store: ✅ primer corte (crate
hammer-mirror+ CLIhammer mirror push|pull|status). Replica/storecontent-addressed (BLAKE3) entre máquinas; el hash ES la dirección, así que un mirror es un CAS replicado + resolución por hash. La integridad se ancla enof_tree(content-hash del árbol): el receptor recomputaof_treesobre lo recibido y exige que case el del índice antes de sellar (Store::sealatómico + read-only) — una transferencia corrupta/manipulada se rechaza, no se instala.push/pullsonsync(src,dst)(idempotente, salta presentes, reporta conflictos = mismo hash con otro contenido). Transporte de esta entrega = sistema de ficheros (el remoto es una ruta a otro store, p.ej. un montaje sshfs); el transporte sobre SSH/red que el producto ya expone es un envoltorio posterior. Endurecimiento pendiente: anclar elof_treea una raíz firmada (log de transparenciabootstrap.json/ atestación de Etapa D) para defenderse de un origen plenamente malicioso (hoy el invariante forzado es "el contenido recibido hashea a lo que el índice anuncia", que ataja corrupción de transporte). - E4 — upgrades: ✅ primer corte (crate
hammer-upgrade+ CLIhammer upgrade apply|rollback|status). Aplica un árbol Stage1/producto nuevo (artefacto sellado del store, CAS BLAKE3) sobre el FHS vivo sin reinstalar, dejando una generación, y revierte exactamente al árbol anterior conrollback. Modelo de generaciones (inspirado en NixOS, in-place al FHS porque el boot lee el root directo, no un symlink indirecto): cada generación graba bajo<state>/generations/<N>/sumanifest.json(tree_dir del store,of_tree, parent, lista de ficheros, cambios) y unbackup/con los bytes previos de todo path que pisó o retiró. Atomicidad: proyección fichero-a-fichero (escritura a temporal +rename⇒ cada fichero conmuta atómicamente), concurrent(id de la generación viva) como commit-point; un corte a mitad dejacurrenten la vieja y los backups intactos ⇒ re-apply/rollback recuperan. Diff vs el árbol previo: el apply añade/pisa los ficheros del árbol nuevo y retira los que la generación viva aportaba y el nuevo no tiene. Rollback: deshace en orden inverso (borra lo añadido, restaura desdebackup/lo pisado/retirado) y retrocedecurrental padre — restauración exacta, incluso al estado pre-upgrades. Cada cambio se registra en el journal comoHammerHydrate{artifact=tree_dir}(replay-able). Verificación opcional de integridad: si el llamador anuncia elof_treeesperado (de un índice de mirror E3 o release firmada), el apply lo exige antes de tocar el root. Maneja ficheros regulares + symlinks; valida E2E en host conscripts/upgrade-e2e-test.sh(apply v1→v2→rollback→rollback contra el binario real). GC de generaciones ✅ (hammer upgrade prune [--keep N]/hammer_upgrade::prune): borra las generaciones que el rollback ya no alcanza (huérfanas tras un rollback — las de la cadena viva siempre se conservan);--keep Nademás recorta la cadena a sus N más nuevas (limita la profundidad de rollback, comodelete-generationsen NixOS). Endurecimiento pendiente: journal de intención + replay para sobrevivir un kernel-panic a media escritura; cableado del init de boot para que el rollback sea seleccionable al arranque. - E5 — ISO/medio de arranque: ✅ primer corte (
recipes/xorriso.toml+scripts/iso-image.sh+scripts/iso-boot-test.sh). Medio live que arranca ENTERO desde RAM: GRUB (El Torito) carga kernel + initramfs desde el ISO 9660, el kernel desempaqueta el rootfs del producto a un tmpfs y corre/init— sin tocar disco. Es el medio para correr el instalador (hammer install /dev/sdX) en hardware nuevo o probar el sistema sin instalarlo. Herramental construido por hammer: el host no traexorriso(ygrub-mkrescuelo exige) ⇒recipes/xorriso.tomllo construye desde fuente (GNU xorriso 1.5.8 = libburn+libisofs+libisoburn, zig 0.13.0, estático musl, libs opcionales off ⇒ binario autocontenido).iso-image.shlo dogfooda: arma el ISO con el xorriso del store. Cadena de boot BIOS: reusa los módulos GRUB i386-pc de E1 pero con el core El Torito (grub-mkimage -O i386-pc-eltorito+ módulosiso9660/biosdisk) en vez del core de disco (ext2);xorriso -as mkisofs -b …/eltorito.img -no-emul-boot -boot-info-table --grub2-boot-info --grub2-mbr boot_hybrid.img⇒ ISO isohybrid (booteable como CD y USB/disco). El initramfs es elproduct-rootfslean empaquetado comocpio.gz(live de RAM; la variante con squashfs+overlay para rootfs grandes es refinamiento). Validado E2E in-VM (iso-boot-test.sh, ISO 113M): SeaBIOS → GRUB 2.14 (El Torito) → Linux 6.16.12 hammer-built →/init(marcadorHAMMER-ISO-LIVE-OK) → arje-zero PID1 → hammerd + netup (lease DHCP) → sshd escuchando: el producto entero corriendo desde el medio live. Pendiente (refinamiento): correr el instaladorhammer installinteractivamente desde el live; squashfs+overlay para no cargar todo el rootfs en RAM; build EFI (el kernel ya trae EFI_STUB, falta poblar la ESP).