gioser

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 188Rust 55Shell 30Markdown 281211 Definiciones

lab: el rootfs se ANCLA en una imagen pineada — deja de resolverse con apk

Al entrar el toolchain en hash_inputs (58d3161) se abrio un agujero
operativo: los repos del rootfs apuntan a Alpine edge, que es RODANTE, asi
que dos maquinas que corran bootstrap-devfs.sh en fechas distintas resuelven
toolchains distintos y NO COMPARTEN NI UN ARTEFACTO. Ni cosecha de granja, ni
mirror pull, ni un cache-hit. Cada worker efimero nacia con un lab propio.

`apk add` contra edge es irrepetible por diseno (edge sirve solo la ultima
version). La salida no es repetir la resolucion, es no repetirla: el rootfs
se construye UNA vez y se TRAE pineado por sha256 — el mismo trato que ya
reciben alpine-minirootfs y zig en este script.

  imagen: 294 M comprimida (1,1 G extraida)
  sha256: db8a32526ad41fbcc76223842dbb4eb65c8ed576570b82152dc7c5691fc2a349

EL TAR ES DETERMINISTA A PROPOSITO (--sort=name --mtime=@1 --owner=0
--numeric-owner): sin eso el sha depende del orden del directorio y del uid
de quien empaqueta, dos empaquetados del MISMO rootfs darian imagenes
distintas y no habria forma de verificar que una imagen es la que dice ser.
Comprobado: dos pasadas, mismo sha.

EL REEMPLAZO ES POR ROTACION, no extrayendo encima: a medias quedaria una
MEZCLA de dos rootfs, que es justo lo que esto existe para evitar.

VERIFICADO de punta a punta: bajada del box, sha intacto tras el viaje,
extraida, y la huella de lab que produce es IDENTICA a la del rootfs vivo
(zlib -> b3:c7a7338f... en las dos). O sea que una maquina que arranque de
esta imagen comparte store.

--from-scratch conserva el camino viejo: es como se FABRICA una imagen nueva.
Reconstruir el corpus es el precio de ese cambio, y ahora es explicito.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

0de4bf08aaeae609481851fec2c3373d92a93ccc · por Sergio · (hace 2 meses) · padre 58d31618b · ver el árbol en este commit

Delta semántico

Ninguna definición cambió: el cambio es de texto, documentación o datos.

2 archivos · +140 −0

diff --git a/scripts/bootstrap-devfs.sh b/scripts/bootstrap-devfs.sh
index ccd80a8f..63b93d49 100755
--- a/scripts/bootstrap-devfs.sh
+++ b/scripts/bootstrap-devfs.sh
@@ -21,6 +21,7 @@
 #   ./scripts/bootstrap-devfs.sh --skip-apk       # sólo rootfs + zig
 #   ./scripts/bootstrap-devfs.sh --force-zig      # re-descarga zig
 #   ./scripts/bootstrap-devfs.sh --relock         # acepta el toolchain actual como lock
+#   ./scripts/bootstrap-devfs.sh --from-scratch   # resuelve el rootfs con apk en vez de traer la imagen
 
 set -euo pipefail
 
@@ -34,6 +35,7 @@ GO_VER="${GO_VER:-1.26.4}"
 GO_SHA256="${GO_SHA256:-1153d3d50e0ac764b447adfe05c2bcf08e889d42a02e0fe0259bd47f6733ad7f}"
 
 SKIP_APK=0
+FROM_SCRATCH=0
 FORCE_ZIG=0
 FORCE_ROOTFS=0
 RELOCK=0
@@ -43,6 +45,7 @@ for arg in "$@"; do
         --force-zig)   FORCE_ZIG=1 ;;
         --force-rootfs) FORCE_ROOTFS=1 ;;
         --relock)      RELOCK=1 ;;
+        --from-scratch) FROM_SCRATCH=1 ;;
         -h|--help)
             sed -n '2,/^$/p' "$0" | sed 's/^# \?//'
             exit 0 ;;
@@ -66,6 +69,24 @@ ok()  { printf '\033[1;32m✓\033[0m  %s\n' "$*"; }
 
 mkdir -p "$DEVFS" "$TOOLS" "$CACHE" "$WORK"
 
