Commit Graph
148 Commits
Author SHA1 Message Date
sergioandClaude Opus 4.8 2d1d40a6f1 matar-gcc: migra-zig.sh + veredicto medido — las C-puras SON granjeables (~85%)
¿Las 47 compiler=gcc que harkaq nombró son factibles de migrar a zig, o hay que
matracar? MEDIDO, no adivinado.

migra-zig.sh: para cada receta, build con zig-cc + SMOKE-TEST del binario —
porque el motivo del gcc era segfault en RUNTIME, no fallo de build; un artefacto
que sella pero segfaultea es peor que gcc. Idempotente, acumula por lotes (probar
47 no cabe en un timeout).

Muestra de 13 C-puras: 11 MIGRAN (bzip2 expat json-c gzip htop less libpng
libyaml zstd libffi xz), 2 MATRACA (pcre2: linker version script que zig ld no
soporta; file: subdir magic). ≈85% yield.

VEREDICTO: es GRANJEADA para las ~36 C-puras. El compiler=gcc era deuda histórica
— marcadas con zig 0.13/0.16 (que segfaultaban C clásicos), zig mejoró, el gcc
quedó por inercia. ~85% migran solas; el residuo (~15%) es matraca individual por
causas concretas del build-system.

Las 11 Rust con sys-crate C son otra evaluación (gcc para el C embebido de
libgit2-sys, no el Rust) — se deja para después.

Rollout = decisión del usuario (re-hashea recetas del índice firmado 748). Correr
migra-zig.sh sobre las 47 en la granja VPS da la lista final; promover las
migrables re-firma el índice. Reporte: tandas/needs-review-harkaq/migracion-zig.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 12:31:53 -04:00
sergio 0f6c841782 mirada-usb: force_gpt en la cmdline + post-mortem del viaje NVIDIA
El firmware del tester reparo el GPT dd-eado en el POST y dejo el header
primario invalido (entradas apuntadas a LBA 2016, presentes en LBA 2):
Linux sin gpt en cmdline no cae al respaldo, no vio particiones y la
telemetria MIRADALOG murio en RAM. Con gpt el kernel usa el respaldo.
Documentado: greeter por nouveau GL validado en metal; input muerto queda
abierto con el plan de evidencia para el proximo viaje.
2026-07-16 12:16:04 -04:00
sergioandClaude Opus 4.8 71ec92345f harkaq: el clasificador distingue gcc-compilador de gcc-sonda — matar-gcc son 47 recetas
El límite del modelo que mtdev destapó, cerrado. harkaq clasificaba /usr/bin/gcc
como "sonda esperada" SIEMPRE, así que marcaba `Hermetico` a las recetas que
compilan CON el gcc de Alpine (compiler=gcc = CC=gcc, musl+GNU ld). Estuvo ciego
a la deuda de compilador todo el barrido.

Fix en harkaq-suggest: 5º arg = la receta medida. Si usa compiler=gcc, los
frontends de gcc (gcc/cpp/c89/c99/g++/cc/ld/as) dejan de ser "esperada" y pasan a
DEUDA DE COMPILADOR (matar-gcc). Para compiler=zig-cc (default, 714 recetas) gcc
sigue siendo sonda de configure — esperada, como antes. Verificado: mtdev(gcc) →
deuda de compilador; zlib(zig) → sin deuda.

EL HALLAZGO: matar-gcc no es "{kernel, cmake}" como creía la memoria del proyecto.
Son 47 recetas (36 C puro + 11 Rust con sys-crate C) que dependen del gcc de
Alpine. harkaq lo reveló sólo cuando aprendió a leer el compiler= de la receta —
antes gcc-como-sonda-esperada ocultaba una deuda mayor que toda la que sí detectó.
Reporte: tandas/needs-review-harkaq/deuda-compilador-matar-gcc.md.

Cada una usa gcc porque zig-cc la miscompila (documentado en su receta). Acción
por receta: migrar a zig cuando zig mejore, o aceptar el escape como deuda
conocida. Ahora es una deuda NOMBRADA y contada, no invisible.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 09:43:06 -04:00
sergioandClaude Opus 4.8 35f58aa75b harkaq: granja con volumen + auto-shutdown VALIDADA end-to-end
Test real (worker + volumen + helper), no en el papel. Las 3 piezas:
  1. worker midió lz4+bzip2 → volumen
  2. tras 1 ciclo idle SE AUTO-ELIMINÓ (verificado: el server desapareció solo)
  3. volumen SOBREVIVIÓ con los 2 verdicts; helper efímero ccx13 los recogió al
     hub y se auto-destruyó

3 bugs cazados EN EL TEST (no adivinados):
  - falta --exclude /work: work/=69GB colgó el rsync del up horas
  - cpx11 'unsupported' en hel1 (AMD sin stock) → ccx13
  - collect llamaba harvest con 'local' (rsync a un worker inexistente): ahora
    sólo recoge; clasificar es paso aparte (worker mide, hub clasifica)

+ auto-delete por API REST (curl+metadata), NO hcloud CLI (la golden no lo trae).
El ID propio sale del metadata service, no del nombre.

