El handoff avisa que nada suyo está verificado contra hammer («si un dato de hammer lo
contradice, manda hammer»). Esto es esa verificación: contesta sus seis preguntas del §9 con
evidencia del código, corrige lo que estaba mal y añade dos objeciones que cambian el orden.
NO es aprobación ni plan de implementación.
EL HECHO QUE REORDENA TODO, y que el handoff no podía conocer: las FASES entran en
hash_inputs, y el config del kernel vive en la fase `configure` ⇒ **el config ES la identidad
del artefacto**. Corta en los dos sentidos. A favor: §2.2 (atestar un config por huella de
hardware) sale casi gratis, porque cada config ya es un artefacto direccionado por contenido,
reproducible y firmable — no hay que inventar el mecanismo. En contra: la explosión de builds
no es hipotética, es aritmética visible en el disco, que ya crece 24 G por ciclo. Y una
«perilla» de la UI no es un parámetro de runtime: es una edición de receta.
LAS SEIS RESPUESTAS:
P1 fragmentos — YA es como §6 prescribe: defconfig + `scripts/config -e/-d` + olddefconfig,
o sea que la app nunca escribe un .config. Media implementación está en producción. Falta
el diff-back, sin el cual un símbolo pedido que no sobrevive se pierde EN SILENCIO.
P2 repro con config variable — sí, por construcción: no hay un kernel cuya reproducibilidad
dependa de congelar el config; hay N kernels, cada uno congelado. Se pueden PREFIRMAR
artefactos, no sólo configs.
P3 tiempo — 35/45/60 min según receta ⇒ la bisección de §5 son ~4,5 h de la máquina que
acaba de no arrancar. Mata la bisección ingenua.
P4 harkaq — el patrón sí, el código no: su clausura enumera fichero a fichero sobre
artefactos del store, semántica de rutas. Kconfig es otro grafo.
P5 eje huella en el índice — sí y compatible hacia atrás: PackageEntry usa campos serde
opcionales. Mismo truco que hizo pagable el campo `license` hoy.
P6 PLAN-KIKIN §4.bis — DADO POR PENDIENTE Y ESTÁ HECHO EN DOS DE TRES: linux-metal y
linux-generic ya declaran los cuatro símbolos; sólo `linux` deja IO_URING y BPF_SYSCALL
al azar del defconfig. Y ojo al método: `grep IO_URING` da positivo por un COMENTARIO;
hay que mirar los -e/-d dentro del bloque configure. Mismo error que infló el conteo de
licencias hoy y que hace mentir la regla del `.a` no-PIC.
OBJECIÓN a §2.1: «clausura» sobre Kconfig no está definida todavía. Hay tres tipos de arista
(`depends on`, `select`, `imply`) con semánticas incompatibles, y `select` fuerza símbolos
IGNORANDO los `depends on` del destino. «Alcanzable desde CONFIG_WIRELESS» da conjuntos
distintos según la arista y ninguno es la respuesta. No lo invalida: lo convierte en la parte
con contenido técnico real. Y hay con qué validarlo GRATIS: el config actual de linux.toml
YA es un juego de bundles N1 hecho a mano («-d WLAN -d WIRELESS -d CFG80211 -d MAC80211
-d RFKILL» es literalmente «no necesito wifi») sobre un kernel que arranca.
LO QUE AGREGO:
· Bisección SIN recompilar: kernel «superset» con lo bisecable como módulo ⇒ los bundles de
hardware se prueban con lista de bloqueo en la línea de arranque. 6 reinicios en vez de 6
rebuilds. No cubre lo que debe ser =y (buena parte de N2), y el superset es el kernel gordo
que N1 quiere evitar: es diagnóstico, no el de todos los días.
· El gate de no-regresión debe ser POR OBJETIVO: linux.toml apaga USB/HID/INPUT a propósito
(es el kernel de QEMU con consola serie) y un gate global rechazaría una receta sana.
· «Booteó» no basta para atestar: un kernel arranca y te deja sin red, que es el desastre que
el propio gate quiere evitar. La atestación debe llevar QUÉ se comprobó — el mismo dato que
calcula el gate: una implementación, dos usos.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
13 KiB
SDD 22 — Configurador de kernel por niveles: respuesta al handoff de tawasuyu
Escrito 2026-08-07, en respuesta a tawasuyu/HANDOFF-KERNEL-CONFIG-A-HAMMER.md.
El handoff avisa de su propio método: «nada de lo que sigue está verificado contra el código de hammer — si un dato de hammer lo contradice, manda hammer». Este documento es esa verificación. No es una aprobación ni un plan de implementación: contesta las seis preguntas de su §9 con evidencia, corrige lo que estaba mal, y señala dos objeciones técnicas que cambian el diseño.
Veredicto corto: la idea se sostiene y el sustrato está más listo de lo que el handoff supone —
el mecanismo de §6 («la app nunca escribe un .config») ya es exactamente como funciona hoy.
Pero hay un hecho estructural que el handoff no podía conocer y que reordena todo, y una
objeción técnica a §2.1 que hay que resolver antes de escribir una línea.
1. El hecho que reordena todo: el config ES la identidad del artefacto
En hammer, Recipe::hash_inputs es una lista blanca, y las fases de build entran en ella. El
config del kernel vive dentro de la fase configure de recipes/linux*.toml. Por lo tanto:
Cambiar un solo símbolo de config cambia el
ArtifactHashdel kernel.
Esto no es un detalle de implementación: es la bisagra del diseño, y corta en los dos sentidos.
A favor, y fuerte: §2.2 (config atestado por huella de hardware) sale casi gratis. No hay que
inventar un mecanismo para «atestar un config»: cada config ya es un artefacto direccionado por
contenido, reproducible bit a bit y firmable con la maquinaria que existe. Un «config que booteó en
esta huella» es, literalmente, un ArtifactHash + una firma. El handoff intuyó que hammer tenía el
sustrato; lo tiene más de lo que creía.
En contra, y también fuerte: la explosión de builds que §2.2 quiere evitar no es hipotética, es aritmética visible en el disco. Cada variante de config es un artefacto nuevo en el store, y el store ya crece 24 G por ciclo de trabajo sólo con las variantes que producimos hoy sin querer. Con N1 (~40–60 bundles) el espacio combinatorio es 2⁴⁰; con «perfiles publicados» —que es lo que el handoff propone en §8— es un puñado. La restricción de §8 no es una preferencia de diseño: es una condición de supervivencia del disco, y conviene decirlo así en la UI.
Consecuencia práctica que hay que aceptar desde el día uno: una «perilla» de la UI no es un
parámetro de runtime, es una edición de receta. No hay forma de tener un config variable sin
generar recetas. El diseño correcto es que hammer kernel plan emita una receta derivada (con su
propio hash) en vez de fingir que el kernel es un binario parametrizable.
2. Las seis preguntas del §9, contestadas con evidencia
P1 — ¿Las recetas de kernel aceptan fragmentos o sólo defconfig completo?
Ya son fragmentos, y con la forma que §6 prescribe. recipes/linux.toml, fase configure:
make ARCH=x86_64 defconfig && \
scripts/config -d MODULE_SIG -d DEBUG_INFO_BTF -e SECURITY_LANDLOCK -e AUDIT \
-d WLAN -d WIRELESS -d CFG80211 -d MAC80211 -d RFKILL … && \
make ARCH=x86_64 olddefconfig
Base + deltas por símbolo + resolución por el olddefconfig del propio kernel. La app nunca
escribe un .config. La única diferencia con el handoff es el vehículo: scripts/config -e/-d en
vez de ficheros de fragmento y merge_config.sh. Son equivalentes y scripts/config es más fácil de
generar programáticamente. Media implementación de §6 ya está en producción.
Falta la otra mitad, que es la que da la UI veraz: el diff-back. Hoy nada comprueba que los
símbolos pedidos hayan sobrevivido a olddefconfig. Un -e FOO cuya dependencia no se cumple se
pierde en silencio — el mismo patrón de fallo que el license que caía dentro de [deps].
P2 — ¿El build es bit-reproducible con config variable?
Sí, y por construcción. El config no es un parámetro externo: es parte de la entrada hasheada (§1). Cada config es su propia receta con su propio hash y su propia garantía de reproducción. No hay un «kernel» cuya reproducibilidad dependa de congelar el config; hay N kernels, cada uno congelado. ⇒ §2.2 puede prefirmar artefactos, no sólo configs.
P3 — ¿Cuánto tarda un kernel completo? 🚨
Declarado en las propias recetas: linux ~35 min, linux-metal ~45 min, linux-generic ~60 min
en la máquina de referencia. En la más lenta del parque, más.
Esto mata la bisección ingenua de §5. ~6 rebuilds × 45 min = 4,5 horas de la máquina del usuario para encontrar un bundle culpable. Nadie espera eso, y menos con la máquina que acaba de no arrancar. El handoff ya preveía el riesgo («si es caro, tope duro de iteraciones»); la respuesta es que es caro. Ver §4, donde propongo una bisección que no recompila.
P4 — ¿harkaq puede prestar su motor de clausura?
El patrón sí; el código no. crates/hammer-build/src/harkaq.rs deriva su política enumerando
fichero a fichero los artefactos de las deps declaradas — es una clausura sobre el grafo de
paquetes materializado en el store, con semántica de rutas de filesystem. Kconfig es otro grafo, con
otra semántica y ~15 000 símbolos. Reusar la idea («política = clausura, no lista») es correcto y
está validado; reusar la implementación no. Hace falta un lector de Kconfig — y ahí está la
objeción del §3.
P5 — ¿El índice firmado admite un eje más (la huella)?
Sí, y sin romper nada. PackageEntry (crates/hammer-core/src/repo.rs) es un struct serde cuyos
campos opcionales llevan #[serde(default, skip_serializing_if = ...)]. Añadir fingerprint: Option<String> es compatible hacia atrás: los índices viejos siguen parseando y los clientes viejos
ignoran el campo. Es exactamente el truco que hizo pagable el campo license hoy mismo.
P6 — El prerrequisito de PLAN-KIKIN §4.bis
Estaba dado por pendiente y está hecho en dos de tres. Medido:
| receta | SECURITY_LANDLOCK |
AUDIT |
IO_URING |
BPF_SYSCALL |
|---|---|---|---|---|
linux-metal |
✅ -e |
✅ -e |
✅ -e |
✅ -e |
linux-generic |
✅ -e |
✅ -e |
✅ -e |
✅ -e |
linux |
✅ -e |
✅ -e |
❌ sólo en un comentario | ❌ sólo en un comentario |
⚠️ Y la forma de medirlo importa: grep IO_URING recipes/linux.toml da positivo porque la palabra
aparece en un comentario. Hay que mirar los -e/-d dentro del bloque configure. Es el mismo
error que hoy infló el conteo de licencias (grep -l license contaba comentarios y nombres de
paquete) y el que hace mentir a la regla del .a no-PIC. En este repo, grep sobre ficheros con
comentarios densos no es una medición.
⇒ Queda pendiente sólo linux, que es el kernel de QEMU/serial.
3. Objeción técnica a §2.1: «clausura» sobre Kconfig no está bien definida todavía
La propuesta —«todo lo alcanzable desde CONFIG_WIRELESS menos lo alcanzable desde lo que sí
quiero»— asume un grafo con aristas de un solo tipo. Kconfig tiene al menos tres, con semánticas
incompatibles:
depends on— arista hacia arriba: no puedo existir sin X.select— arista hacia abajo y forzada: si yo entro, X entra, ignorando losdepends onde X. Es la fuente clásica de configs inválidos.imply— sugerencia blanda.
«Alcanzable» por depends on y «alcanzable» por select dan conjuntos distintos y ninguno de los
dos es la respuesta. Un driver de otro subsistema puede select un helper de wireless sin ser
wireless: cae dentro de la clausura ingenua y apagarlo rompe algo que el usuario sí quería. Y al
revés, select puede meter símbolos wireless desde fuera de la clausura.
Esto no invalida §2.1 — lo convierte en la parte con contenido técnico real, tal como el propio handoff dice en su §10. Pero hay que definir la semántica de arista antes de escribir el predicado, y la definición hay que validarla contra algo.
Y tenemos contra qué validarla, hoy y gratis
El config actual de linux.toml ya es un juego de bundles N1 escrito a mano. Literalmente:
-d WLAN -d WIRELESS -d CFG80211 -d MAC80211 -d RFKILL ← el bundle «no necesito wifi»
-d SOUND -d SND ← «no necesito audio»
-d DRM -d DRM_I915 -d DRM_AMDGPU -d DRM_NOUVEAU -d FB ← «sin gráficos»
-d XFS_FS -d BTRFS_FS -d F2FS_FS -d JFS_FS -d REISERFS_FS ← «sólo ext4»
Es un corpus de verdad-de-campo: alguien decidió a mano qué símbolos componen «no necesito wifi», y
ese kernel arranca. La prueba de §2.1 es directa y barata: ¿la clausura calculada de
CONFIG_WIRELESS reproduce ese conjunto? Si sobra o falta mucho, la semántica de arista está mal y
nos enteramos en una tarde, sin compilar nada. Refuerza el §10 del handoff: eso se hace antes que
nada, junto al modo reversa.
4. Lo que agrego: bisección sin recompilar
Dado P3 (35–60 min por kernel), la bisección de §5 cuesta horas. Pero la mayoría de los bundles N1
son drivers, y un driver puede ser =m en vez de =y.
Propuesta: un kernel «superset» con todo lo bisecable como módulo. Los bundles N1 de tipo hardware no se resuelven entonces recompilando, sino con una lista de bloqueo de módulos en la línea de arranque. La bisección deja de ser 6 rebuilds (4,5 h) y pasa a ser 6 reinicios (minutos), sobre un kernel que ya está en el árbol de generaciones.
Consecuencias:
- El «kernel a medida» pasa a ser una optimización de tamaño/arranque, no un requisito de funcionalidad ⇒ el usuario puede probar bundles antes de pagar un build.
- Encaja con el árbol de generaciones y el rollback E4, que ya existen.
- Limitación honesta: no cubre lo que debe ser
=y(LSM, preempción, unwinder,-march, LTO, o sea buena parte de N2). Para esos sí hay rebuild y sí hay que topar iteraciones. - Y un coste que hay que decir: el superset es exactamente el kernel gordo que el nivel 1 quiere evitar. Es una herramienta de diagnóstico, no el kernel de todos los días.
5. Lo que agrego: el gate de no-regresión (§6) debe ser por objetivo, no global
El handoff lo llama la mejor relación valor/esfuerzo y estoy de acuerdo. Pero el config actual de
linux.toml apaga USB_SUPPORT, HID, HID_SUPPORT, INPUT_MOUSE, INPUT_TOUCHSCREEN. Ese
kernel no puede recibir teclado en metal — y es correcto, porque es el kernel de QEMU con consola
serie. Un gate global «todo dispositivo en uso debe seguir teniendo driver» rechazaría una receta
sana.
⇒ El gate necesita el objetivo declarado (qemu-serial / metal / servidor) y una noción de
«dispositivos en uso de la máquina destino», que no siempre es la máquina donde se construye.
Sigue siendo la propuesta más barata de las nueve; sólo que el portón se pone en el sitio correcto.
6. Dos huecos del handoff que conviene cerrar antes
«Booteó» no es una aserción suficiente para atestar (§2.2). Un kernel puede arrancar y dejarte sin red, sin wifi o sin teclado — que es justo el desastre que §6 quiere evitar. Si la atestación dice sólo «booteó en esta huella», estamos firmando la clase de fallo que más duele. La atestación debería llevar qué se comprobó: qué dispositivos de la huella quedaron con driver bindeado después de arrancar. Es el mismo dato que ya calcula el gate de §6 ⇒ una implementación, dos usos.
El catálogo de bundles necesita las tres etiquetas de nivel, no dos. El handoff avisa en §5 que
«la mitad de N2 no es Kconfig sino variables de receta». Con §1 en la mano eso es más grave de lo que
parece: las perillas de receta (LTO, -march, zig_version) cambian el hash igual que los
símbolos, pero se aplican en otra fase y fallan de otra forma. La UI tiene que saber de qué lado
cae cada perilla o prometerá diffs que no puede explicar.
7. Qué contesto al reparto (§7)
De acuerdo con el reparto tal como está. Dos precisiones desde este lado:
- El contrato JSON: de acuerdo, y el precedente que cita (
/run/hammer/boot-graph.json) es el correcto. Añadiría que el JSON dehammer kernel plandebe incluir elArtifactHashresultante, porque por §1 el plan determina el artefacto: la UI puede decir «esto ya está construido y firmado» sin construir nada. - Los verbos propuestos me parecen bien salvo
hammer kernel build --plan, que esconde dos cosas muy distintas (construir, y dar de alta en el árbol de generaciones). Separarlos deja que la sonda en VM de §4 se meta entre medio como un paso propio y auditable.
8. Orden que propongo (modifica el §10 del handoff)
- Modo reversa (#9) — tal cual lo propone: cero riesgo, valida el catálogo contra un kernel real.
- Validar la clausura contra el config actual (§3 de este documento) — sale gratis, usa un corpus que ya existe, y decide si §2.1 es viable. Va antes que el gate, porque si la clausura no reproduce los bundles hechos a mano, el diseño de N1 cambia.
- Gate de no-regresión (#6), por objetivo (§5 de este documento).
- Diff-back (la mitad que falta de §6 del handoff): barato, y sin él la UI miente.
- Recién después, #1 (clausuras) con la semántica ya decidida.
No implementar todavía. Este documento existe para que la decisión se tome con los seis datos verificados, no con las suposiciones del handoff — que su autor, correctamente, marcó como tales.