+# --- 0. Imagen pineada del lab (el camino por defecto) ------------------------
+# Desde que el toolchain entra en `hash_inputs`, el rootfs NO se resuelve: se TRAE. `apk add` contra
+# Alpine edge es irrepetible por diseño (edge sólo sirve la última versión), así que dos máquinas
+# que lo resuelvan en fechas distintas obtienen toolchains distintos y **no comparten ni un
+# artefacto**: ni cosecha de granja, ni `mirror pull`, ni un cache-hit. Con la imagen pineada por
+# sha256 todas las máquinas tienen el MISMO lab y vuelven a compartir store.
+#
+# `--from-scratch` recupera el camino viejo (minirootfs + apk). Es como se FABRICA una imagen nueva:
+#   ./scripts/bootstrap-devfs.sh --from-scratch && ./scripts/lab-image.sh --crear
+# y después se pinea el sha nuevo acá abajo. Reconstruir el corpus es el precio de ese cambio.
+LAB_IMAGE_SHA256="${LAB_IMAGE_SHA256:-db8a32526ad41fbcc76223842dbb4eb65c8ed576570b82152dc7c5691fc2a349}"
+
+if [[ $FROM_SCRATCH -eq 0 ]] && [[ ! -f "$ROOTFS/bin/busybox" ]]; then
+    log "rootfs: trayendo la imagen pineada del lab"
+    LAB_IMAGE_SHA256="$LAB_IMAGE_SHA256" "$REPO_ROOT/scripts/lab-image.sh" --traer
+    SKIP_APK=1   # la imagen ya trae el toolchain resuelto; volver a correr apk lo movería
+fi
+
 # --- 1. Rootfs Alpine ---------------------------------------------------------
 if [[ $FORCE_ROOTFS -eq 1 ]] && [[ -d "$ROOTFS" ]]; then
     log "rootfs: --force-rootfs ⇒ borro $ROOTFS"