Cierra los 2 huecos de la 1ª campaña: idle cobrando (ahora auto-delete) y
dependencia de que yo esté viva (ahora el volumen persiste). Runbook:
docs/runbooks/harkaq-granja-volumen.md. Volumen clausurado tras el test (€0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 07:47:36 -04:00
sergioandClaude Opus 4.8 f7916b5ee9 harkaq: granja con volumen persistente + workers auto-terminantes
La arquitectura que la 1ª campaña pidió a gritos (el worker quedó idle ~4h
cobrando). Tres piezas en harkaq-vol.sh {up|collect|close|status}:

1. VOLUMEN persistente (harkaq-cosecha): los workers escriben verdicts a
   /mnt/cosecha, que sobrevive a su destrucción ⇒ NO dependo de estar viva para
   no perder el trabajo.
2. AUTO-SHUTDOWN (harkaq-campana-vol.sh): tras IDLE_CICLOS ciclos con 0
   mediciones nuevas, el worker SE AUTO-ELIMINA. En Hetzner un server apagado
   sigue cobrando ⇒ apagar de verdad es delete. Vía API REST + curl + metadata
   service (NO hcloud CLI: la golden Ubuntu no lo trae; curl siempre está). El
   ID propio sale del metadata, no del nombre.
3. collect: monta el volumen en un helper barato efímero (o un worker vivo),
   rsync al hub, corre el harvest, destruye el helper. close: detach+delete del
   volumen (deja de cobrar, ~€0.44/mes mientras exista).

+ dos fixes de la 1ª campaña horneados en el bucle: borra el artefacto -hkm tras
medir (envenenaba la caché) y los dos gates (ABI>=7 + binario-con-jaula).

El token va al worker (para auto-delete): riesgo acotado — worker efímero, sin
servicios entrantes salvo SSH-por-clave, destruido al terminar.

Bug cazado antes de gastar: las llaves {..} dentro del mensaje de ${1:?...}
cerraban la expansión (CMD salía 'status}').

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 07:23:02 -04:00
sergioandClaude Opus 4.8 7ec2e86b7b harkaq: declarar deps medidas por el kernel (cosecha automática)
Cosecha desatendida de la campaña harkaq. Cada dep de acá la denunció el audit de
Landlock en un build real: el kernel vio al build usarla sin declararla, y el
store del hub confirma qué artefacto la provee (deuda DECLARABLE, §4.5).

Recetas:coreutils elfutils expat file flex gawk

No se tocó ninguna receta con deuda IRREDUCIBLE: declarar algo que el store no
provee rompería el build en vez de arreglarlo. Esas van a tandas/needs-review-harkaq.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 06:30:36 -04:00
sergioandClaude Opus 4.8 21ac060c4b harkaq: declarar deps medidas por el kernel (cosecha automática)
Cosecha desatendida de la campaña harkaq. Cada dep de acá la denunció el audit de
Landlock en un build real: el kernel vio al build usarla sin declararla, y el
store del hub confirma qué artefacto la provee (deuda DECLARABLE, §4.5).

Recetas:gettext-tiny giflib git gnupg gperf gzip htop iproute2 jq kbd libassuan libcap libevent libffi libgcrypt libgpg-error libksba libnl libpng libsass libsodium libssh2 libudev-zero libusb libwebp libxml2 libyaml linux-generic linux-headers linux-metal linux-pam linux lz4 mandoc mtools musl nano npth openssh openssl parted pciutils pcre2 pigz procps-ng python3 readline rsync samurai ca-certificates curl doas dosfstools e2fsprogs fontconfig freetype

No se tocó ninguna receta con deuda IRREDUCIBLE: declarar algo que el store no
provee rompería el build en vez de arreglarlo. Esas van a tandas/needs-review-harkaq.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 23:27:57 -04:00
sergioandClaude Opus 4.8 593a11c034 harkaq: gate 2 de la campaña — ¿el binario TIENE la jaula dentro?
La golden hornea un target/release/hammer viejo y el rsync excluye /target ⇒ si
el cargo build del provisioning no corre, el worker construye alegremente SIN
JAULA y produce cero veredictos.

PASÓ: la primera corrida molió 2 recetas con un binario del 29/06 (0 ocurrencias
de 'harkaq' en strings) y las marcó 'sin veredicto' como si fuera culpa de las
recetas. Ocho horas así son la noche entera perdida, y el log habría dicho
'salteada' — nunca 'estoy ciego'. Es el falso Hermetico de D9 mudado al
orquestador: la ausencia como respuesta tranquilizadora.

Ahora aborta si strings no encuentra harkaq en el binario. Mismo criterio que el
gate del ABI: no medir es mejor que creer que se mide.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 23:24:58 -04:00
sergioandClaude Opus 4.8 f783f0107d harkaq: campaña desatendida — medir→declarar→verificar, sin regex y sin preguntas
El problema real no es el coste de la tanda: es que una pregunta a las 3am son 8
horas de idle. Estas 3 piezas están hechas para que la noche no se desperdicie.

  harkaq-campana.sh   (worker) bucle autónomo: construye cada receta bajo la
                      jaula y guarda los veredictos crudos. Idempotente, saltea
                      lo medido, duerme y re-escanea. NUNCA bloquea esperando: un
                      build que falla no detiene la cola (y su veredicto interesa
                      igual — es cuando más importa saber qué tocó fuera). Gatea
                      por ABI>=7: moler la noche para producir "no sabemos" es
                      peor que no moler.
  harvest-harkaq.sh   (hub, cron) hermano de harvest-go.sh: baja veredictos,
                      clasifica contra el store COMPLETO, aplica las deps
                      DECLARABLES y commitea con `git add` explícito. La deuda
                      IRREDUCIBLE NO se toca (declarar algo que el store no
                      provee rompe el build en vez de arreglarlo) → needs-review.
  harkaq-add-deps.py  editor de recetas conservador: idempotente, preserva las
                      deps existentes, y ANTE LA DUDA NO TOCA (valida que el
                      resultado siga parseando como TOML y no encoja). Corre de
                      noche sobre la fuente de verdad del catálogo: un fichero mal
                      editado a las 3am no da un error, da una receta corrupta que
                      nadie mira hasta el lunes.

POR QUÉ NO HAY REGEX EN EL CAMINO CRÍTICO: intenté sacar la lista de "a quién le
falta declarar make" con uno y falló dos veces en la misma tarde. `\bmake\b`
colaba `cargo-make` (el guión ES límite de palabra). La versión estrecha perdía
LAS CINCO recetas donde harkaq había medido la deuda de verdad (`compile = "make
…"`: el carácter previo es una comilla). El número bailó 69→45→99 según el
retoque. Construí una herramienta de medición y después intenté adivinar con un
regex — la lista buena la da el kernel. El regex queda sólo para ORDENAR la cola.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 22:53:57 -04:00
sergioandClaude Opus 4.8 ee51e5d596 harkaq: el kernel de hammer ya trae Landlock — ABI 7 verificado BOOTEANDO (Fase 1 precond. 1)
recipes/linux.toml: -e SECURITY -e SECURITY_LANDLOCK -e AUDIT. Era lo único que
faltaba para que harkaq corra DENTRO de hammer (VM/metal) y no sólo en el laptop
y la granja. El CONFIG_LSM del defconfig ya lista `landlock` de primero ⇒ bastó
encenderlo, sin tocar la cadena de LSMs.

AUDIT se fija explícito aunque ya viniera =y por defconfig: sin él el kernel no
emite un solo registro y harkaq certificaría TODO como hermético en silencio
(§3.4). Es la precondición de la que cuelga la evidencia; no se deja al azar de
un default.

VERIFICADO BOOTEANDO, no leyendo el .config — que dice lo que se compiló, no lo
que el kernel hace al arrancar. Misma disciplina de §3.1 (el ABI se consulta por
syscall, jamás por versión) llevada a la verificación: el único que sabe si
Landlock está vivo es el kernel vivo. scripts/harkaq/vm-abi-probe.c es un /init
de initramfs mínimo que pregunta y apaga:

    ===== HARKAQ EN EL KERNEL DE HAMMER =====
    LANDLOCK ABI = 7
    audit de denegaciones (>=7): SI
    =========================================

Kernel nuevo: b3:f2583d61… (el hash cambia, como se esperaba; el viejo
34755ff2… decía "# CONFIG_SECURITY_LANDLOCK is not set").

Con esto las 4 precondiciones de la Fase 1 están cerradas y harkaq corre en las
tres máquinas del proyecto: laptop (ABI 10), granja (ABI 7 tras el bump de la
golden) y el kernel propio de hammer (ABI 7).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 20:13:39 -04:00
sergioandClaude Opus 4.8 caa23f9ab7 harkaq: seccomp implementado (D4) — denylist, no allowlist; el hash no se mueve
D4 declaraba seccomp "obligatorio, no opcional" y harkaq-exec tenía CERO
seccomp: el documento afirmaba algo que el código no hacía. Cerrado.

CORRECCIÓN DELIBERADA A D4: pedía ALLOWLIST por syscall, se implementó DENYLIST.
Un allowlist para builds ARBITRARIOS (compiladores, make, shells, linkers, perl)
es un blanco móvil que cada herramienta nueva rompe — el riesgo del §7 ("los
falsos positivos matan proyectos de sandboxing") aplicado a las syscalls, y con
peor final: un build que muere por una syscall legítima no da un diagnóstico
útil, da un misterio. Lo PELIGROSO sí es enumerable y estable: no hay build
honesto que cargue módulos, haga kexec o attachee un ptrace.

Denegadas: io_uring_*, ptrace, process_vm_{readv,writev}, bpf, userfaultfd,
keyctl/add_key/request_key, {init,finit,delete}_module, kexec_*,
perf_event_open, mount/umount2, open_tree/move_mount/fs*, setns.
pivot_root NO: bwrap lo usa ANTES de llegar a harkaq-exec.

EPERM y no KILL: matar deja un cadáver sin explicación; EPERM deja al build
fallar donde corresponde y al log decir por qué. Se instala DESPUÉS de
no_new_privs y de Landlock, justo antes del exec, y se hereda por fork/exec como
el dominio (D5). Si el kernel lo rechaza NO se corre el build (D7: media jaula
creyéndose entera es peor que ninguna).

Chequeo de arquitectura antes del número de syscall: los nros son POR ARCH y sin
el check un binario i386 podría colar otra syscall con el mismo número — el error
clásico de los filtros seccomp a mano.

COMPROBADO: ptrace → EPERM bajo la jaula (control positivo), y un build REAL de
zlib sale Hermetico ×3 con el hash del artefacto IDÉNTICO al de antes de seccomp
(b3:adc5c251…). Añadir media jaula no movió un byte — como debe ser: esto recorta
superficie de escape, no cambia el build.

El --seccomp <fd> de bwrap quedó SIN USAR: harkaq-exec ya corre dentro y con
no_new_privs puesto, así que instala el filtro él mismo — una pieza menos de
plumbing y el filtro queda al lado de la política que lo justifica.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 19:31:58 -04:00
sergioandClaude Opus 4.8 13233ee4e9 harkaq: primer barrido en la granja — la deuda irreducible de la muestra es perl (§4.10)
harkaq-farm-setup.sh: provisiona un worker para barrer (idempotente; gatea por
ABI>=7 y aborta si no llega — con la golden vieja el barrido daría SinEvidencia
en TODO). Deriva el runtime base DEL ROOTFS DEL WORKER, no copia el del laptop:
es por-rootfs (§4.3) y los sonames dependen de la versión de Alpine.

Barrido: 24 recetas, 14 con veredicto. Primer número crudo: 4 irreducibles (29%).
ERA FALSO, y por dos motivos propios:

1. GAP DE POLÍTICA. Los `list` salían sólo de los ancestros de la clausura ⇒ una
   receta SIN deps (bash) no generaba ni uno y todo escaneo del árbol caía como
   denegación (/usr/lib). Listar NO es leer: Landlock separa READ_DIR de
   READ_FILE ⇒ la estructura del árbol es CONTRATO. Se puede `ls /usr/lib` sin
   leer un solo fichero no declarado; la deuda es leer lo ajeno, no saber que
   existe. Idem /var/tmp: con --tmp-overlay / es scratch descartable, como /tmp.
2. La heurística del catálogo es por NOMBRE y un fichero no se llama como su
   paquete: /usr/bin/ranlib lo trae `binutils`, /usr/bin/diff lo trae
   `diffutils`. El único que sabe la verdad es el STORE (conoce la lista de
   ficheros de cada artefacto) — y el worker tiene 103 artefactos contra los
   cientos del hub.

⇒ EL WORKER MIDE, EL HUB CLASIFICA. Misma separación que lector/clasificador: el
que tiene el privilegio —o los datos— hace lo mínimo. Reclasificado en el hub:
    bash             ranlib,/usr/lib,/var/tmp → nada (gap de política)
    doas             /usr/bin/diff            → nada (diffutils lo provee)
    ca-certificates  /usr/bin/perl            → /usr/bin/perl
    curl             /usr/bin/perl            → /usr/bin/perl

RESULTADO: la deuda irreducible de toda la muestra es UN path — /usr/bin/perl
(2 de 14 = 14%). No hay recipes/perl.toml ni artefacto: deuda genuina y nombrada.
Todo lo demás era declarable (make ×10, ranlib→binutils, diff→diffutils).

Sigue por encima del <5% del §7, pero el perfil es el que importa: NO hay cola
larga de sorpresas. La deuda de un catálogo entero se resume en "declarar make" y
"no tenemos perl".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 17:50:34 -04:00
sergioandClaude Opus 4.8 cba59e829b harkaq: la granja VPS ya puede ser el compilador continuo — golden bumpeada a 6.17 (ABI 7)
El VPS va a ser el compilador permanente ⇒ harkaq TIENE que correr ahí o el
frente queda como decoración de laptop. Verificado de punta a punta en un worker
efímero desde la golden, no en el papel:

  antes:   kernel 6.8.0-134  →  Landlock ABI 4  →  sin audit  →  SinEvidencia siempre
  después: kernel 6.17.0-40  →  Landlock ABI 7  →  AUDIT ✓

`apt install linux-image-generic-hwe-24.04` (archivo estándar de Ubuntu 24.04, sin
PPA ni cambiar distro), reboot, y la cadena COMPLETA de evidencia corre igual que
en el laptop:
    same-exec 2 registros · new-exec 0 (el ciego) · new-exec-logon 4
    domain=14e9f83c2 blockers=fs.read_file path="/etc/passwd" dev="sda1" ino=133259
El store del catálogo (103 artefactos) sobrevivió el bump intacto.

NUEVA GOLDEN: snapshot 408909310 "hammer-golden-harkaq-6.17-2026-07-15".
farm-up.sh pasa a usarla por defecto. La vieja (405120842) queda como fallback.

+ harkaq-uapi.h — EL HALLAZGO QUE IMPORTA para un compilador continuo: **el kernel
y los headers envejecen por separado**. El worker corre 6.17 pero su
linux-libc-dev es 6.8 y NO define NADA de lo necesario: ni los flags de log de ABI
7/8, ni AUDIT_LANDLOCK_ACCESS/DOMAIN (¡los tipos de registro!), ni IOCTL_DEV de
ABI 5. El kernel puede; el compilador no sabe pedírselo.

Es el mismo error de §3.1 al revés: allá, deducir el ABI de la versión del kernel;
acá, de la versión de los headers. NINGUNA de las dos dice la verdad — la única
fuente es el syscall en runtime. Estas constantes son números de contrato de UAPI,
estables por definición, seguros de fijar con #ifndef.

Sin esto harkaq sólo compila en distros con headers al día, que es justo lo que un
compilador continuo NO puede exigir. Y el modo de falla habría sido el peor: sin
AUDIT_LANDLOCK_ACCESS el lector filtraría por un número que no conoce y vería CERO
denegaciones — el falso `Hermetico` de D9, esta vez por headers viejos.

Nota: ABI 7 da el audit (lo que el proyecto necesita); TSYNC (ABI 8) pide 7.0 y no
está — es robustez opcional de D5, no un bloqueo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 17:33:14 -04:00
sergioandClaude Opus 4.8 993060630d harkaq: el bucle CERRADO — de Impuro a Hermetico sobre una receta real (§4.7)
Diagnosticar no vale nada si no se puede accionar. El bucle completo sobre
recipes/zlib.toml, cada paso guiado SÓLO por lo que dijo harkaq:

  zlib tal cual → Impuro: /usr/bin/make         → "declarar dep: make"
  + make        → Impuro: /usr/bin/ranlib       → "declarar dep: binutils"
  + binutils    → Impuro: liblto_plugin.so      → irreducible ⇒ clasificar
  final         → Hermetico ×3 fases, artefacto b3:adc5c251… SELLADO

Cada capa que se pela destapa la siguiente, y TODAS convergen en el gcc de
Alpine. El último hallazgo es el que no se encuentra a mano: los binutils DE
HAMMER —ya declarados como dep— cargan el plugin LTO del gcc DE ALPINE
(/usr/libexec/gcc/x86_64-alpine-linux-musl/15.2.0/liblto_plugin.so). No es un
fallo de declaración de la receta: es un agujero de soberanía DENTRO de un
artefacto que hammer construye.

Va a denegación esperada por la misma razón que /usr/bin/gcc (§4.2): el build
completa sin él ⇒ es una SONDA, no una necesidad, y bloquearla es DESEABLE —
impide que los binutils de hammer usen el plugin de Alpine por detrás. D3 otra
vez: no se calla, se clasifica.

Detalle que confirma el modelo: el hash del artefacto es IDÉNTICO antes y
después de clasificar la sonda (b3:adc5c251… las dos veces). Clasificar cambia
el VEREDICTO, no el BUILD. Es el principio de H1 sosteniéndose solo: la
evidencia es comportamiento, no identidad (recipe.rs:228, y por eso Evidence
está fuera de hash_inputs).

Y eso es lo que `Hermetico` significa ahora, con todo el peso: el kernel
certificó que ese build no usó NADA fuera de su clausura declarada, y el canario
prueba que el certificado no es el silencio de un lector roto.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 16:07:50 -04:00
sergioandClaude Opus 4.8 d0c9cdfa33 harkaq: deuda declarable vs irreducible (§4.5) + primer barrido de Fase 2 (§4.6)
La métrica del §7 (<5% de recetas necesitan excepción) medía otra cosa. zlib
salió Impuro por /usr/bin/make y parecía runtime base: NO lo es. recipes/make.toml
existe, store/fbad44ac…-make está sellado y SWAP_MAKE es uno de los swaps del
selfhost-verify ⇒ a zlib le falta una LÍNEA en [deps], no una excepción.

  declarable   el store ya provee el path ⇒ arreglo mecánico    → NO cuenta
  irreducible  nadie lo provee ⇒ receta nueva o runtime base    → SÍ cuenta

harkaq-suggest.py cruza cada path de deuda contra el store (el mapeo de §4.1 al
revés: el path relativo dentro del artefacto ES el path del sandbox):
    /usr/bin/make          → declarar dep: make
    /usr/lib/libz.so.1.3.2 → irreducible (la zlib de hammer da libz.a estático,
                             no .so ⇒ NO se arregla declarando)
El diagnóstico deja de ser "algo pasó" y pasa a ser "hacé esto".

PRIMER BARRIDO (6 recetas C, fuentes cacheadas):
  Hermetico ............... 0
  sólo deuda DECLARABLE ... 5  ← y la deuda es EL MISMO path en las 5: /usr/bin/make
  con deuda IRREDUCIBLE ... 1  (brotli)

Lo que importa: la deuda de 5/6 es UN SOLO path y se arregla con una línea. No
hay cola larga de excepciones — que era el riesgo de muerte del §7.

Y el caso irreducible es la frontera CONOCIDA: brotli (C++) pide
/usr/lib/libstdc++.so.6.0.34 y /usr/lib/libgcc_s.so.1 — el runtime C++ del gcc de
Alpine, que hammer no construye. Es exactamente lo que la campaña "matar gcc" ya
tenía identificado. TERCERA vez que harkaq llega por su cuenta a una lista que
otro frente ya tenía: los swaps del selfhost-verify (§4.3), los binutils (§4.4)
y ahora el runtime C++.

Honestidad: 6 recetas no son 750 y son las fáciles. 1/6 = 17%, muy por encima del
<5% — pero el único caso es un problema conocido, nombrado y compartido con otros
dos frentes, no una cola de sorpresas. El barrido grande es trabajo de granja.

Bug del barrido encontrado por el propio barrido: copiar la receta a otro
directorio rompe la resolución de deps.build (son relativas al dir de la receta)
⇒ brotli fallaba con "no pude cargar la dep 'cmake'" y se descartaba EN SILENCIO
— o sea que se descartaban justo las recetas CON deps, las interesantes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 16:02:27 -04:00
sergioandClaude Opus 4.8 ed614e6898 harkaq: el runtime base DERIVADO (§4.3) + el diagnóstico sobre un configure real (§4.4)
harkaq-base-closure.py: deriva el cierre dinámico del runtime base. Es D1
aplicado a la base — mantenerla a mano sería el "alguien mantiene un perfil de
permisos" que D1 dice que mata a todos los sandboxes. No usa ldd (resolvería
contra el HOST, no contra el rootfs Alpine): lee los DT_NEEDED del ELF y
resuelve dentro del rootfs por las rutas de musl. Emite symlink Y destino: el
kernel denuncia el fichero real (libz.so.1.3.2) pero el build abre por el nombre
corto (libz.so.1).

VALIDACIÓN: el cierre derivado de {sh,busybox,bash,coreutils,env} reproduce
EXACTAMENTE las 7 librerías que las rondas 2-3 habían descubierto a mano, en una
sola pasada, y además caza los symlinks y libc.musl-x86_64.so.1 que se habían
escapado. El método es computable, no adivinado.

§4.4 — con el entorno del Sandbox real replicado (CC="zig cc -mcpu=baseline",
AR, SOURCE_DATE_EPOCH, LC_ALL=C), configure llega mucho más lejos: 205
denegaciones del kernel, y clasificadas:

  esperadas (170): /usr/bin/gcc, /usr/bin/ldd        ← sondas de compilador
  DEUDA (34) en 8 paths:
      /usr/bin/{ld,nm,objdump,strip}                 ← binutils de ALPINE
      /usr/bin/{make,getconf}
      /usr/lib/gcc/x86_64-alpine-linux-musl          ← libdir del gcc de ALPINE
      /opt                                           ← bug de política

Es la tesis del §0 hecha dato: un configure que se creía hermético va a buscar
los binutils y el libdir de gcc de Alpine. Y la clasificación es lo que lo hace
legible — sin ella son 205 denegaciones planas y el hallazgo queda enterrado;
con ella son 8 paths accionables. Coincide con los swaps del selfhost-verify y
con la campaña "matar gcc" (recipes/binutils.toml existe justo porque zig provee
as/ld/ar pero el resto se toma de Alpine).

Bug de política encontrado por el propio experimento: /opt sale como deuda
porque la política concede `ro /opt/zig` pero no deja LISTAR el padre. Regla
general: todo directorio concedido necesita `list` en sus ancestros, o el escaneo
del padre es un falso positivo. harkaq-policy.sh ya lo hace para la clausura de
las deps; falta para las superficies de contrato.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 15:46:07 -04:00
sergioandClaude Opus 4.8 77317a7291 harkaq: Verdict clasificado (base|esperada|deuda) + el runtime base es por forma de build
harkaq-verdict.py: clasifica las denegaciones crudas. Va DELIBERADAMENTE fuera
del lector — éste corre con CAP_AUDIT_READ y clasificar es POLÍTICA, no
privilegio (mismo argumento que Q1c). De yapa: se itera sin recompilar el
binario capabilitado y sin perder el setcap. Las expectativas viajan en la
propia política como `# expect <path>` (una sola fuente de verdad; harkaq-exec
las ignora como comentario). SinEvidencia manda sobre todo: si el lector no es
confiable no se clasifica nada — reinterpretarlo sería el falso Hermetico que el
canario existe para impedir.

  MODE=base  rc=0  Hermetico  esperadas: /usr/bin/gcc  deuda: ninguna ✓
  MODE=zlib  rc=1  Impuro     DEUDA: /usr/lib/libz.so.1.3.2

§4.3 — ¿aguantan los 5 paths en otra forma de build? Medido con un configure de
autotools REAL (libgpg-error, MODE=configure):

  ronda 1: /bin/bash, /bin/coreutils
  ronda 2: libacl, libattr, libcrypto, libreadline, libutmps
  ronda 3: libncursesw, libskarnet
  ronda 4: /usr/bin/c89, /usr/bin/c99, /usr/bin/ldd, /usr/bin/make

NO: el runtime base es POR FORMA DE BUILD (~16 entradas para autotools, no 5).
Pero converge en 3 rondas (34 denegaciones → 5) y se queda chico ⇒ la métrica de
<5% de la Fase 2 sigue siendo plausible.

Hallazgo: **el runtime base no es una lista de binarios, es su CIERRE de .so**
(bash arrastra libreadline+libncursesw; coreutils arrastra libacl+libattr). Es
computable con ldd, no adivinable ⇒ harkaq-policy debe derivarlo igual que
deriva la clausura de las deps. Misma idea, otro origen.

Y el residuo es todo señal, nada ruido: c89/c99/ldd son sondas de compilador de
Alpine (→ esperadas, como gcc) y /usr/bin/make es una decisión de diseño real —
hoy los builds de hammer usan el make de Alpine sin declararlo. Es EXACTAMENTE
lo que los swaps del selfhost-verify reemplazan uno a uno: la lista de deuda de
harkaq y la lista de swaps del bootstrap son la MISMA lista, descubierta por dos
caminos independientes. Que coincidan es la mejor validación externa del método.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 15:35:42 -04:00
sergioandClaude Opus 4.8 61171bfe7f harkaq: Q2 RESUELTA — el runtime base son 5 paths, y aparece una 3ª categoría
Método (lo importante): NO se adivina, se mide. MODE=base corre SIN ninguna dep
declarada ⇒ sin clausura que pueda explicarlas, toda denegación es runtime base
por definición. Aísla la respuesta sin juicio de valor.

MODE=base, hello.c mínimo con zig cc: 18 denegaciones, 4 paths distintos
(contador del kernel 19 = 18 + canario ✓):
    8× /dev/urandom   6× /usr/lib/os-release   3× /dev/null   1× /usr/bin/env
Y el build salió rc=0: no son cosas que el build NECESITE, son cosas que INTENTA
— pero salen en todos los builds y sin tratarlas entierran la deuda real.

La partición cierra en TRES categorías, todas medidas (§4.2):
  - Contrato del sandbox: /src /out /tmp /dev/null(rw) /dev/urandom /proc
    /opt/zig — los monta BWRAP, no son Alpine ⇒ no son deuda. Ésta era la línea
    divisoria que faltaba.
  - Runtime base Alpine: /bin/sh /bin/busybox /lib/ld-musl /usr/bin/env
    /usr/lib/os-release. CINCO. ⇒ el resto de Alpine es deuda genuina, y eso
    hace viable todo el planteo.
  - Denegación ESPERADA (no estaba en el diseño): /usr/bin/gcc. zig cc lo sondea
    3× y el build sale rc=0 igual ⇒ NO es ruido a permitir, es la jaula haciendo
    su trabajo: concederla dejaría a zig invocar el gcc de Alpine por detrás,
    justo lo que la campaña "matar gcc" persigue a mano. Es D3 literal ("una
    denegación esperada no se calla, se CLASIFICA") y confirma que no tener
    `quiet` era correcto: silenciarla habría borrado el hallazgo.

El contraste con el mismo runtime base:
  MODE=base  rc=0  Impuro [ /usr/bin/gcc ]            ← esperada
  MODE=zlib  rc=1  Impuro [ /usr/lib/libz.so.1.3.2 ]  ← DEUDA PURA, sin ruido

Consecuencia de diseño para el Verdict: las denegaciones tienen que venir
CLASIFICADAS (base|esperada|deuda), no en lista plana. `Hermetico` debe
significar "cero deuda", no "cero denegaciones" — si no, ningún build real lo
alcanzaría jamás.

Gotchas medidos: /dev/null es destino de ESCRITURA (rw, no ro) y el piso duro es
/bin/sh→busybox→ld-musl (sin eso ni el canario corre ⇒ sólo SinEvidencia).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 15:28:47 -04:00
sergioandClaude Opus 4.8 24c6ab455e harkaq: §4 corregido (política POR FICHERO) + Q2 con método y primeros datos reales
CORRECCIÓN ESTRUCTURAL del §4 (otra herencia del marco nix). El Draft 2 decía
`fs_ro: [store paths de la clausura]`. Falso: esos paths NO existen dentro del
sandbox. `Sandbox::bwrap_args` apila las deps con --overlay-src y las FUNDE en
el mismo /usr que el rootfs Alpine, a propósito, para que el compilador las
encuentre sin plumbing de flags. Medido:

  /usr/include/zlib.h   (dep DECLARADA)        dev=63 ino=13641973
  /usr/include/stdio.h  (Alpine, NO declarado) dev=63 ino=4196788
  /usr/include          (el directorio)        dev=61 ino=15  ← overlayfs, uno

Una regla sobre /usr concede las dos ⇒ la evidencia no valdría nada. Ni `dev`
distingue. ⇒ La clausura se enumera FICHERO A FICHERO (computable: en el host
sabemos qué aporta cada dep). Dos consecuencias: `list` ≠ `ro` (pkgconf NECESITA
escanear /usr/lib/pkgconfig, pero listar no es leer; Landlock separa READ_DIR de
READ_FILE) y máscara por tipo (derechos de-sólo-dir sobre un fichero ⇒ EINVAL y
la regla entera se cae).

Piezas nuevas: harkaq-policy.sh (deriva la clausura, D1) + q2-runtime-base.sh
(responde Q2 midiendo, no adivinando) + harkaq-exec con `list`/máscara por tipo.

Q2, primeros datos con un build REAL en el sandbox REAL contra la dep zlib:
  estado: Impuro | canario: True | contador kernel: 3
      fs.read_file /usr/bin/env
      fs.read_file /usr/lib/libz.so.1.3.2   ← la zlib de ALPINE!

El build se linkaba contra la zlib de Alpine en vez de la dep declarada (que
aporta libz.a). Sin harkaq eso pasa en verde y el artefacto queda dependiendo de
Alpine. ES EXACTAMENTE EL BUG DEL §0, cazado en un build real.
Piso duro del runtime base: /bin/sh → /bin/busybox → /lib/ld-musl (sin eso ni el
canario corre ⇒ el mínimo es "lo que hace falta para que el canario pueda correr").

Dos rastrillos: el canario no puede depender de un binario (`cat` moría en
/bin/cat ⇒ ahora `read _ < $CANARY`, sólo builtins) ni vivir en /tmp (la política
concede `rw /tmp` ⇒ el canario caía DENTRO de la clausura y no denegaba nada).
El segundo lo cazó D9 MISMO: el lector se negó a certificar en vez de decir
Hermetico. El canario funcionando como se diseñó, sobre un bug de quien lo diseñó.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 15:20:39 -04:00
sergioandClaude Opus 4.8 4eecba06f4 harkaq: Fase 1 — la cadena corre de punta a punta (Hermetico / Impuro sobre kernel real)
Tres piezas nuevas, las del §4 del SDD:
  - harkaq-audit.c  lector de evidencia; HOST junto a hammerd, CAP_AUDIT_READ;
                    espera el canario (que revela el domain=), junta las
                    denegaciones de ESE dominio, cruza contra el contador del
                    kernel al liberarse, y emite el Verdict JSON de 3 estados.
  - harkaq-exec.c   aplica la política y execea el builder; DENTRO de bwrap,
                    estático (rootfs musl). Negocia el ABI por syscall (D7:
                    <min rechaza, <7 avisa SIN EVIDENCIA) y pone
                    LOG_NEW_EXEC_ON + TSYNC.
  - harkaq-run.sh   encadena lector+bwrap+exec, pone el canario con nonce y
                    espera a que el lector confirme que escucha (--ready-fd)
                    antes de arrancar el build: sin esa barrera el canario se
                    emitiría sin nadie escuchando y daría SinEvidencia por una
                    carrera, no por un problema real.

El hito, con comandos sintéticos sobre el kernel real (ABI 10):
  limpio  {"estado":"Hermetico","canario_visto":true,"contador_kernel":1,"denials":[]}
  impuro  {"estado":"Impuro","canario_visto":true,"contador_kernel":2,
           "denials":[{"blockers":"fs.read_file","path":"…/README.md",…}]}
Canario visto y contador coherente en ambos ⇒ los 3 estados de D9 funcionan.

Q1c contestada en la práctica: el privilegio vive en un helper chico con
`setcap cap_audit_read,cap_audit_control`, NO en hammerd entero.

Sutileza medida: lo que DAC ya bloquea es INVISIBLE para harkaq (el hook del
LSM no llega a correr). Descubierto porque el escenario "impuro" con
/root/.bashrc (drwx------) dio Hermetico: DAC lo bloqueó antes que Landlock.
No rompe D2 (si DAC lo bloqueó, el build tampoco lo usó ⇒ Hermetico es cierto)
pero acota el diagnóstico, y al escribir tests hay que elegir paths que DAC
permita o el control no controla nada.

Falta para cerrar Fase 1: harkaq-policy derivando de la clausura real + el
contraste receta-de-Alpinizada vs. receta-que-no. El rebuild del kernel
(SECURITY_LANDLOCK) NO bloquea el hito: sólo hace falta para que harkaq corra
DENTRO de hammer; el laptop ya está en ABI 10.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 15:04:27 -04:00
sergioandClaude Opus 4.8 6419f2a952 harkaq: Q1b CERRADA — la evidencia cruza el userns y el canario es la clave primaria
Banco: scripts/harkaq/q1b-attrib.c. Dos builds CONCURRENTES en bwrap
--unshare-all (los ns de Sandbox::bwrap_args), sin privilegio (uid 1000),
canarios con nonce distinto, lector en el host como root.

(a)  Los registros CRUZAN el userns/pidns/mountns (6 registros llegaron al
    lector del host) y cruzan con builds SIN privilegio, que es como corren de
    verdad. ⇒ harkaq-audit vive en el host junto a hammerd; la arquitectura
    del §4 se sostiene.
(b)  El canario con nonce ATRIBUYE: AAAA=1 BBBB=1, cero cruces con 2 builds
    en paralelo. ⇒ la granja puede construir en paralelo sin contaminarse la
    evidencia entre builds.

Tres hallazgos colaterales que valen más que el veredicto:
  - El contador quedó VALIDADO: dos `deallocated denials=1`, uno por dominio,
    cuadrando con 1 ACCESS cada uno ⇒ el chequeo anti-pérdida (nº ACCESS ==
    denials) está medido, no supuesto.
  - La identidad sobrevive a los ns: pid=8498 uid=1000 son del HOST (adentro
    del pidns la víctima es pid 1).
  - dev+ino identifican el OBJETO, no el BUILD: los 2 canarios dan el mismo
    ino=4457713 (ambos bindean /etc/hostname). Atribuir por dev/ino habría
    fundido dos builds leyendo el mismo fichero no declarado — el caso más
    común que harkaq va a ver. La clave es domain=, y el canario la revela.

⇒ D9 hace TRES trabajos con un mecanismo: honestidad (§3.4), clave primaria
(§3.5) y, vía el contador, detección de pérdida.

Nota de método: esta corrida también falló 2 veces más, y las 2 el banco
afirmó algo falso con total confianza. (1) `cat $canario >/dev/null` — /dev no
estaba en la clausura ⇒ sh fallaba en el REDIRECT y cat no corría; el rc=1 no
era del canario. (2) Como root, bwrap no pudo ni ejecutar el binario y el
veredicto imprimió "NADA cruzó ⇒ replantear la arquitectura" a partir de cero
estímulo (causa: --unshare-all mapea sólo uid 0→0; CAP_DAC_OVERRIDE en userns
sólo vale sobre uids MAPEADOS ⇒ root-en-userns no atraviesa un home
drwx------). Fix: drop_privs() + gate de estímulo (exit 4 si una víctima no
sale con status 0).

Van 3 veces en una sesión que un cero fue el instrumento y no el kernel, con
quien escribía el banco mirando de frente ese riesgo. harkaq sin canario no es
un riesgo de configuración: es el comportamiento por defecto.

Quedan Q1c (privilegio de harkaq-audit: mejor un helper acotado que hammerd
entero) y Q1d (qué ruta reporta exe= con --bind /src).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 14:42:12 -04:00
sergioandClaude Opus 4.8 ad0948cfb0 harkaq: Q1 CERRADA — la evidencia llega; + D9 (canario deliberado) y el banco de pruebas
Q1 era el gate del proyecto (¿llegan los registros AUDIT_LANDLOCK_* a un
lector?). Medido en el laptop (ABI 10) con scripts/harkaq/q1-audit.c:

   VIABLE. El multicast AUDIT_NLGRP_READLOG entrega
     blockers=fs.read_file path="/etc/passwd" dev="nvme1n1p6" ino=4458369
     + exe=/comm= del que lo intentó. Es el diagnóstico de de-Alpinización
     que el SDD promete, saliendo del kernel sin instrumentar nada.

Dos precondiciones que el Draft 2 no tenía, ambas medidas:
  - audit_enabled=1 NO viene de fábrica (sin audit=1 en cmdline, sin auditd)
    ⇒ cero registros de cualquier escenario. AUDIT_SET o audit=1 horneado.
  - LOG_NEW_EXEC_ON es OBLIGATORIO: same-exec=2 registros, new-exec=0,
    new-exec-logon=4. harkaq restringe y DESPUÉS execea el builder ⇒ sin el
    flag certificaría herméticos TODOS los builds, denials=[], sin un error.

D9 (nueva, no negociable): el canario lo fabrica harkaq. Se investigó si el
registro `deallocated denials=N` servía de verificación cruzada gratis: NO.
Medido con ventana de 6s, un dominio con flags default y denegación post-exec
no emite NADA (ni allocated, ni access, ni el contador); y el `allocated` es
perezoso (llega pegado al primer denial logueado) ⇒ su ausencia tampoco prueba
nada. Un build hermético y un lector ciego son bit-idénticos: denials=[]. El
Verdict pasa a 3 estados (Hermetico / Impuro / SinEvidencia).
El contador SÍ sirve para lo otro: nº ACCESS == denials caza registros
perdidos por backlog (riesgo real con la granja en paralelo).

Nota de método (§3.4): la 1ª corrida dio 0 en los 3 escenarios y "confirmaba"
la hipótesis. Era el instrumento roto (AUDIT_GET con NLM_F_ACK: el ACK llegaba
antes que la respuesta → enabled=-1 → nunca se encendía el audit). Se cazó
sólo porque el CONTROL (same-exec) también dio 0, y eso era imposible. Es el
modo de falla de harkaq en vivo sobre su propio banco. El test ahora aborta
(exit 4) si no confirma audit_enabled=1.

Nuevas Q1b (¿el lector ve a través del userns de bwrap? ¿cómo se atribuye un
registro a SU build con la granja en paralelo?) y Q1c (privilegio de
harkaq-audit).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 13:23:19 -04:00
sergioandClaude Opus 4.8 e263b66ec1 mirada/USB: telemetría persistente en el pendrive + bump a mirada de hoy (fix musl en tawasuyu)
TELEMETRÍA (el pedido): el rootfs de la imagen es un initramfs = RAM pura y los logs iban a /run (tmpfs)
⇒ al apagar la máquina ajena no sobrevivía NADA. Ahora la imagen lleva 2da partición FAT32 MIRADALOG
(512M), montada por findfs+LABEL con -o sync (un cuelgue de GPU o corte de luz no se lleva los logs).
Cada arranque vuelca run-NNNN/ con: resumen (modo/GPU/driver), hardware (PCI vía sysfs, módulos, DRM,
input, firmware), pantallas (status/modes por conector), dmesg ANTES y DESPUÉS de mirada, seatd, y el log
del compositor+greeter con RUST_LOG=wgpu_core=debug + EGL_LOG_LEVEL=debug + LIBGL_DEBUG=verbose (para ver
por qué wgpu eligió el backend y si hay float16). Shell de rescate + . FAT32 a propósito: el
pendrive se lee después desde cualquier SO.

metal-usb-sdboot.sh gana DATA_MB/DATA_LABEL genéricos (partición de datos opcional en la GPT).

GOTCHAs verificados antes de confiar: el kernel linux-generic SÍ trae VFAT_FS=y + NLS + USB_STORAGE=y
(sin eso no montaba); el busybox de Alpine NO trae el applet 00:00.0 Host bridge: Intel Corporation Tiger Lake-UP3/H35 4 cores Host Bridge/DRAM Registers (rev 01)
00:02.0 VGA compatible controller: Intel Corporation TigerLake-LP GT2 [Iris Xe Graphics] (rev 01)
00:04.0 Signal processing controller: Intel Corporation TigerLake-LP Dynamic Tuning Processor Participant (rev 01)
00:07.0 PCI bridge: Intel Corporation Tiger Lake-LP Thunderbolt 4 PCI Express Root Port #0 (rev 01)
00:08.0 System peripheral: Intel Corporation GNA Scoring Accelerator module (rev 01)
00:0a.0 Signal processing controller: Intel Corporation Tigerlake Telemetry Aggregator Driver (rev 01)
00:0d.0 USB controller: Intel Corporation Tiger Lake-LP Thunderbolt 4 USB Controller (rev 01)
00:0d.2 USB controller: Intel Corporation Tiger Lake-LP Thunderbolt 4 NHI #0 (rev 01)
00:12.0 Serial controller: Intel Corporation 500 Series Chipset Family On-Package Integrated Sensor Hub (rev 20)
00:14.0 USB controller: Intel Corporation 500 Series Chipset Family On-Package USB 3.2 Gen 2x1 (10 Gbs) xHCI Host Controller (rev 20)
00:14.2 RAM memory: Intel Corporation 500 Series Chipset Family On-Package Shared SRAM (rev 20)
00:14.3 Network controller: Intel Corporation Wi-Fi 6 AX201 (rev 20)
00:15.0 Serial bus controller: Intel Corporation 500 Series Chipset Family On-Package I2C Controller #0 (rev 20)
00:15.1 Serial bus controller: Intel Corporation 500 Series Chipset Family On-Package I2C Controller #1 (rev 20)
00:16.0 Communication controller: Intel Corporation 500 Series Chipset Family On-Package CSME HECI #1 (rev 20)
00:1c.0 PCI bridge: Intel Corporation 500 Series Chipset Family On-Package PCI Express Root Port #5 (rev 20)
00:1d.0 PCI bridge: Intel Corporation 500 Series Chipset Family On-Package PCI Express Root Port #9 (rev 20)
00:1f.0 ISA bridge: Intel Corporation 500 Series Chipset Family On-Package eSPI Controller (rev 20)
00:1f.3 Multimedia audio controller: Intel Corporation 500 Series Chipset Family On-Package High Definition Audio (HD Audio) (rev 20)
00:1f.4 SMBus: Intel Corporation 500 Series Chipset Family On-Package System Management Bus (SMBus) (rev 20)
00:1f.5 Serial bus controller: Intel Corporation 500 Series Chipset Family On-Package SPI (flash) Controller (rev 20)
39:00.0 Non-Volatile memory controller: Kingston Technology Company, Inc. NV2 NVMe SSD [SM2267XT] (DRAM-less) (rev 03)
3a:00.0 Non-Volatile memory controller: ADATA Technology Co., Ltd. IM2P33F3 NVMe SSD (DRAM-less) (rev 03) (sí findfs/blkid) ⇒ el dump de PCI
lee /sys/bus/pci/devices/*/ directo; sintaxis del init validada con busybox ash (no bash).

BUMP mirada: el pin era 9967b02c (18-jun) — 439 commits viejo. Ahora 9a17fefc, que incluye el fix de
portabilidad a musl que hice en tawasuyu (ioctl: glibc usa c_ulong, musl c_int ⇒ las constantes DRM
casteaban mal y mirada-compositor NO compilaba para musl). mirada-compositor ya selló (87ced528);
greeter/ctl en curso.

+ scripts/kde/{metal-desktop-image,plasma-start-metal}.sh del hilo KDE (imagen de escritorio en metal).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 16:20:55 -04:00
sergio dcae94f411 kde/Plasma: 🎉 H-SHELL — plasmashell carga el escritorio + panel sobre kwin (headless, soberano)
run-plasma-headless.sh lanza kactivitymanagerd antes de plasmashell (plasmashell aborta sin él).
RESULTADO: kwin_wayland --virtual composita + plasmashell carga org.kde.plasma.desktop (Desktop.qml),
el panel puebla applets, kactivitymanagerd corre, actividades registran, xkb funciona. El escritorio
KDE Plasma 6 PRENDE headless por software desde el rootfs musl soberano. Applets restantes (polish):
kickoff/notifications piden org.kde.coreaddons QML (kcoreaddons KCOREADDONS_USE_QML, cascadea) +
kirigami-addons (no construido); pager podado; iconos breeze + fuentes dejavu pendientes.
2026-07-14 03:55:20 -04:00
sergioandClaude Opus 4.8 c813664497 kde/Plasma: LANDMARK — kwin+plasmashell corren headless end-to-end (software, rootfs soberano)
- Fix clave: bwrap con rootfs merged como / (el hack loader --library-path rompía el dlopen
  de plugins de kwin → QPA create() core-dump). Paths FHS nativos + LD_LIBRARY_PATH lib64.
- kwin_wayland --virtual LEVANTA el compositor (socket wayland-0, compositing software tras
  fallar gbm); plasmashell CONECTA y arranca (QtQuick software). Stack Plasma 6 soberano
  (musl/zig-cc) EJECUTA end-to-end SIN GPU real.
- Bloqueo a H-shell: falta el shell org.kde.plasma.desktop (paquete plasma-desktop, no
  construido) → 'invalid corona'. Próximo: construir plasma-desktop + kpipewire + baloo.
- run-plasma-headless.sh reescrito con la sesión bwrap validada (dbus+kwin+cliente).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 00:02:46 -04:00
sergioandClaude Opus 4.8 725adbd487 kde/Plasma: mesa-swrast (GL software) + lanzador Plasma headless + handoff nocturno
- run-plasma-headless.sh: lanzador de sesión Plasma headless (software EGL/softpipe sobre KMS).
- mesa-swrast construido (softpipe, sin LLVM, sello c59738eb): swrast_dri/kms_swrast_dri.
- HANDOFF-noche-kde: kwin_wayland linkea+carga QPA pero create() del compositor crashea headless
  (virtio-gpu/EGL-sw/seat en musl, opaco sin gdb). El rootfs es para GPU Intel real (iris) →
  camino recomendado = imagen booteable para metal (laptop TigerLake). Estado + próximos pasos.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 23:56:59 -04:00
sergioandClaude Opus 4.8 a7449a0b88 kde/Plasma: cierre runtime COMPLETO del rootfs + zlib soberano shared
- complete-closure.sh: escanea DT_NEEDED de todos los ELF del rootfs y proyecta del store
  sellado el artefacto que provee cada soname AUSENTE, iterando a punto fijo. Resuelve las
  deps runtime que el índice del repo NO lista (libmount.so.1/util-linux, libltdl, libzstd,
  libQt6UiTools). Sonames base (musl libc/loader) no se hidratan.
- zlib-shared.toml: variante DINÁMICA de zlib (el canónico es --static). El stack GUI NEEDED
  libz.so.1 por soname y ningún artefacto lo proveía → caía al libz.so.1 de Alpine (fuga de
  soberanía Capa 0). .so fabricado del libz.a PIC vía --whole-archive (soname libz.so.1).
- Resultado: rootfs kde-store-rootfs con 1269 .so, 0 sonames faltantes; kwin_wayland resuelve
  TODAS sus libs y carga su QPA (bloqueado sólo en render: worker sin GPU/llvmpipe).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 23:43:18 -04:00
sergioandClaude Opus 4.8 d6944b2cd9 kde/Plasma: hidratación del ESCRITORIO COMPLETO store-directa (rebuild-free), cierre runtime verificado
- hydrate-from-store.sh: proyecta artefactos SELLADOS del store al FHS vía hammer hydrate,
  sin reproduce (install-reproduce rebuildеa qtbase por consumidor — hashing frágil bajo
  régimen dinámico). Selección por-pkg = artefacto más reciente con mtime<hoy (cosecha final
  coherente de la granja). 137/137 proyectados, 0 faltantes, 1257 .so, 5.0G en kde-store-rootfs.
- Hashes elegidos = los sellados de la campaña (kwin 3a674cb6, plasma-workspace 284c862f,
  libplasma 963403fe, kscreenlocker 55120487, breeze df1d1e68, kio 44376420).
- Cierre runtime VERIFICADO completo: todos los NEEDED de plasmashell(30)/kwin_wayland(24)/
  startplasma-wayland(11)/krunner(19) + libs core resuelven en el rootfs — cero colgantes.
- hydrate-fhs.sh: soporte multi-target (TARGETS) + canon prefiere nombre real qt6-* (el alias
  fuerza cache-miss+rebuild) + más build-only excluidos.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 23:26:26 -04:00
sergioandClaude Opus 4.8 08510c0424 kde/Plasma: hito H-QML PASADO — hidratación FHS del cierre runtime + reproducer QML
- repo-selfcontain.py: work/repo-kde auto-contenido (alias qt-corto→qt6-* + cierre base
  desde dist/repo), re-firmado 884 pkgs, repo verify trusted.
- hydrate-fhs.sh: hidrata el cierre runtime instalando cada pkg del cierre en el mismo
  prefix (install --prefix sólo baja el pkg pedido); 32 pkgs → work/kde-rootfs, 455 .so
  (libQt6Core/Gui/Qml/Quick + QPA offscreen + mesa/xcb/wayland/openssl).
- h-qml-run.sh + h-qml-test.cpp: QQmlApplicationEngine carga QtQuick Window+Text, compila
  zig-cc/musl contra el FHS, corre offscreen rc=0 (engine instancia el árbol QML).
- ADR 0011: sección resultado H-QML + deudas (target_bin espurio en libs, cache-miss por
  re-resolución de deps → qtbase más completo con SSL, fuentes).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 22:22:54 -04:00
sergioandClaude Opus 4.8 fc8d68c66f kde/H-QML: GATE PASADO — reproducer QtQuick corre rc=0 offscreen
Capa 1 Qt6 completa y sellada: qtbase+qtshadertools+qtsvg+qtlanguageserver+
qtdeclarative(QtQuick full d23aba45)+ECM. h-qml-test.cpp (Window+Text via QtQuick)
instancia la escena y sale rc=0 con offscreen+software backend. El motor QML
—corazón de Plasma— probado end-to-end bajo el stack soberano dinámico musl/zig-cc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 18:28:39 -04:00
sergioandClaude Opus 4.8 af5a175e01 kde/H-Qt: reproducer de la app Qt de prueba (gate PASADO, rc=0 offscreen)
qtbase 6.11.1 SELLÓ (b3:7c7c747d…) — Core/Gui/Widgets/OpenGL + QPA wayland, todo
.so dinámico bajo musl+zig-cc. La app compila y linkea contra él; corrida en el
rootfs musl del worker con QT_QPA_PLATFORM=offscreen crea la ventana y el event
loop sale rc=0. Régimen dinámico del ADR 0011 probado end-to-end.
Hidratación runtime: mesa+libdrm del store + libxkbcommon.so.0 fabricada del .a
(PIC) — deuda Capa 0 (varios libs canónicos son static-only).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 16:06:04 -04:00
sergioandClaude Opus 4.8 071be00396 kde/H-Qt: receta qtbase de-Alpinizada (gate campaña KDE) + cola incoming-kde al worker
qt6-qtbase 6.11.1 desde Alpine community: conserva sus 3 parches de musl (LFS64/
DNS-resolver/symlinks), reescribe abuild→CMake/Ninja explícito. link=DINÁMICO
(política capa GUI, ADR 0011). Minimal para el gate: Wayland-only (xcb OFF),
icu/vulkan/cups/libproxy/libinput/accessibility OFF, double-conversion+libb2
bundled ⇒ compila reusando SOLO deps ya selladas, cero recetas nuevas.
sha256 pinneado (sha512 cotejado con Alpine). farm-worker-loop añade
recipes/incoming-kde a QUEUES.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 14:05:23 -04:00
sergio f1ae579ff4 producto E2E: hidratación no-clobber (clobber ciego de /usr/lib rompe el sshd musl)
Revierte el experimento de shadow-por-clobber: pisar /usr/lib con libs del base-system deja
arje-zero+hammerd arrancando pero sshd/login mudo (ABI musl del producto disturbado). No-clobber
preserva el producto entero y añade los binarios nuevos (probado VERDE). La sombra de applets busybox
(sed/ps/ip→full) es política del ensamblado del producto (mecanismo 'adelgazar busybox' de coreutils),
que re-linkea el NOMBRE sin tocar /usr/lib — follow-up de empaque documentado en el script.
2026-07-11 06:23:03 -04:00
sergio d9bd5220f0 producto: E2E del base-system integrado al producto, booteado in-VM (front c productionizado)
product-base-system-boot-test.sh: hidrata los 26 paquetes del base-system sobre el product-rootfs
sellado (cp -an, preserva /etc/passwd/shadow/ssh del producto), lo bootea en QEMU y verifica DENTRO
del guest por SSH que corren. VERDE: arje-zero PID1 + sshd + bash 5.3/git 2.54/gpg 2.4.9/useradd/
vim 9.2/119 CA certs. Nota: MEM=6144 (rootfs aumentado ~1.1G en tmpfs). Caveat conocido: ps/ip/sed
resuelven a busybox por precedencia de PATH (los binarios full están, a sus rutas) — mismo patrón
'adelgazar busybox' que el producto ya aplica a coreutils; refinamiento de empaque pendiente.
2026-07-11 06:16:30 -04:00
sergio 0a03e1c579 producto: base-system en el userland curado del producto (integración front c)
product-userland-from-repo.sh ahora hidrata el userland foundational C (bash/git/sudo/util-linux/
shadow/red/disco/TLS/gpg/…) además de la capa CLI Rust, todo desde el repo firmado con
--require-signed (verifica firma → reproduce del .swm → hidrata FHS). BASE_SYSTEM_PKGS + CLI_RUST_PKGS.
2026-07-11 00:43:26 -04:00
sergioandClaude Opus 4.8 9305bb16e9 instalador: TUI interactivo (disco/identidad/locale/tamaños) sobre el autoinstalador
hammer-install crece una cara humana sin tocar la mecánica EFI/BIOS validada:
- sin device + TTY ⇒ interactive_setup: detecta discos (/sys/block, marca el
  montado como posible medio live), el usuario elige + confirma escribiendo el
  nombre del disco, y recoge hostname / credencial de root (contraseña vía
  cryptpw o authorized_keys) / zona horaria (best-effort si hay tzdata) /
  teclado / tamaños de / y /store.
- apply_post_install_config escribe hostname+hosts, reemplaza el hash de root en
  /etc/shadow, instala authorized_keys, symlinkea localtime y deja install.conf;
  se llama tras la copia en AMBAS ramas (EFI y BIOS).
- con device ⇒ autoinstalador de siempre (iso/efi-install-test intactos); la
  config sale de env (HAMMER_HOSTNAME/ROOT_PW/ROOT_AUTHKEYS/TZ/KEYMAP).
- iso-image.sh: ISO instalador sin AUTO_INSTALL ahora recibe con la TUI en la
  consola (INSTALLER_INTERACTIVE, default on) y ofrece reiniciar/caer al live.

Verificado: scripts/install-tui-test.sh (4/4, dry-run con sysfs+rootfs falsos)
+ iso-install-test.sh VERDE end-to-end en QEMU (autoinstalador con la config
nueva bajo busybox real → disco bootea solo y sirve SSH).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 22:22:21 -04:00
sergioandClaude Opus 4.8 a5be781ef6 arranque-grafo: hammer boot menu — orquestador del menú + wiring en el init (ADR 0010)
hammer boot menu ata las tres piezas del contrato: emite el grafo → lanza el
compositor (mirada, --compositor configurable) → activa el nodo que el usuario
dejó en boot-select. Graceful: sin compositor (servidor headless) emite el grafo
y sigue el arranque. Refactor: activate_and_report compartido con boot activate;
--out/--select configurables (testeable sin /run/hammer root-only).

Wiring: iso-image INSTALLER=1 + install-image-efi bundlean el CLI hammer (static
musl) al sistema instalado (el producto trae arje-zero/hammerd pero no el CLI);
el wrapper /sbin/init lo invoca tras hammer-recover, salida al serial (no pinta
tty0 ⇒ respeta cero-parpadeo). Cierra la pieza #3 del handoff mirada (quién
lanza el menú en el boot): lo ownea hammer, desde el hook de init.

Tests: boot_menu.rs (3 caminos del glue: graceful/sin-selección/selección→activate)
+ efi-disk-boot-test asevera que el menú corre. Validado en OVMF: pivote →
INIT-OK → menú (emite boot-graph.json + saltea sin mirada) → arje-zero.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 21:30:30 -04:00
sergioandClaude Opus 4.8 3ac329ec8f arranque-grafo: cero-parpadeo VERDE + fix marcador INIT-OK tras switch_root
efi-flicker-test valida por CONTRIBUCIÓN del kernel al framebuffer (2 capturas:
handoff-firmware vs post-boot; Δ≈0 ⇒ el kernel no tocó el FB). Con el kernel
metal cero-parpadeo: Δ=0.000 VERDE — el splash de la firmware sobrevive intacto,
sin texto de boot ni cursor (el kernel viejo saltaba de ~3 a ~11). No mide negro
absoluto: el splash de OVMF (TianoCore) daría falso-positivo y no existe en metal.

Fix: busybox switch_root NO mueve /dev ⇒ los wrappers /sbin/init montan devtmpfs
antes de tee'ar HAMMER-EFI-INIT-OK a /dev/ttyS0 (si no, el nodo no existe y el
marcador se perdía). efi-disk-boot-test VERDE con el kernel quiet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 21:04:05 -04:00
sergioandClaude Opus 4.8 fd033fb920 efi-flicker-test: media de brillo vía %[fx:mean] normalizado (0..1)
El 1er número de identify -verbose varía por quantum (10.99 en PPM vs 0.129 en
PNG para la misma imagen); el normalizado 0..1 es fiable. Validado: negro→0,
texto→11, umbral 1.5 los separa.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 20:21:48 -04:00
sergioandClaude Opus 4.8 e80f15b6bc arranque-grafo: harness EFI robustos a quiet + efi-flicker-test (ADR 0010 paso 4)
Con el kernel metal cero-parpadeo (quiet), los marcadores serie kernel-side
(EFI-stub/Linux-version/EXT4) desaparecen. Los /init de pivote + /sbin/init
real ahora tee'an marcadores a /dev/ttyS0 (escritura al puerto directo, sortea
el printk ⇒ sobreviven quiet): HAMMER-EFI-DISK-PIVOT-OK + HAMMER-EFI-INIT-OK.
efi-disk-boot-test y efi-install-test pasan a apoyarse en ésos (kernel-side =
informativos). Compatible con el kernel viejo (verbose) también.

efi-flicker-test.sh: valida cero-parpadeo por captura de framebuffer OVMF-GOP
(media de brillo < umbral ⇒ pantalla negra, sin texto de boot). El seamless
i915 queda para metal Intel real.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 20:13:45 -04:00
sergioandClaude Opus 4.8 6921dfead3 arranque-grafo: validación 2-stage del instalador live EFI en OVMF (ADR 0010 paso 5)
efi-install-test.sh cierra el lazo completo bajo UEFI (hermano de
iso-install-test.sh que es BIOS): (1) ISO live EFI=1 INSTALLER=1 AUTO_INSTALL
en OVMF con disco en blanco → hammer-install detecta /sys/firmware/efi y toma
la rama EFI (MBR+ESP-0xEF) desatendido → HAMMER-INSTALL-OK; (2) el disco
instalado bootea SOLO en OVMF → firmware → BOOTX64.EFI → pivote → arje-zero
PID1, sin GRUB. VERDE end-to-end.

Prueba el instalador live REAL corriendo dentro del live, no sólo sus
artefactos. ADR 0010: paso 5 cerrado salvo metal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 18:32:13 -04:00
sergioandClaude Opus 4.8 4e00178fc5 arranque-grafo: rama EFI-stub en el instalador live (ADR 0010 paso 5)
hammer-live-install.sh detecta /sys/firmware/efi y ramifica a EFI-stub soberano:
MBR con ESP tipo 0xEF (UEFI arranca una ESP MBR igual que GPT ⇒ sólo busybox
fdisk/mkfs.vfat/mount, sin GPT-tool ni mtools en el live). Arma el mismo
initramfs de pivote (findfs LABEL=hammer-root → switch_root) que el builder
host-side; /sbin/init = wrapper hammer-recover→arje-zero (el pivote ya montó
store/estado por LABEL). La rama BIOS+GRUB queda intacta tras la detección.

iso-image.sh INSTALLER=1 bundlea el kernel metal EFI-stub (bzImage-efi) en el
payload; el genérico no sirve (no hornea initrd=/rdinit= en la cmdline).

MBR+ESP-EF spot-checked en OVMF (firmware → BOOTX64.EFI → pivote → arje-zero).
ADR 0010 actualizado: pasos 2 y 5 .

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 18:11:06 -04:00
sergioandClaude Opus 4.8 11ff41d614 arranque-grafo: install-to-disk EFI-stub soberano (ADR 0010 paso 5)
install-image-efi.sh arma una imagen GPT+ESP donde el bzImage metal ES el
binario EFI (\EFI\BOOT\BOOTX64.EFI, ruta fallback removible) — sin GRUB ni
systemd-boot. La cmdline horneada del kernel (initrd=/initramfs.cpio.gz
rdinit=/init) se activa al no haber LoadOptions; un initramfs mínimo de pivote
resuelve hammer-root por LABEL (findfs) y hace switch_root a la ext4 real.
Layout: ESP + hammer-root/store/state ext4 (mke2fs -d bajo unshare -r, ESP con
mtools de hammer). Cierra el paso 2 (initrd chico destraba EFI-stub) y el 5.

efi-disk-boot-test.sh valida en OVMF (sin -kernel): VERDE — firmware →
BOOTX64.EFI → pivote → arje-zero PID1. Marcadores serie deterministas (los dos
primeros bytes del handoff firmware→kernel son informativos; el veredicto se
apoya en el montaje/re-mount aguas abajo, prueba concluyente de la cadena).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 18:03:47 -04:00
sergioandClaude Opus 4.8 cf2e8bf44d mirada-usb: modo hardware nouveau para GTX 1060 (Pascal/GP106)
- Hornea firmware nvidia/gp106 (ctxsw/acr/sec2) descomprimido a .bin (nouveau da GL 3D).
- init: modo HARDWARE por defecto (WGPU_BACKEND=gl sobre gallium-nouveau, que da float16
  vía unpackHalf2x16; tapa lavapipe). Pascal no tiene NVK ⇒ greeter va por GL, no Vulkan.
- cmdline default = hardware; 'mirada.sw nouveau.modeset=0' = fallback software.
- CHIP/FW_SRC parametrizables para otras GPUs.

Regresión: el fallback software sigue levantando el compositor en la imagen final.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 17:05:23 -04:00
sergioandClaude Opus 4.8 c82588ced8 mirada-usb: imagen USB de prueba de mirada en GPU ajena (NVIDIA)
- recipes/mesa-swrast.toml: mesa softpipe hammer-nativo (sin LLVM) para render software.
- scripts/mirada-usb.sh: ensambla rootfs (dev-fs musl + hydrate mirada/seatd/libinput/
  mesa + overlay Alpine llvmpipe/lavapipe/nouveau + XKB + PAM + init seatd+compositor) y
  empaqueta la USB con linux-generic vía systemd-boot.
- docs/mirada-usb-nvidia.md: uso + estado.

Validado en QEMU virtio-gpu: el COMPOSITOR arranca entero (libseat→card0→GBM+EGL+
GlesRenderer→[8/8]→escritorio, acepta clientes, dibuja la ventana del greeter). El
GREETER (Vello) exige shaders float16 que llvmpipe/lavapipe NO dan ⇒ su login sólo
pinta con GPU real (nouveau+firmware, v2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 14:20:46 -04:00
sergioandClaude Opus 4.8 d9d891a64a granja: tanda VPS efímero 2026-07-06 — 7 promovidos al catálogo firmado (714)
dalfox/templ/errcheck/ineffassign/unconvert (Go) + dprint (Rust) + kyverno (Go),
construidos en un worker efímero hcloud, cosechados y firmados. Binarios musl-static
verificados: dalfox 3.1.2, templ v0.3.1020, dprint 0.55.1, ineffassign, y los
analizadores Go corren. feroxbuster/jless/mdcat/mise quedan staged (frontera *-sys:
openssl-sys/xcb desde fuente).

Fix farm-down: el promote final ahora recorre TODAS las colas del worker
(incoming/incoming-go/incoming-clib), no sólo recipes/incoming — los artefactos de
las otras colas se cosechaban pero no se firmaban.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 07:31:05 -04:00
sergioandClaude Opus 4.8 e2e5bc6f04 granja: fix host-key en workers efímeros — known_hosts a /dev/null
Los workers efímeros reciclan IPs de Hetzner. accept-new RECHAZA una clave de host
cambiada ⇒ el 'until ssh true' de farm-up colgaba para siempre en una IP reusada.
Como el modelo es hub-and-spoke (worker sin secretos, compute descartable) no hay
superficie MITM: saltamos verificación de host y no ensuciamos known_hosts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 06:56:25 -04:00
sergioandClaude Opus 4.8 b90855dc30 granja: farm-run.sh — el ciclo efímero desatendido (up→esperar-ciclo→cosecha+destruye)
Encadena farm-up + farm-down con detección de fin-de-ciclo por el marcador 'ciclo
terminado' del worker-loop (cuenta ocurrencias en el journal, robusto a skew de reloj
y builds largos). 'Subí la cola, corré esto, se apaga solo al terminar.'

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 06:47:11 -04:00
sergioandClaude Opus 4.8 5658e9337c granja: ciclo de vida EFÍMERO por hcloud API (farm-up/farm-down) + baseline €0
Migración del worker pet-24/7 a workers efímeros on-demand. farm1 (ccx23 persistente que
idleaba quemando plata) → snapshot golden 'hammer-golden-2026-07-05' (img 405120842: toolchain
+ store sellado horneados) → destruido. Baseline ahora 0 cajas / €0.

- farm-up.sh [N]: crea N workers desde el snapshot (cache-hit instantáneo del catálogo baked),
  los alinea con la cola actual del laptop, registra la flota en scripts/farm/.fleet (gitignored).
- farm-down.sh [name...]: cosecha el store (CAS, merge seguro) al laptop y DESTRUYE; promueve+firma.
  Orden seguro: sólo destruye si el pull de store salió bien.
- Validado punta a punta: up 1 → boot+cache+toolchain OK → down (cosecha+destruye) OK.
- Cron laptop harvest-go contra farm1 removido (la cosecha ahora la hace farm-down).

El worker sigue sin secretos (hub-and-spoke): compute puro y descartable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 08:23:06 -04:00
sergioandClaude Fable 5 e4ef80b76e Etapa G (go): cosecha 20 + syft/traefik promovidos a mano (pack fallaba: patches no viajaban a recipes/ — fix en harvest-go) + pre-check por name interno del TOML (exporters) + re-cola crossplane-cli/fabric-ai/oh-my-posh con fixes (alias crossplane, ./cmd/fabric, -o oh-my-posh) + logs por-cola en build-farm
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 05:31:58 -04:00