netup = binario hammer-propio (sync, sólo libc, sin tokio) que configura la red:
levanta el link (netlink RTM_NEWLINK), negocia un lease DHCPv4 a mano sobre UDP
(DISCOVER/OFFER/REQUEST/ACK con flag broadcast), y aplica IP + ruta default +
/etc/resolv.conf (RTM_NEWADDR/RTM_NEWROUTE). Hand-roll de netlink y DHCP porque
no hay cliente DHCP Rust "maduro" para adoptar al estilo ripgrep, y el workspace
es 100% sync (rtnetlink arrastraría tokio). Reemplaza el `ip` estático de busybox.
Validado in-VM contra el DHCP de QEMU slirp: ✓ lease 10.0.2.15 + ruta + DNS +
egress TCP. Autodetecta la NIC (primera no-loopback) y espera carrier por sysfs.
Infra de prueba:
- scripts/drive-netup.py: bootea la VM, corre netup y verifica lease/NAT/DNS.
SLIRP=1 ⇒ red user-mode (cero setup de host); si no, tap del lab.
- scripts/labnet.sh: lab de LAN real opcional (tap directo + dnsmasq + NAT) para
fidelidad de hardware; no requerido para validar netup.
- scripts/netdiag.sh: diagnóstico del camino DHCP del lab (tcpdump+nft+dnsmasq).
Lección: para validar el CÓDIGO conviene slirp primero (cero plomería de host);
la LAN real (firewall INPUT, sutilezas del bridge, perms) es fidelidad posterior.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Primer entregable de release engineering (SDD 13 nuevo): scripts/install-image.sh
produce una imagen GPT que se BOOTEA SOLA en QEMU (`-drive file=img`, sin
-kernel ni firmware extra) — SeaBIOS → MBR → GRUB → kernel del propio disco →
arje-zero PID 1. Capaz de arrancar en hardware real.
Layout: vda1=BIOS boot (ef02), vda2=/ (con /boot/bzImage + /boot/grub),
vda3=/store, vda4=/var/lib/hammer. Reusa el particionado sin-root de B3
(sfdisk + mke2fs -d bajo unshare -r + dd conv=sparse) y el wrapper /sbin/init.
GRUB instalado SIN root ni loop: grub-bios-setup sondea el disco físico del
host (/dev/nvme…, 660 root:disk) para adivinar el root device del dir -d y falla
sin privilegios. Lo reemplaza un patch binario determinista sobre la ABI estable
de GRUB i386-pc: grub-mkimage arma core.img; un script Python escribe core.img
en la BIOS boot partition y parchea los punteros (boot.img off 0x5c kernel_sector
→ LBA de core.img; core.img off 0x1F4 blocklist.start → resto de core.img),
con asserts de los valores por defecto (1 y 2) para fallar ruidoso si la ABI
cambia. boot.img va al MBR sin pisar la GPT protective (sólo 440 B).
Verificado in-VM (KVM, sin -kernel): SeaBIOS → GRUB 2.14 → Linux 6.16.12 →
vda1..4 detectadas, vda2 root + vda3/vda4 montadas por el wrapper (0 errores) →
arje-zero PID 1 + hammerd (store=/store journal=/var/lib/hammer/journal).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ripgrep 14.1.1 (tag, commit 4649aa97) como receta Cargo, mismo patrón que
uutils/findutils/diffutils. Es su propio workspace ⇒ sin patch [workspace].
Estático musl, zig cc sólo linker; MSRV 1.72 ≤ 1.91.1.
ripgrep-no-jemalloc.patch: ripgrep mete jemallocator incondicional en musl64,
pero jemalloc-sys compila C (autotools) y embebe rutas de OUT_DIR ⇒ rompería la
reproducibilidad bit-a-bit del lab. El patch lo quita (dep Cargo.toml + stanzas
Cargo.lock + global_allocator en main.rs) ⇒ build 100% Rust, hermético, con el
allocator de musl. PCRE2 ya es opt-in ⇒ regex puro, sin C.
Construye+sella+corre: ELF static sin interpreter, `ripgrep 14.1.1
features:-pcre2`, búsqueda recursiva/glob OK. `rg` no es flag-compatible con
POSIX grep ⇒ no se symlinkea a grep; convive con el grep interino.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
scripts/disk-image.sh ahora arma una imagen GPT de 3 particiones ext4 en vez
de una sola: vda1=/ , vda2=/store (CAS inmutable), vda3=/var/lib/hammer (estado
mutable). Construcción sin root ni loopback: cp -al stagea el rootfs por
hardlinks (vacía store/ y var/lib/hammer/, instala el wrapper /sbin/init),
mke2fs -d puebla cada ext4 bajo unshare -r (root-owned), sfdisk escribe la GPT
y dd conv=sparse,notrunc empalma cada fs en su offset (imagen sparse, ~2G
reales). El wrapper /sbin/init monta vda2/vda3 y hace exec de arje-zero (el
kernel sólo monta vda1). drive-rebuild.py: root=/dev/vda1 en modo DISK.
Consecuencia resuelta: con /store en su propia partición el sellado cruza
filesystems y rename(2) da EXDEV. Store::seal cae a copia recursiva a un
staging dentro del store (preserva symlinks+modos) + rename store-interno
(atómico, mismo FS) + borrado del origen. Test copy_tree añadido.
Verificado in-VM (kernel hammer, KVM): vda{1,2,3} montados dedicados, 0
errores Cross-device, stage1' == stage1 ✓ REPRODUCIBLE. Cierra el ☐ de
SDD 11 §6 (particionado/montaje en la imagen destino).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
B1 — kernel con disco+FS: linux.toml añade VIRTIO_BLK/VIRTIO_PCI/VIRTIO_NET/EXT4_FS
built-in (=y, sin módulos), aditivo al path initramfs. B2 — infra de disco:
- scripts/disk-image.sh: empaqueta un rootfs como imagen ext4 booteable con
`mke2fs -d` bajo `unshare -r` (archivos root-owned sin sudo, como el cpio
--owner=root:root; evita EACCES en el copy-up de overlay del rebuild).
- scripts/drive-rebuild.py: modo DISK= ⇒ QEMU monta la imagen como virtio /dev/vda
y el kernel arranca con `root=/dev/vda rw rdinit=/sbin/init` — sin initramfs.
Verificado en QEMU/KVM: el kernel monta /dev/vda ext4 como root REAL y arje-zero
arranca como PID 1 DIRECTO, sin el hack /init+switch_root (el muro pivot_root del
path initramfs desaparece por construcción: / ya es un mount pivotable).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
uutils/findutils 0.9.1 --bin xargs (mismo crate/commit/patch que findutils.toml).
Construye+sella+corre: xargs 0.9.1, ELF estático, -n1/-I{}/pipe OK.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
uutils/findutils 0.9.1 (tag 17d852c4, --bin find, static musl). Construye+sella
+corre: find (Rust) 0.9.1, ELF estático sin interpreter, -name/-type/-maxdepth OK.
onig_sys (C vía cc-rs) linkea con el wrapper zig-cc.
Gotcha general de toda receta Cargo de un crate suelto: hammer copia la fuente a
work/sources/ dentro del repo hammer (un workspace), así cargo vendor la absorbe y
aborta. Fix: findutils-workspace.patch inyecta un [workspace] vacío. coreutils lo
esquiva por ser ya raíz de workspace.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
zig 0.13 compila los host-tools del kernel (wrapper que traduce el depfile
-Wp,-MMD,PATH → -MMD -MF PATH; cache de zig deshabilitado), pero el target
choca con la traducción de flags x86 de zig cc (-mtune=generic, -march=x86-64).
gcc se retiene SOLO para {kernel, cmake}; el resto del userland C es gcc-free
vía zig 0.13. Futuro intento limpio: make LLVM=1 con el clang que zig empaqueta.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mata gcc para 5 de las 7 recetas que lo forzaban, vía una escotilla nueva:
- hammer-core/hammer-build: campo `[build].zig_version` por receta. Cuando se
fija, el lab resuelve ese zig (hermano del por defecto, `zig-x86_64-linux-<v>`)
en vez del global, y entra al hash SÓLO si está presente (baseline 9adefb82
intacto). `effective_zig_dir` lo aplica en ensure_layout + Sandbox.
- Causa: BISECT con oráculo flex (reproducido sólo vía lab: musl DINÁMICO) — el
miscompile es una REGRESIÓN de zig 0.14; 0.13.0 compila limpio, 0.14/0.15/0.16
fallan. Es C/musl-dinámico, NO afecta C++.
- Flip a zig_version="0.13.0" (quitando CC=gcc): flex, openssl, elfutils,
binutils, python3. Verificados: `as` 2.45.1 corre, python3 3.12.10 corre
(deepfreeze OK), libcrypto/libelf sellan. Todas son tools (no inputs del 4/4).
cmake queda en gcc: su segfault es C++ (libc++/musl), bug distinto que 0.13 NO
arregla (ni con -static). El kernel queda pendiente de verificar.
Tests: hammer-core/hammer-build verdes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Primera pieza del userland Rust-nativo (Etapa C): adopta uutils/coreutils 0.9.0
(MIT) como receta Cargo — el patrón de hammerd/arje-zero, no recompila C ni se
escribe de cero. Multicall static-musl (12 MB) + 79 symlinks por applet
(ls/cp/cat…), drop-in de busybox/GNU coreutils.
- Esquiva el miscompile de zig por construcción: codegen rustc/LLVM, zig cc sólo
como linker.
- `--bin coreutils` (no `-p`): el paquete tiene lib+bin y `cargo rustc -- flags`
exige un único target (gotcha vs arje-zero).
- Install custom (sólo esa fase; configure/compile siguen auto-Cargo) crea los
symlinks con `coreutils --list`.
Construido + sellado + verificado funcional (ls/cat/echo vía dispatch argv0).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cierra la Etapa A del camino a la distro (SDD 11 §5):
- `hammer_bootstrap::all(seed, recipes_dir, base_cfg, store) -> AllReport`
encadena stage0→stage1→stage2 en una corrida del host y deja el
`bootstrap.json` poblado. El veredicto ✓ REPRODUCIBLE lo sigue sellando el
rebuild in-VM (selfhost-verify.sh); `all` ancla la referencia para comparar.
- CLI: `hammer bootstrap all --url … --sha256 … --version …` y
`hammer bootstrap manifest` (imprime el log de transparencia, §4).
- Refactor: `parse_seed_kind` factoriza el match de `--seed` (estaba duplicado
en 4 sitios del despacho).
- hammer-build: arregla el test stale `detect_cargo` — la fase Cargo evolucionó
a `RF=…`+`-C link-self-contained=no` condicional, pero la aserción esperaba la
forma vieja `RUSTFLAGS="-C linker=…`. Restaura el workspace en verde (43/43).
Tests: cargo test --workspace verde.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
flex/bison estaban en el devfs base (apk) Y como recipes/{flex,bison}.toml en
deps.build del kernel — pero las capas de deps van BAJO el rootfs, así que el
Alpine flex/bison eclipsaba a los hammer y las recetas eran decorativas. Quito
flex/bison del devfs/bootstrap-devfs.sh; ahora el kernel los toma de deps.build
(hammer). Validado: rebuild en store fresco → kconfig compila con flex/bison
hammer (lexer.lex.o/parser.tab.o), HAMMER_KERNEL=1 verify → ✓ REPRODUCIBLE.
(El bzImage difiere en 1 byte del Alpine-flex/bison build: el timestamp embebido
UTS_VERSION, no flex/bison; el kernel no es input del of_tree del 4/4.)
m4 (que bison invoca) se deja en el base como tool autotools general. Con esto
los build-deps ESPECIALIZADOS del kernel (flex/bison/openssl/elfutils) son todos
hammer-from-source; sólo quedan las libs base/runtime (m4, libz/libzstd de libelf,
gcc) como capa de arranque, igual status que libc.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
tools/objtool del kernel enlaza -lelf (libelf+gelf.h); venía de `apk add
elfutils-dev`. Ahora recipes/elfutils.toml construye SÓLO libelf (0.194, lo que
objtool necesita — no libdw/libdwfl/src) desde fuente, wired como deps.build del
kernel. Con esto NINGÚN build-dep del kernel viene de Alpine: el camino del
kernel es 100% hammer-from-source.
elfutils es glibc-céntrico; musl no trae <error.h>/<argp.h>/<libintl.h>/fts/
obstack/rawmemchr. En vez del parche completo de Alpine, shims mínimos en compat/
(vía CPPFLAGS/-include): error.h y argp.h (sólo los usan color.c/printversion.c,
cuyos .o van en libeu.a pero objtool NO referencia ⇒ basta que compilen); libintl
no-op (+ --disable-nls); rawmemchr inline; y libargp/libfts/libobstack.a stubs
para pasar los AC_SEARCH_LIBS del configure (símbolos inertes para libelf). CC=gcc.
bootstrap-devfs.sh: elfutils-dev fuera del NEEDED. Validado: purgué elfutils-dev
del devfs, rebuild → objtool linkea el libelf hammer (sin gelf.h Alpine), y
HAMMER_KERNEL=1 verify → ✓ REPRODUCIBLE bit a bit, DRIVER_RC=0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
certs/extract-cert (host-tool del kernel, lo arrastra CONFIG_SYSTEM_DATA_
VERIFICATION=y) enlaza -lcrypto; venía de `apk add openssl-dev`. Ahora
recipes/openssl.toml (OpenSSL 3.5.4, CC=gcc, libcrypto/libssl ESTÁTICAS vía
no-shared) lo construye desde fuente, wired como deps.build del kernel
(materializado como capa overlay en /usr: libcrypto.a + headers + pkgconfig).
bootstrap-devfs.sh: openssl-dev fuera del NEEDED. El runtime libcrypto3/libssl3
(que curl/git necesitan) lo sigue trayendo Alpine aparte — no es openssl-dev.
Validado: purgué openssl-dev del devfs, rebuild del kernel → certs/extract-cert
compila con el libcrypto hammer (sin Alpine openssl), y HAMMER_KERNEL=1 verify →
✓ REPRODUCIBLE bit a bit, DRIVER_RC=0. libelf (objtool) sigue como bootstrap-lib
apk (elfutils-en-musl es el difícil; de-Alpinizable luego).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
El defconfig compilaba un PC completo (i915/DRM, sound, USB, media, wireless,
infiniband, BT, HID...) — nada de eso lo usa la VM headless del selfhost-verify
(consola serie + e1000 + initramfs + bwrap). Desactivo esos subsistemas + ATA/
SCSI/NVME/MD (sin disco, todo initramfs) + filesystems de disco innecesarios.
Verificado: HAMMER_KERNEL=1 con el kernel lean → ✓ REPRODUCIBLE bit a bit,
DRIVER_RC=0. Build baja de ~35min a ~14min.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CIERRA el frente kernel-from-source: el bzImage hammer-built (Linux 6.16.12,
recipes/linux.toml) bootea la VM del selfhost-verify y el rebuild in-VM reproduce
el of_tree bit a bit (✓ REPRODUCIBLE, DRIVER_RC=0, ~3min).
El muro era bwrap `pivot_root: Invalid argument`: con un kernel hammer el / del
initramfs es el rootfs absoluto del mount-namespace (sin mount padre movible), y
bwrap del sandbox no puede pivotar de ahí (el kernel host lo permitía — quirk
suyo; diagnosticado con un debug-loop de initramfs mínimo, boot ~10s).
Fix portable (HAMMER_KERNEL=1): un /init wrapper PID1 que copia el rootfs a un
tmpfs y hace switch_root, dejando / como mount tmpfs real (pivotable). bwrap
pivota en CUALQUIER kernel. Condicional ⇒ el camino del kernel host (rdinit=
/sbin/init directo) queda intacto. drive-rebuild.py: APPEND env override para
pasar rdinit=/init.
Uso: HAMMER_KERNEL=1 KERNEL=<bzImage> KVM=1 MEM=16384 PRESEED=hammerd \
./scripts/selfhost-verify.sh
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Generaliza el -append hardcodeado a una env var APPEND (default igual). Útil para
el frente kernel-from-source: experimentar con params del kernel sin tocar el
script. Documenta el hallazgo del muro pivot_root del bwrap (el bzImage hammer
bootea pero bwrap falla porque / es el rootfs absoluto, sin mount padre;
rootfstype=tmpfs no lo arregla).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Hito: el kernel hammer-built 6.16.12 BOOTEA la VM del selfhost-verify:
console ttyS0, arje-zero (PID1) levanta, hammerd corre, y la red e1000 vendorea
los crates OK (cargo vendor completo). El compile in-VM falla sólo en el sandbox
bwrap: "pivot_root: Invalid argument" (--tmp-overlay /) ⇒ DRIVER_RC=1.
Iteración de config (vs el kernel Arch/Artix que sí corre bwrap):
- +FANOTIFY +FANOTIFY_ACCESS_PERMISSIONS: hammerd lo usa para el watcher del
diario (sin él: WARN ENOSYS, no fatal — pero ahora queda funcional).
- +OVERLAY_FS_{REDIRECT_DIR,INDEX,XINO_AUTO,METACOPY}: el defconfig los deja off,
el working los tiene =y; hipótesis para el pivot_root del overlay en userns.
Estado: el kernel compila+bootea+inicia+red; falta el pivot del sandbox.
Pendiente tras esto: optimizar config (defconfig es grande, build ~35min).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Avance del frente kernel: el build pasó objtool y certs.
- bootstrap-devfs.sh: +flex +bison +elfutils-dev +openssl-dev al NEEDED.
* elfutils-dev (libelf/gelf.h): objtool lo exige — en x86_64 las mitigaciones
seleccionan CONFIG_OBJTOOL aunque se use frame-pointer unwinder.
* openssl-dev: el host-tool certs/extract-cert lo #incluye — el defconfig
fuerza CONFIG_SYSTEM_DATA_VERIFICATION=y vía KEYS←NFS/integrity/dns_resolver
(no apagable sin desarmar media config).
Ambas son build-libs de arranque (status g++/zlib-dev), SEGURAS para el of_tree
del self-host: hammerd no usa openssl (Cargo.lock limpio) y arje-zero va
preseeded/cacheado (no se reconstruye). De-Alpinizables luego con
recipes/{elfutils,openssl}.toml.
- linux.toml: quité los -d de keyring que no pegan (olddefconfig/syncconfig los
revierten por el select-chain); openssl resuelve extract-cert. Comentario
actualizado con el diagnóstico.
Estado: build compila el kernel completo (objtool+certs OK); defconfig es grande
(GPU/wireless), pendiente el bzImage + boot-test + optimización de config.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Arranque del último frente de soberanía del bootstrap: construir el kernel que
bootea la VM del selfhost-verify (hoy importado pinned). Versión 6.16.12
(coherente con recipes/linux-headers.toml).
Estado: defconfig + ajustes monolíticos (e1000/overlay/userns =y, BTF/firma
off, frame-pointer unwinder). deps.build=[flex,bison,m4] (materializados como
capa overlay en /usr). flex/bison YA funcionan: kconfig parsea y asm-offsets se
genera. BLOQUEO confirmado: tools/objtool necesita libelf (gelf.h) — en x86_64
las mitigaciones seleccionan CONFIG_OBJTOOL aunque se use frame-pointer.
PRÓXIMO PASO: recipes/elfutils.toml (libelf) como dep.build → completa el
bzImage. Luego: KERNEL=<bzImage> ./scripts/selfhost-verify.sh debe dar
✓ REPRODUCIBLE (iterar la config hasta que el builder bootee).
CC=gcc/HOSTCC=gcc (zig miscompila binarios grandes; el kernel es el mayor).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Primeros prerequisitos para construir el kernel Linux desde fuente: el kbuild
usa flex (lexer de kconfig) y bison (parser LALR de kconfig/dtc), ausentes del
toolchain Alpine base. Herramientas de build-time, no inputs del 4/4.
- flex 2.6.4: CC=gcc (zig miscompila el stage1flex que procesa su propio scan.l
→ "unrecognized rule"; mismo gotcha que binutils/python/cmake). link=dynamic.
- bison 3.8.2: zig cc estático (tool chica, corre bien). Necesita m4 en runtime
(recipes/m4.toml) y sus skeletons en /usr/share/bison (BISON_PKGDATADIR).
Validados: flex 2.6.4 / bison 3.8.2 corren y procesan specs (.l→1736 líneas,
.y→1270 líneas).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Toda la variante (b) del SDD 11 §7.2 vivía sólo en scripts/rust-frontier/README
y notas dispersas; el SDD §7.2 y el runbook §8c quedaban en "Pendiente:
linux-headers → bwrap → rust/llvm". Nuevo runbook docs/runbooks/
self-hosting-toolchain.md documenta el end-state:
- las dos categorías de swap y los dos anclajes de of_tree (9adefb82 rustc
Alpine / 7fa6cb4e hammer-rust auto-consistente);
- tabla de las piezas from-source (make/busybox/coreutils/bwrap/linux-headers
/rust/binutils + patch/m4/pkgconf) con hash, flag y si están en-camino;
- las 3 corridas verify (5-swap, rust, capstone 6-swap) con comandos y
resultados ✓ REPRODUCIBLE in-VM;
- el mecanismo --swap (archivo/directorio/rust-overlay);
- gotchas reusables (zig miscompila→gcc, musl 256 TLS keys, CARGO_BUILD_JOBS=1,
zsh :u / word-splitting);
- binutils inerte verificado por bisección.
SDD §7.2 y runbook §8c ahora marcan variante (b) cerrada y enlazan el runbook.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Última pieza del toolchain hammer-from-source (variante b). Misma versión que
Alpine 3.23.4 (binutils-2.45.1-r0). INERTE para el of_tree del 4/4: el lab usa
`zig cc` (ensamblador interno + lld + zig ar/ranlib/objcopy), así que las
recetas del 4/4 no invocan el ld/as de binutils ⇒ el swap no cambia
of_tree(stage1'). Existe para completar la provenance (builder 100%
reconstruible desde fuente), sin flag SWAP_* dedicado (vía SWAPS= como
patch/m4/pkgconf).
CC=gcc/CXX=g++ (NO zig), igual que python3/cmake: zig 0.16 miscompila binarios
musl grandes ⇒ binutils con zig cc segfaultea al correr (as: "Internal error
(Segmentation fault)"). gcc nativo (como Alpine) los produce funcionales.
link=dynamic: libtool descarta el -static del exe final; gcc dinámico enlaza
contra el musl del toolchain, idéntico al binutils de Alpine.
Validado: ld/as/ar/strip/objcopy/nm/objdump/ranlib/readelf reportan 2.45.1 y
pasan smoke funcional (as ensambla, ar archiva, nm lee símbolos, strip reduce,
objdump desensambla).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
✓ REPRODUCIBLE in-VM (2026-06-18): SWAP_RUST=1 KVM=1 PRESEED=hammerd selfhost-verify.sh
→ el host construye stage1 con hammer-rust (of_tree b3:7fa6cb4e), la VM lo reconstruye con
hammer-rust y reproduce 7fa6cb4e BIT A BIT (stage1' == stage1, DRIVER_RC=0). hammerd
reconstruido in-VM = byte-idéntico al host (d08fd273). Cierra el frente rust del
auto-alojamiento: el compilador (rustc 1.91.1 hammer-built, mrustc→1.90→1.91.0→1.91.1) se
auto-aloja bit a bit, con su propio EXPECT_REF (≠ 9adefb82 de Alpine; rustc emite los bytes).
PRESEED=hammerd es OBLIGATORIO con SWAP_RUST: arje-zero (monorepo tawasuyu) vendorea ~1973
crates; un rebuild in-VM completo (PRESEED=all) desborda el rootfs en RAM (ENOSPC). Con
PRESEED=hammerd se preseedea el arje-zero host-built (hammer-rust, locked desde 9967b02c) y
sólo hammerd se reconstruye in-VM. Documentado en el bloque SWAP_RUST y en rust-frontier/README.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bump del commit fijado 35ec8ef9 → 9967b02c (rama tawasuyu selfhost/arje-zero-lockfile =
35ec8ef9 + Cargo.lock del workspace committeado). Causa raíz del drift del baseline: el
monorepo tawasuyu gitignora Cargo.lock ⇒ el fetch de hammer vendoreaba SIN --locked ⇒ las
versiones de deps derivaban en el tiempo ⇒ arje-zero (y of_tree(stage1)) no reproducible
entre corridas (musl/busybox/hammerd sí, por tener lock o ser C). Con el lock fijo, el
fetch detecta Cargo.lock y vendorea --locked ⇒ build bit-reproducible.
Verificado: el fetch resuelve el commit nuevo (mirror --all) y vendorea SIN el warn de
"sin Cargo.lock". El input-hash de la receta cambia ⇒ nuevo baseline estable (≠ 9adefb82,
que queda como ancla histórica pre-lock).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Auto-consistencia de hammer-rust VERIFICADA en host (2026-06-17): of_tree(stage1)
construido con hammer-rust 1.91.1 = b3:7fa6cb4e… reproducido 2× (store-rust y store-rust2,
builds frescos independientes). Es el nuevo EXPECT_REF para SWAP_RUST (criterio elegido:
auto-consistencia, ≠ el 9adefb82 de Alpine). Cableado como default de RUST_EXPECT_REF.
swap-rust-into-toolchain.sh --restore: ahora también elimina lo AÑADIDO por el swap
(rustlib x86_64-unknown-linux-musl + librustc_driver de hammer), no sólo restaura
rustc/cargo. Round-trip apply→restore deja el devfs prístino (verificado: sólo
x86_64-alpine-linux-musl + driver de Alpine). Antes dejaba ~350 MB de cruft.
Nota: los leftovers NO afectaban el of_tree (un build Alpine con/ sin ellos diverge igual
del baseline Jun13 — ver hallazgo de drift abajo); la limpieza es higiene/reversibilidad.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fix de 503e09a, que añadía el flag SIEMPRE: el rust de Alpine (host x86_64-alpine-linux-
musl) NO sólo tiene self-contained apagado — PARCHEA la opción para que sea un error
("option `-C link-self-contained` is not supported on this target"), así que pasarla
rompía el build baseline (verificado: baseline-check falló en el build-script de
proc-macro2). Un rust vanilla (host x86_64-unknown-linux-musl, p. ej. hammer-rust) SÍ la
soporta y la NECESITA (su musl trae self-contained/rcrt1.o que choca con el crt1.o de zig
⇒ duplicate _start).
Ahora condicional por host-triple del rustc: se añade el flag SÓLO si el host NO es
`-alpine-`. Para Alpine ⇒ comando idéntico al pre-503e09a (sin flag) ⇒ 9adefb82 intacto
por construcción. Para hammer-rust ⇒ con flag ⇒ linkea con zig sin duplicar _start.
(`--print`/`--version` no validan -C; sólo el link real lo hace — por eso se discrimina
por host-triple, no por probe.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
El link de las recetas Cargo nativas lo hace `zig cc` (un driver de compilador completo
que ya aporta sus startfiles crt1.o). El target musl vanilla `x86_64-unknown-linux-musl`
trae CRT AUTOCONTENIDO (self-contained/rcrt1.o) y lo pasaría también al linker ⇒
`ld.lld: duplicate symbol: _start` (rcrt1.o vs crt1.o de zig). Añadir
`-C link-self-contained=no` a RUSTFLAGS hace que rustc NO aporte startfiles propios y
deje que zig los provea — para TODAS las unidades (deps, build-scripts, proc-macros y la
crate top; RUSTFLAGS las alcanza a todas, los bin_flags de `cargo rustc --` sólo a la top).
No-op para el rust de Alpine (host x86_64-alpine-linux-musl): su self-contained ya está
APAGADO — de hecho el baseline 9adefb82 linkea sin chocar con zig, lo que sólo es posible
si Alpine NO aporta startfiles self-contained. Imprescindible para un rust vanilla, p. ej.
el hammer-rust del frente self-host (SWAP_RUST): con él, hammerd YA compila y sella con
hammer-rust (antes fallaba en el build-script de proc-macro2).
Verificación baseline (of_tree(stage1) sin swap == 9adefb82 con este hammer) en cola.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
recipe.hash_inputs = source+compiler+target+link+patches+flags+phases+deps; NO incluye
el binario rustc del toolchain. En el ./store compartido, arje-zero/hammerd quedarían
cacheados con los bytes de Alpine ⇒ el swap de rust sería un no-op (REF seguiría 9adefb82).
SWAP_RUST=1 ahora usa un store dedicado (store-rust, override RUST_STORE) para forzar el
rebuild de los 4/4 con hammer-rust, sin tocar el ./store baseline. Así of_tree refleja el
compilador nuevo (auto-consistencia).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pieza rust del auto-alojamiento "variante b" (la última del frente). A DIFERENCIA de
make/busybox/bwrap/coreutils/linux-headers (herramientas que orquestan/copian ⇒ bytes
idénticos ⇒ of_tree 9adefb82 por construcción), **rustc EMITE los binarios del 4/4**
(arje-zero, hammerd) ⇒ un rustc distinto diverge of_tree. Criterio elegido: AUTO-
CONSISTENCIA (REF recomputado con hammer-rust = nuevo EXPECT_REF; la VM reproduce ESE),
no igualdad byte-a-byte con el baseline Alpine.
- swap-rust-into-toolchain.sh: overlay aditivo y reversible de .scratch/rust-1.91.1-prefix
sobre /toolchain. El rustlib x86_64-unknown-linux-musl de hammer y sus .so con hash propio
NO chocan con el x86_64-alpine-linux-musl de Alpine ⇒ sólo se reemplazan /usr/bin/{rustc,
cargo} (Alpine guardado en *.alpine; --restore deshace). Los 4/4 compilan NATIVO (target
x86_64-linux-musl == SANDBOX_NATIVE_TARGET, sin --target) ⇒ hammer-rustc emite para su
triple nativo x86_64-unknown-linux-musl.
- selfhost-verify.sh: SWAP_RUST=1 aplica el swap en $TOOLCHAIN antes del build de stage1
(así el REF host se computa con hammer-rust) y restaura al salir (trap). Sirve TANTO al
REF host COMO al toolchain in-VM (--toolchain $TOOLCHAIN). EXPECT_REF pasa a RUST_EXPECT_REF
(no el 9adefb82 de Alpine). Vars: RUST_PREFIX, RUST_EXPECT_REF.
VALIDADO: el swap aplica/restaura limpio; hammer-rust 1.91.1 (host x86_64-unknown-linux-musl)
compila+corre un crate NATIVO (sin --target) en el devfs — el camino exacto de los 4/4.
NEXT: computar el nuevo RUST_EXPECT_REF (bootstrap stage1+stage2 con SWAP_RUST) y verify in-VM.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ambos hops del climb construidos con el bootstrap real (x.py) en el sandbox devfs,
reusando LLVM 20.1.8 externo en todo (gate >=19) — sin rebuild de LLVM:
mrustc → rustc 1.90.0 → rustc 1.91.0 → rustc 1.91.1
- hop 1 (1.90.0→1.91.0): bootstrap.toml, stage0 = prefix run_rustc musl-host
1.90.0. stage-2 + install → rust-1.91.0-prefix relocatable (rpath).
- hop 2 (1.91.0→1.91.1): bootstrap-1.91.1.toml, stage0 rustc = el 1.91.0 instalado
(bind ro /stage0 vía STAGE0), stage0 cargo = el 1.90.0 (pasa el check minor-1).
extended+cargo. FIX cargo-native-static=true → feature all-static (openssl/curl/
libgit2/libz vendored desde fuente vía gcc), el devfs hermético no tiene OpenSSL.
- run-xpy.sh: añade bind ro opcional /stage0 (env STAGE0) para el stage0 del hop.
VERIFICADO: rust-1.91.1-prefix/bin/{rustc,cargo} corren standalone (solo prefix
montado, rpath). rustc 1.91.1 (ed61e7d7e) host x86_64-unknown-linux-musl LLVM
20.1.8; cargo 1.91.1. Smoke-test compila+corre. Es la versión EXACTA de Alpine,
auto-alojada desde la cadena mrustc. Cada hop ~45min (LLVM reusado).
NEXT: swap rust 1.91.1 en /toolchain + criterio de auto-consistencia (EXPECT_REF).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Frente rust, fase climb: la toolchain mrustc-bootstrapped 1.90.0 (host musl) ya
sirve de stage0 para construir rust 1.91.0 con el bootstrap real (x.py).
- bootstrap.toml: build/host/target=x86_64-unknown-linux-musl, stage0 rustc/cargo
= prefix run_rustc 1.90.0 host-musl, LLVM 20.1.8 reusado vía llvm-config (pasa
el gate >=19, sin rebuild de LLVM), crt-static=false (link dinámico contra el
musl del devfs, como Alpine y la toolchain de run_rustc), rpath=true.
- run-xpy.sh: corre x.py en el sandbox devfs (bind /src, /out, /mrustc) para que
resuelvan los paths de stage0 + llvm-config del bootstrap.toml.
- README: documenta ambos.
dry-run de x.py VALIDADO (stage0 pasa el version-check minor+1, LLVM hallado,
target ok). Build stage-2 de 1.91.0 EN MARCHA en background.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Con el stage0 musl-host, run_rustc all terminó: rustc stage-3 (host=musl, LLVM 20.1.8,
1.90.0-stable-mrustc) + cargo 1.90.0 + sysroot (294 rlibs) construidos y verificados
corriendo. El muro de proc-macros quedó roto (tinystr/displaydoc/icu nativos).
Dos snags finales arreglados (en run_rustc.patch, vs be69c74):
- Makefile:262 (hello_world FINAL): mismos flags CRT que la 172 (--target +
-C target-feature=-crt-static -C link-self-contained=no); solo aflora al completar
stage-2/3.
- Makefile:235 (cargo FINAL): --features vendored-openssl (openssl-sys no halla openssl
del sistema en el devfs hermético; openssl-src lo compila desde fuente). README
documenta ambos como snags #4 y #2(262).
NEXT: climb 1.90 -> 1.91.0 -> 1.91.1.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Con el muro de proc-macros roto (stage0 musl-host), run_rustc avanzó por primera vez
hasta el smoke-test hello_world del prefix FINAL (Makefile:262) y falló igual que el
de stage-1: usa el default musl -static-pie + CRT self-contained ausente
(rcrt1.o/crti.o/crtbeginS.o/crtendS.o/crtn.o + -lunwind estático) -> ld: cannot find.
Fix idéntico al de la línea 172: añade --target $(RUSTC_TARGET) -C target-feature=
-crt-static -C link-self-contained=no a la regla del hello final. run_rustc.patch
regenerado (vs be69c74) e incluye ahora ambas reglas; README snag #2 documenta las dos.
Verificado parcialmente: stage-2 rustc, stage-3 rustc y cargo se construyeron OK antes
del fallo (proc-macros nativos: tinystr/displaydoc/icu compilan). Rebuild en curso.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Al construir el cargo del stage0 musl-host, minicargo (con RUSTC_TARGET seteado,
aunque host==target==musl) corre los build-scripts bajo cargo-build/host/build_X/
pero el crate target hace include!/include_bytes!(OUT_DIR/..) apuntando a
cargo-build/build_X/ (vacío) -> mrustc aborta con signal 6. Rompió en libsqlite3-sys
(bindgen.rs) y en el binario cargo (man.tgz).
Fix: symlink_outdirs() enlaza cada dir target -> host (salidas bit-idénticas porque
host==target), aplicado en un bucle de reintento alrededor del build incremental de
cargo (los sys-crates tardíos -cargo/curl/openssl/libgit2-sys/libssh2-sys- corren sus
scripts al final, así que un solo pase no basta). Documentado como snag #4 en el README.
Verificado: stage0 musl-host rustc 1.90.0 + cargo 1.90.0 construyen y corren con
host=x86_64-unknown-linux-musl; desbloquea proc-macros nativos en run_rustc.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rastrea las correcciones del frente rust que vivían en .scratch/ (gitignored):
- run_rustc.patch (vs mrustc be69c74): minicargo/hello con --target musl;
RUSTFLAGS + sitios de link con link-self-contained=no (CRT vía gcc Alpine,
link dinámico contra musl); proxy host-units con crt-static off (sin inject
de --target, host==target==musl con el stage0 musl-host).
- build-stage0-musl.sh: rebuild del stage0 rustc+cargo con host triple musl
(CFG_COMPILER_HOST_TRIPLE vía RUSTC_TARGET, --target vía MRUSTC_TARGET),
reusa LLVM + bin/mrustc. Gotcha OUTDIR_SUF documentado.
- run-runrustc.sh: corre targets de run_rustc en el sandbox devfs apuntando
al stage0 musl-host.
- README: diagnóstico de los 3 snags acoplados (std-gnu mmap64/open64,
CRT self-contained ausente, host-triple gnu + proc-macros) y el workflow.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
El rustc booteado por mrustc agota los pthread keys de musl al correr
("fatal runtime error: out of TLS keys" — gotcha #4 de mrustc issue #388).
Es el musl del TOOLCHAIN (el loader /lib/ld-musl-x86_64.so.1 que toda tool
del rootfs linkea), NO el musl target del 4/4 (recipes/musl.toml, estático,
intacto). Se añade a bootstrap-devfs.sh un paso idempotente que reconstruye
musl 1.2.5 con PTHREAD_KEYS_MAX=256 (sed en limits.h, build gcc lib/libc.so)
y reemplaza el loader (backup .orig128). ABI-compat: gcc/make siguen
corriendo. Seguro para of_tree: el loader runtime de las tools no cambia los
bytes que emiten; el 4/4 es estático contra hammer-musl.
Verificado a mano: run_rustc pasó el punto donde moría (build de libstd
musl) y avanza (27%+).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
El rebuild de python3 con zig cc pasó el date-time pero el intérprete
`_bootstrap_python` (zig cc) SEGFAULTEÓ en el paso de "freeze modules" —
mismo fallo que cmake: zig 0.16 miscompila binarios musl grandes (un
hello-world dinámico zig sí corre). Fix idéntico: CC=gcc/CXX=g++ en las
fases (gcc nativo del toolchain, como compila Alpine). Con gcc además
-Wdate-time deja de molestar (gcc honra SOURCE_DATE_EPOCH).
NOTA: rebuild gcc EN CURSO (fase compile, ya pasó configure) al commitear;
aún no sellado. zlib (f3ddad6) validado.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Primer intento de ambas falló; correcciones tras diagnóstico con probes aislados:
- cmake: el cmake mínimo que `./bootstrap` compila con `zig c++` SEGFAULTEA al
correr ("Problem while running initial CMake") — interacción libc++/musl de
zig. Fix: forzar CC=gcc/CXX=g++ en las fases (Alpine compila cmake con gcc;
el toolchain ya trae gcc/g++, nativos y funcionales en el sandbox).
- python3: Modules/getbuildinfo.c usa __DATE__/__TIME__ y la build habilita
-Wdate-time; zig (clang) lo promueve a ERROR. Probé que SOURCE_DATE_EPOCH=1
(que el sandbox ya exporta) fija el VALOR a 1970-01-01 (determinista) pero
clang NO silencia -Wdate-time vía SDE (eso es sólo-GCC). Fix:
CFLAGS="-Wno-error=date-time" — degrada a warning, valor sigue determinista.
NOTA: ambos rebuilds estaban EN CURSO al commitear (aún no sellados en store);
si fallan otra vez, commit de seguimiento. zlib (f3ddad6) sí validado.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Dependencias de build que rustc usa para construir su LLVM bundled (mrustc
README: cmake ≥3.4.3 + python3). De-Alpinizan el camino de bootstrap de rust;
hoy satisfechas por apk de arranque.
- cmake 3.31.6: build vía ./bootstrap (zig cc/c++), enlace dinámico, OpenSSL OFF.
- python3 3.12.10: ./configure autotools (zig cc), deps.build=[zlib],
--without-ensurepip --disable-test-modules, enlace dinámico.
NOTA: ambos están EN VALIDACIÓN — sus builds estaban en fase `configure` (sin
errores, zig cc aceptado) al commitear, todavía NO sellados en store. Si la
compilación/instalación falla, requerirán ajuste en un commit de seguimiento.
zlib (f3ddad6) sí está validado y sellado.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
zlib 1.3.1 estática (zig cc) — primera dependencia de build de la cadena rust
construida desde fuente. mrustc enlaza -lz y el toolchain Alpine sólo trae el
.so runtime (sin zlib.h/libz.a/zlib.pc); esta receta sella libz.a + headers +
zlib.pc en /usr (mismo patrón que libcap → materialize_build_deps los overlaya
en el sandbox), reemplazando el zlib-dev de arranque cuando se cablee como
deps.build de la receta mrustc/rust.
Validada: build+sello OK (store cf716586…-zlib). configure de zlib es a mano
(rechaza flags autotools) ⇒ fases override como musl (./configure --static).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Inicia la última pieza del toolchain hammer-from-source: reemplazar el
rustc/cargo de Alpine por una cadena purista vía mrustc (mrustc → rustc
1.90.0 → 1.91.1, sólo 2 saltos). mrustc es C++14 y DEBE compilarse con g++
(clang/zig rompen sus macros tagged_union, ver mrustc issue #388) y enlaza
-lz; el toolchain traía libstdc++ runtime pero no cc1plus/headers ni
zlib-dev. Se añaden `g++` y `zlib-dev` a la lista NEEDED del devfs como
herramientas de arranque (mismo status que make/zig/gcc — el veto purista
es sobre un *rustc* prebuilt, no sobre un compilador C++; zlib-dev quedará
reemplazado por recipes/zlib.toml al avanzar el frente).
.gitignore: ignora .scratch/ (clone de mrustc + fuentes de rustc, multi-GB).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Más de-Alpinización del builder (variante b, SDD 11 §7.2b): recetas para construir desde fuente
tres build-tools del toolchain que hoy vienen de Alpine.
- recipes/patch.toml (GNU patch 2.8): lo invoca apply_patches (hammer-build/fetch.rs) cuando una
receta trae source.patches, p.ej. el overlay de linux-headers.
- recipes/m4.toml (GNU m4 1.4.20): base de la cadena autotools.
- recipes/pkgconf.toml (pkgconf 2.5.1): lee los .pc que materialize_build_deps deja en el sandbox.
Las 3 estáticas musl con zig cc (AutoconfReady), validadas: corren, versión correcta y funcionales
(patch aplica, m4 expande, pkgconf resuelve). El 4/4 mínimo no las invoca ⇒ sin flag SWAP_* dedicado
(swapeables con el escape SWAPS="name=hash:rel"); avanzan "builder reconstruible al completo".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Quinta pieza del toolchain hammer-from-source (variante b, SDD 11 §7.2b): GNU coreutils 9.8
(cp/mkdir/ln/chmod/mv/install…), que el `make install` de musl y busybox invocan.
- recipes/coreutils.toml: empaquetada multicall (--enable-single-binary), mismo layout que el
paquete de Alpine (un /bin/coreutils + ~100 symlinks que despachan por argv[0]). Estática musl
con zig cc, vainilla sin patches (es tool del toolchain, no input compilado del 4/4).
- scripts/selfhost-verify.sh: SWAP_COREUTILS=1 construye la receta y monta el único binario sobre
/toolchain/bin/coreutils — los symlinks del toolchain (cp/mkdir/install/…) lo siguen, un solo --swap.
Bisección host fuerte: con hammer-coreutils pisando el de Alpine (trap-restore en .dev-fs), musl Y
busybox rebuildearon BYTE-IDÉNTICO (57b66a2e, 56664d70). Pendiente: corrida in-VM acumulando swaps.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
hammerd::arje_link se suscribe al bus del init (ENTE_BUS_SOCK), relee el frame
postcard de arje-bus con un mirror mínimo de suscriptor (sin arrastrar el
crate-graph de arje ⇒ hammer sigue standalone) y reenvía cada BusEvent →
crashes → Event::Crashed → agent.sock. Wire verificado byte-a-byte contra
arje-bus real (ulid string, frame Subscribe=[00,01,00,0d]); 2 tests de round-trip
local + frame. Se lanza en thread si ENTE_BUS_SOCK está definido (no-op si no).
Roadmap B.2 marcado ✅ (resta sólo el smoke contra init vivo).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Deja de ser un error "pendiente": hydrate(.., DynamicSpec{interpreter,rpath})
detecta ELF por magic, copia los que necesitan parcheo (no hardlink: patchelf
mutaría el store) y aplica --set-interpreter/--set-rpath atómicamente
(copy→patch→rename); no-ELF y spec vacío siguen hardlinkeando. ensure_patchelf
falla limpio antes de tocar el FHS si falta la herramienta. HydrateReport.patched
cuenta los reescritos. Callers (bus/cli/bootstrap/e2e) pasan None=estático.
4 tests nuevos (incl. camino de error sin patchelf y real gated). Doc §4.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Deja de ser un no-op "pendiente fase 5": apply::apply_init_rule escribe una
regla TOML por servicio (service/action/command); enable/start/restart la
escriben, disable/stop la retiran (idempotente), con guardas de nombre y acción.
CLI y orchestrator la aplican (rebaseando con prefix/overlay). Es el contrato
on-disk que el init (arje) lee para supervisar. 3 tests + doc del formato.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mutation::SourcePatch y RecipeInline ganan campos repo/commit XOR tarball/sha256
(Option, serde-default ⇒ compat con .swm git existentes), resueltos por
swm::swm_source_kind con la misma regla que recipe::Source::kind. swm_bridge
sintetiza la receta según el modo; hammer export reconstruye fuentes tarball
como source_patch en vez de caer a file_drop (provenance fina recuperada).
Prompt del traductor y roadmap actualizados. Tests nuevos para tarball + XOR.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>