diff --git a/scripts/lab-image.sh b/scripts/lab-image.sh
new file mode 100755
index 00000000..6035c595
--- /dev/null
+++ b/scripts/lab-image.sh
@@ -0,0 +1,119 @@
+#!/usr/bin/env bash
+# lab-image.sh — empaqueta y publica el ROOTFS YA BOOTSTRAPEADO como imagen pineada por sha256.
+#
+# ── POR QUÉ EXISTE ──────────────────────────────────────────────────────────────────────────────
+# Desde el 2026-08-10 el toolchain del lab entra en `hash_inputs` (ver `hammer-core/src/lab.rs`):
+# dos máquinas con distinto `rustc` sellan en direcciones distintas. Eso cierra un agujero real
+# —antes sellaban bytes distintos en la MISMA dirección— pero abre uno operativo:
+#
+#   Los repos del rootfs apuntan a Alpine **edge**, que es RODANTE. Dos máquinas que corran
+#   `bootstrap-devfs.sh` en fechas distintas resuelven toolchains distintos ⇒ **no comparten NI UN
+#   artefacto**. Ni cosecha de granja, ni `mirror pull` entre hubs, ni cache-hit. Cada worker
+#   efímero nacería con un lab propio y reconstruiría todo desde cero.
+#
+# El `apk add` contra edge es, por diseño, irrepetible. Así que el rootfs deja de resolverse y pasa
+# a **traerse**: una imagen construida UNA vez, pineada por sha256 y compartida por todas las
+# máquinas — exactamente el trato que ya reciben `alpine-minirootfs` y `zig` en bootstrap-devfs.sh.
+#
+# ── EL TAR ES DETERMINISTA A PROPÓSITO ──────────────────────────────────────────────────────────
+# `--sort=name` + `--mtime=@1` + `--owner=0 --group=0 --numeric-owner`: sin esto el sha256 depende
+# del orden del directorio y del uid de quien empaqueta, así que dos empaquetados del MISMO rootfs
+# darían imágenes distintas y no habría forma de verificar que una imagen es la que dice ser.
+# Con esto, `--crear` sobre el mismo rootfs reproduce el mismo sha, y eso es comprobable.
+# Ojo: el uid 0 del tar es cosmético — quien extrae sin ser root se queda como dueño igual, y el
+# sandbox usa `--tmp-overlay /`, así que la propiedad de los ficheros del rootfs no influye.
+#
+# Uso:
+#   scripts/lab-image.sh --crear     # empaqueta .dev-fs/alpine, imprime el sha256 y lo sube al box
+#   scripts/lab-image.sh --sha       # sólo calcula el sha del rootfs actual (no sube nada)
+#   scripts/lab-image.sh --traer     # baja la imagen pineada y reemplaza .dev-fs/alpine
+set -euo pipefail
+
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+cd "$ROOT"
+DEVFS="$ROOT/.dev-fs"
+ROOTFS="$DEVFS/alpine"
+
+# El sha de la imagen VIGENTE. Es el contrato compartido: todas las máquinas deben tener ésta.
+# Se actualiza a mano tras un `--crear`, igual que ALPINE_SHA256 y ZIG_SHA256 en bootstrap-devfs.sh.
+LAB_IMAGE_SHA256="${LAB_IMAGE_SHA256:-PENDIENTE}"
+
+SB_USER="${SB_USER:-u647150}"
+SB_HOST="${SB_HOST:-u647150.your-storagebox.de}"
+SB_PORT="${SB_PORT:-23}"
+KEY="${KEY:-$HOME/.ssh/github5}"
+SSH_CMD="ssh -4 -p $SB_PORT -i $KEY -o StrictHostKeyChecking=accept-new -o ServerAliveInterval=30"
+REMOTO="hammer/lab"
+
+log() { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
+ok()  { printf '\033[1;32m✓\033[0m  %s\n' "$*"; }
+
+empaquetar() {  # $1 = destino
+  [ -d "$ROOTFS" ] || { echo "no existe $ROOTFS — corré bootstrap-devfs.sh primero" >&2; exit 1; }
+  tar --sort=name --mtime=@1 --owner=0 --group=0 --numeric-owner \
+      -C "$DEVFS" -cf - alpine \
+    | zstd -19 -T0 -q -o "$1" -f
+}
+
+case "${1:---sha}" in
+  --sha)
+    tmp="$(mktemp -p "$DEVFS" lab-image.XXXXXX.tar.zst)"
+    trap 'rm -f "$tmp"' EXIT
+    log "empaquetando (determinista) para calcular el sha…"
+    empaquetar "$tmp"
+    echo "sha256: $(sha256sum "$tmp" | cut -d' ' -f1)"
+    echo "tamaño: $(du -h "$tmp" | cut -f1)"
+    ;;
+
+  --crear)
+    img="$DEVFS/lab-image.tar.zst"
+    log "empaquetando $ROOTFS (determinista)…"
+    empaquetar "$img"
+    sha="$(sha256sum "$img" | cut -d' ' -f1)"
+    ok "imagen: $(du -h "$img" | cut -f1) · sha256 $sha"
+    log "subiendo a $SB_USER@$SB_HOST:$REMOTO/"
+    $SSH_CMD "$SB_USER@$SB_HOST" "mkdir $REMOTO" >/dev/null 2>&1 || true
+    # El nombre lleva el sha: las imágenes son inmutables y conviven, así que una máquina que
+    # todavía use la anterior no se queda sin su imagen cuando se publica una nueva.
+    rsync -a --partial-dir=.rsync-partial -e "$SSH_CMD" \
+      "$img" "$SB_USER@$SB_HOST:$REMOTO/lab-$sha.tar.zst"
+    ok "publicada como $REMOTO/lab-$sha.tar.zst"
+    echo
+    echo "AHORA: poné este sha en bootstrap-devfs.sh (LAB_IMAGE_SHA256) y commiteá."
+    echo "  LAB_IMAGE_SHA256=\"$sha\""
+    ;;
+
+  --traer)
+    [ "$LAB_IMAGE_SHA256" = "PENDIENTE" ] && {
+      echo "no hay imagen pineada todavía: corré --crear en la máquina de referencia" >&2; exit 1; }
+    img="$DEVFS/lab-$LAB_IMAGE_SHA256.tar.zst"
+    if [ -f "$img" ] && [ "$(sha256sum "$img" | cut -d' ' -f1)" = "$LAB_IMAGE_SHA256" ]; then
+      ok "imagen ya presente y verificada"
+    else
+      log "bajando lab-$LAB_IMAGE_SHA256.tar.zst"
+      rsync -a --partial-dir=.rsync-partial -e "$SSH_CMD" \
+        "$SB_USER@$SB_HOST:$REMOTO/lab-$LAB_IMAGE_SHA256.tar.zst" "$img"
+      real="$(sha256sum "$img" | cut -d' ' -f1)"
+      [ "$real" = "$LAB_IMAGE_SHA256" ] || {
+        echo "!! sha NO coincide: esperaba $LAB_IMAGE_SHA256, llegó $real" >&2
+        rm -f "$img"; exit 1; }
+      ok "sha verificado"
+    fi
+    # Reemplazo atómico-ish: extraer al lado y rotar. Extraer ENCIMA dejaría una mezcla de los dos
+    # rootfs si algo falla a medias, y una mezcla es justo lo que este fichero existe para evitar.
+    log "extrayendo…"
+    rm -rf "$DEVFS/alpine.nuevo"
+    mkdir -p "$DEVFS/alpine.nuevo"
+    zstd -d -q -c "$img" | tar -x -C "$DEVFS/alpine.nuevo"
+    rm -rf "$DEVFS/alpine.viejo"
+    [ -d "$ROOTFS" ] && mv "$ROOTFS" "$DEVFS/alpine.viejo"
+    mv "$DEVFS/alpine.nuevo/alpine" "$ROOTFS"
+    rmdir "$DEVFS/alpine.nuevo"
+    rm -rf "$DEVFS/alpine.viejo"
+    ok "rootfs reemplazado por la imagen pineada"
+    ;;
+
+  *)
+    sed -n '2,/^set -euo/p' "$0" | sed 's/^# \?//;$d'
+    exit 0 ;;
+esac