SeedSpec::toolchain_dir resuelve el dir del compilador dentro de la semilla sellada, listo para que el sandbox lo bindee como /opt/zig; seed_build_config deriva una BuildConfig que cross-compila con el seed pinned, no con el zig del host. Stage 0 sigue siendo ingesta pura: el layout se resuelve al usarlo (zig en la raíz o en el hijo versionado), con error claro si falta o es ambiguo. Decisiones del Lote 4 registradas en ADR 0008 / SDD 11: - coreutils+shell = busybox (balsa desechable; GPLv2 no contamina el final) - init arje diferido (Stage 1 con init mínimo provisional; CRASHED real luego) - layout de semilla: ingesta pura + resolución al usar +5 tests (layouts: raíz / hijo versionado / no-sellada / ambiguo / build_config). Workspace verde. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3.8 KiB
3.8 KiB
ADR 0008 — Bootstrap from-scratch en 3 stages, con zig como semilla
- Estado: aceptada
- Fecha: 2026-06-10
Contexto
El track posterior (SDD 11) baja de "hammer sobre Alpine" a "hammer sobre sí mismo": compilar el sistema completo sin heredar el toolchain ni el userland de Alpine. Hay que decidir cómo se rompe el cordón umbilical sin reintroducir la fragilidad del Stage 0 clásico (cross-toolchain a mano) que ADR 0003 ya esquivó.
Decisión
Bootstrap en tres etapas, todas modeladas como recetas selladas en el Store (sin
mecanismo de build nuevo):
- Stage 0 — toolchain semilla: se ingiere un toolchain ya construido (no se compila)
como una fuente fijada por sha256, y se sella en el store. El
ArtifactHashse deriva de la identidad pinned (clase + versión + sha256), no de rutas del host.ziges la semilla primaria;musl-cross-make(gcc+musl) es escotilla detrás de la misma interfaz. - Stage 1 — userland mínimo: cross-compilado con Stage 0 (musl, busybox, init,
hammerd), ensamblado en un rootfs sellado. Decisiones de implementación fijadas al abrir el Lote 4:- coreutils+shell =
busybox(no toybox): el Stage 1 es una balsa desechable —se sustituye por el userland multi-call nativo en Rust/Zig más adelante—, así que se optimiza la velocidad y la compatibilidad de flags POSIX en el pipeline (mismo comportamiento que el rootfs Alpine de desarrollo). Su GPLv2 queda atrapada en una etapa temprana del manifiesto; no contamina el sistema final mutable. - init
arjediferido: Stage 1 arranca con musl+busybox+hammerdy un init mínimo provisional.arje(ADR 0007) —y con él elCRASHEDreal de la Fase 5— se integra como receta git pinned en un lote posterior, sin retrabajo. - layout de la semilla: Stage 0 sella el árbol del tarball tal cual (ingesta pura, no
aplana); el directorio del toolchain se resuelve al usarlo localizando el ejecutable
zigen la raíz o en su único hijo versionado (SeedSpec::toolchain_dir). Stage 0 no adivina layouts y el sandbox recibe unzig_dircorrecto sea cual sea la forma del tarball.
- coreutils+shell =
- Stage 2 — rebuild nativo: dentro del rootfs Stage 1, recompilar con las herramientas de
Stage 1 y comparar hashes (
stage1vsstage1'). Iguales ⇒ auto-alojado y reproducible.
El único insumo del host es la semilla pinned (más un kernel para correr procesos).
Razones
- Reusa el lab entero: las etapas son subgrafos del grafo de dependencias de SDD 02; cero retrabajo.
zigsemilla por las mismas razones que ADR 0003: un binario hermético = compilador + musl + headers, cross al target, determinista. Evita compilar gcc desde cero sólo para arrancar.- Ingerir, no compilar, el Stage 0 mantiene el bootstrap finito y reproducible: el sha256 fijado (ADR 0006) ancla la confianza; quien audita reproduce desde el mismo tarball.
- Stage 2 como verificación convierte la reproducibilidad en una propiedad comprobable, no una promesa — alimenta el log de transparencia (SDD 09 §4).
Consecuencias / riesgos
- La semilla no se compila desde fuente: confiamos en el tarball pinned de
zig. Es un punto de confianza explícito y auditable (su sha256 está en el manifiesto), no oculto. Reducir esa confianza (diverse double-compilation, semilla desde fuente) es trabajo futuro. - El kernel se importa pinned en el primer hito; construirlo desde fuente es ortogonal al auto-alojamiento del userland y queda después.
- Stage 2 expondrá no-determinismos reales (timestamps, paths embebidos, orden de enlace): es el costo esperado de exigir bit-reproducibilidad, no una sorpresa.