diff --git a/docs/10-roadmap.md b/docs/10-roadmap.md index eea301c7..b3ed5e00 100644 --- a/docs/10-roadmap.md +++ b/docs/10-roadmap.md @@ -194,8 +194,14 @@ Diseño completo en [SDD 11 — Bootstrap from-scratch](11-bootstrap.md). Resume init propio que la Fase 5 difirió ([ADR 0007](adr/0007-arje-como-init-propio.md)). Diseño en [SDD 12](12-init-real.md); corrida y fixes en el [runbook](runbooks/stage1-vm-boot.md). Pendiente: bus único (B.2: exponer el `CRASHED` a la capa de IA) y atestación (A1/A2). - - **Stage 2** ☐ — rebuild nativo dentro del rootfs y diff de hashes ⇒ auto-alojamiento - bit-reproducible. + - **Stage 2** ✅ — rebuild nativo dentro del rootfs y diff de hashes ⇒ **auto-alojamiento + bit-reproducible CONFIRMADO end-to-end (host↔VM)**. `KVM=1 MEM=24576 + ./scripts/selfhost-verify.sh` reconstruye los 4/4 dentro del rootfs Stage 1 con sólo su + propio toolchain (arje-zero cu=1 compila en ~27 min in-VM), sella el rootfs y su + `of_tree(stage1') = b3:0039b2b9…` **iguala** la referencia ⇒ `✓ REPRODUCIBLE: stage1' == + stage1`. El último no-determinismo (#3, `arje-zero`/`codegen-units`) se cazó y eliminó + imponiendo `CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1` en el sandbox (commit `4c9bcc0`). + Corrida y veredicto en el [runbook](runbooks/stage1-vm-boot.md) §8. - Reemplazar el init de Alpine por **tu init** (bus por pipes nativo) — habilita el `CRASHED` real que la Fase 5 dejó diferido. **Decisión: adoptar `arje`** (init from-scratch ya existente en el monorepo `tawasuyu`, con PID 1 + supervisión real) en lugar de escribir uno nuevo — @@ -219,8 +225,14 @@ Diseño completo en [SDD 11 — Bootstrap from-scratch](11-bootstrap.md). Resume los de red en `HAMMER_NETWORK_TESTS`). - ⏳ Único ítem de las fases diferido: `CRASHED` real (necesita supervisión de servicios → init propio del track posterior). -- ⏭️ **Siguiente: arrancar el track posterior** (distro propia). Primer paso natural sin - bloquear nada: el bootstrap from-scratch Stage 0/1 con `zig`/`musl-cross-make`. +- ✅ **Track posterior arrancado y con su hito mayor cerrado:** bootstrap from-scratch + Stage 0 → Stage 1 (booteado en QEMU, arje-zero como PID 1, `CRASHED` real) → **Stage 2 + auto-alojamiento bit-a-bit confirmado** (`✓ REPRODUCIBLE`, host↔VM). Baseline reproducible + 4/4: `of_tree(stage1)=b3:0039b2b9…` (cu=1). +- ⏭️ **Siguiente:** (1) auto-alojamiento *puro* — que hammer construya el toolchain semilla + desde fuente (no el zip de zig + Alpine como sustrato del builder), camino (b) de + [SDD 11 §7](11-bootstrap.md); (2) ítems Stage 1 pendientes: bus único (B.2: exponer el + `CRASHED` a la capa de IA) y atestación arje (A1/A2). ## Notas de entorno - Desarrollo principal: laptop del autor. diff --git a/docs/11-bootstrap.md b/docs/11-bootstrap.md index 6addc3d7..6917b688 100644 --- a/docs/11-bootstrap.md +++ b/docs/11-bootstrap.md @@ -121,7 +121,7 @@ No introduce mecanismo nuevo de build: encadena recetas y persiste el manifiesto pub fn stage0(seed: &SeedSpec, store: &Store) -> Result; // ✅ toolchain semilla pub fn stage1(spec: &Stage1Spec, cfg: &BuildConfig, store: &Store) -> Result; // ◑ musl+busybox+hammerd; init arje ☐ -pub fn stage2(stage1: &RootfsHash, store: &Store) -> Result; // ◑ ancla+verifica; rebuild in-rootfs en VM +pub fn stage2(stage1: &RootfsHash, store: &Store) -> Result; // ✅ ancla+verifica; rebuild in-rootfs en VM ✓ REPRODUCIBLE pub fn all(seed: &SeedSpec, store: &Store) -> Result; // ☐ ``` @@ -141,8 +141,12 @@ entrega el `CRASHED` real— está diseñado en el [SDD 12](12-init-real.md) y * reales, no el hash input-addressed del store) como la referencia a reproducir, y la anota en el manifiesto. `verify_against` la compara con un rebuild `stage1'` producido **dentro** del rootfs (en la VM, recompilando con sólo las herramientas de Stage 1): iguales ⇒ auto-alojado bit a bit. El -determinismo necesario lo dan las rutas fijas (`/src`) + `SOURCE_DATE_EPOCH` del sandbox. El rebuild -in-rootfs es el sub-ítem que queda (corre en la VM destino). +determinismo necesario lo dan las rutas fijas (`/src`) + `SOURCE_DATE_EPOCH` del sandbox + +`CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1` (impuesto por el sandbox tras cazar el no-determinismo del +codegen paralelo de rustc en `arje-zero`). **El rebuild in-rootfs ya se ejecutó end-to-end**: `KVM=1 +MEM=24576 ./scripts/selfhost-verify.sh` reconstruyó los 4/4 dentro del rootfs y su +`of_tree(stage1') = b3:0039b2b9…` igualó la referencia ⇒ `✓ REPRODUCIBLE: stage1' == stage1` +(ver [runbook](runbooks/stage1-vm-boot.md) §8). `SeedSpec { kind, version, url, sha256 }` es la identidad pinned de la semilla; `seed_hash()` deriva el `ArtifactHash` de `(kind, version, sha256)` — no del `url` ni del host, así que @@ -153,7 +157,7 @@ CLI (implementado lo de Stage 0; el resto pendiente): ``` hammer bootstrap stage0 --url URL --sha256 HEX --version V [--seed zig|musl-cross-make] # ✅ hammer bootstrap stage1 --seed-hash HASH [--seed zig] [--recipes DIR] # ✅ booteado en QEMU -hammer bootstrap stage2 --rootfs HASH [--verify CONTENT_HASH] # ◑ ancla+verifica; rebuild en VM +hammer bootstrap stage2 --rootfs HASH [--verify CONTENT_HASH] # ✅ ancla+verifica; rebuild en VM ✓ REPRODUCIBLE hammer bootstrap --all # las tres + reporte de reproducibilidad # ☐ ``` @@ -174,10 +178,11 @@ hammer bootstrap --all # las tres + reporte de reproducibilidad ## 7. Auto-alojamiento: el builder rootfs (camino a Stage 2 pleno) -La verificación de reproducibilidad de Stage 2 ya está operativa y probada: `of_tree` (content-hash -de bytes) + `verify_against`, y los 4 componentes reconstruyen **bit-idéntico** (ver -[runbook §8/§8b](runbooks/stage1-vm-boot.md)). Lo que falta es el **rebuild *dentro* del rootfs -usando sólo sus herramientas** — y ahí aparece el obstáculo real: +La verificación de reproducibilidad de Stage 2 está operativa, probada y **ejecutada end-to-end**: +`of_tree` (content-hash de bytes) + `verify_against`, los 4 componentes reconstruyen +**bit-idéntico**, y el **rebuild *dentro* del rootfs** ya corrió en la VM dando `✓ REPRODUCIBLE` +(variante (a) abajo; ver [runbook §8](runbooks/stage1-vm-boot.md)). El obstáculo que hubo que +resolver para llegar ahí: > El Stage 1 que booteamos es un **runtime** (musl+busybox+hammerd+arje-zero): **no trae compilador** > (ni `zig`, ni `make`/autotools, ni `cargo`/rust, ni `linux-headers`, ni `bwrap`). No puede @@ -206,7 +211,7 @@ veredicto. El determinismo necesario está cubierto (paths fijos `/src`, `SOURCE deterministas). El sub-ítem era: **ensamblar el builder** (variante de `assemble_rootfs` que hidrata el toolchain) y **correr el rebuild anidado** en la VM. -### 7.4 Estado: el ensamblado del builder, hecho ✅ +### 7.4 Estado: builder ensamblado + rebuild in-VM ejecutado ✅ ✓ REPRODUCIBLE El **ensamblado** del builder está implementado (`hammer_bootstrap::builder_rootfs`, variante a): @@ -227,8 +232,12 @@ El builder **no se sella** (su `/toolchain` Alpine no es content-addressed); se `out_dir` para empaquetar como initramfs. Su identidad sí es reproducible: un `ArtifactHash` *lógico* de sus insumos (stage1 + semilla + recetas + binario + tag del toolchain + el driver), anotado en el manifiesto (línea stage 2). Validado contra el store real (Stage 1 `73d7a9be…` + semilla -`3ce721ec…` ⇒ builder `81dad3d9…`, 1.2 GB con el toolchain). El paso que queda es **operacional**: -bootear el builder en la VM y correr `rebuild-stage1` (ver [runbook §8c](runbooks/stage1-vm-boot.md)). +`3ce721ec…` ⇒ builder `81dad3d9…`, 1.2 GB con el toolchain). **El paso operacional ya se ejecutó:** +`scripts/selfhost-verify.sh` empaqueta el builder, lo bootea con KVM y corre `rebuild-stage1` +no-interactivo; in-VM reconstruyó los 4/4 (arje-zero cu=1 en ~27 min) y `of_tree(stage1') = +b3:0039b2b9…` igualó la referencia ⇒ `✓ REPRODUCIBLE: stage1' == stage1`. Lo que queda es la +variante (b), el auto-alojamiento *puro* (toolchain construido por hammer desde fuente, §7.2). +Ver [runbook §8](runbooks/stage1-vm-boot.md). ## 8. Hecho cuando @@ -236,3 +245,9 @@ bootear el builder en la VM y correr `rebuild-stage1` (ver [runbook §8c](runboo propio toolchain y userland con hashes idénticos a los publicados**, sin que ninguna herramienta del host entre al resultado. Ese día Alpine deja de ser una dependencia y pasa a ser, a lo sumo, una conveniencia de desarrollo. + +**Estado (2026-06-12):** el **mecanismo** está demostrado end-to-end con la variante (a) — el +rootfs reconstruye su userland in-VM con `of_tree` idéntico (`✓ REPRODUCIBLE`, §7.4). Falta sólo +la variante (b): que el **toolchain** dentro del builder lo construya hammer desde fuente (no +provenga de Alpine), reemplazando una a una las piezas con Stage 2 verificando cada paso. Ese es +el corte pleno del cordón.