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 815Rust 56Shell 37Markdown 291229 Sutichaykuna

ADR 0021: takana pone el motor, churay pone la cara — y contesta las tres del SDD 28 §8

El usuario preguntó lo correcto: «churay era la herramienta de instalar? ya tiene varias
cosas adelantadas». Sí, y mucho: instalador por grafo con GUI y CLI, bundles firmados
ed25519, repo remoto multi-origen con clave anclada y anillo rotable, actualizador con
rollback, vigía de espejos en cron, y la puerta desde la web (tawasuyu.net/instalar.sh).
Lo que no hace es instalar takana en metal.

Entonces la pregunta no era «¿hay que escribir un instalador?» sino de quién es qué. La
decisión: takana es dueño del MOTOR y expone `takana metal {check,survey,install}`; churay
es dueño de la CARA y lo llama, consumiendo `survey --json` en vez de leer los discos por
su cuenta. Esa última parte es la que no se negocia — el censo es lo que decide qué NO se
toca, y una cara que clasifique por su cuenta y no sepa, por ejemplo, que una partición
marcada LUKS en la tabla no se ofrece aunque blkid no vea cabecera adentro, no muestra de
menos: formatea de más.

Las tres preguntas que el SDD 28 §8 dejó anotadas, contestadas:

1. ¿hidrata o reproduce? Para ESTE verbo, ninguna: el instalador lleva el rootfs ya
   construido adentro, en RAM. La pregunta seguía valiendo para paquetes sueltos en una
   distro ajena, que es otro verbo, y queda abierta ahí. Confundirlas era lo que hacía
   parecer que había una sola decisión.
2. ¿quién es dueño del verbo? takana el motor, churay la cara.
3. ¿y un host glibc? No es un problema, y hay que decir por qué: lo que el ADR 0015 aparta
   del store son IMÁGENES ajenas; acá no entra nada al store y lo que corre en el host es
   un ELF estático, medido. El instalador de verdad corre sobre su propio kernel después
   del salto, donde ya no hay host.

Lleva la entrega para el agente de tawasuyu (los cuatro pasos de churay) y el resultado
medido del arnés del salto: once comprobaciones en verde, con el bootmgfw.efi del vecino
byte a byte igual y la tabla de particiones sin cambiar.

No decide Windows (frente propio de churay), ni firmar el kernel del instalador (decisión
abierta del ADR 0018), ni el redimensionado (pide desmontar, y eso es física).

02c529b8a50c0199bfea6c7b7d3f0d3179774463 · ruwaq Sergio · (29 p'unchaw ñawpaq) · tayta 3a0b6dc03 · kay commitpi sach'ata qhaway

Semántico sayk'uy

Mana ima sutichaypas t'ikrakurqanchu: qillqalla, yachachiylla utaq willaylla t'ikrakurqan.

3 qillqakuna · +271 −3

diff --git a/docs/28-servidor-de-produccion.md b/docs/28-servidor-de-produccion.md
index 6c7fb6ba..0abae7d9 100644
--- a/docs/28-servidor-de-produccion.md
+++ b/docs/28-servidor-de-produccion.md
@@ -3785,6 +3785,14 @@ Encaja con lo que ya hay y por eso vale escribirlo: `takana install` ya resuelve
 ya corre imágenes ajenas enjauladas sobre el mismo kernel; y el vigía de espejos de churay ya corre
 en cron desde gioser.
 
+✅ **DECIDIDO el 2026-09-23 — [ADR 0021](adr/0021-dueno-del-verbo-de-instalar.md).** Las tres
+preguntas de abajo están contestadas ahí: **takana pone el motor y expone `takana metal
+{check,survey,install}`; churay pone la cara y lo llama**, consumiendo `survey --json` en vez de leer
+los discos por su cuenta. La (1) se partía en dos y por eso no se dejaba contestar —para instalar el
+SISTEMA no se hidrata ni se reproduce: el instalador lleva el rootfs adentro, en RAM—; sigue abierta
+para *paquetes sueltos en una distro ajena*, que es otro verbo. La (3) no era un problema: lo que
+corre en el host ajeno es un ELF **estático**, así que su libc no participa.
+
 **No se decide acá.** Es un ADR propio, y tiene al menos tres preguntas abiertas antes de escribir
 una línea: (1) si el instalador «en cualquier distro» hidrata binarios o reproduce desde fuente —la
 misma pregunta del [SDD 27 §4](27-perfiles-instalables-y-el-instalador.md), y la respuesta puede no
diff --git a/docs/adr/0021-dueno-del-verbo-de-instalar.md b/docs/adr/0021-dueno-del-verbo-de-instalar.md
new file mode 100644
index 00000000..96a0efa6
--- /dev/null
+++ b/docs/adr/0021-dueno-del-verbo-de-instalar.md
@@ -0,0 +1,218 @@
+# ADR 0021 — El dueño del verbo de instalar: takana pone el motor, churay pone la cara
+
+- **Estado:** PROPUESTO, con la **parte de takana IMPLEMENTADA y medida** (2026-09-23). Lo de churay
+  es entrega, no está hecho.
+- **Fecha:** 2026-09-23
+- **Contesta:** [SDD 28 §8](../28-servidor-de-produccion.md) («`churay` como instalador centralizado
+  — la dirección, no la decisión»), que dejó tres preguntas anotadas para que se **decidieran** en
+  un ADR propio y no se descubrieran a mitad.
+- **Frontera:** `crates/takana-cli/src/metal.rs`, `scripts/takana-install-partition.sh`,
+  `scripts/instalador-initramfs.sh`, y —del otro lado— `02_ruway/churay` en tawasuyu.
+- **Relacionado:** [ADR 0017](0017-ciclo-de-vida-del-kernel.md) (kexec),
+  [ADR 0018](0018-soberania-del-arranque.md) (NVRAM), [ADR 0015](0015-imagenes-ajenas.md) (glibc),
+  [SDD 27](../27-perfiles-instalables-y-el-instalador.md) (perfiles instalables).
+
+## Contexto
+
+El instalador en metal quedó funcionando el 2026-09-23: censo del terreno, elección por PARTUUID,
+formateo de una sola partición, ESP poblada sin montarla, entrada NVRAM con respaldo previo, y el
+camino «sin USB» por `kexec`. Y aun así **no se podía usar**. La forma de invocarlo era ésta:
+
+```sh
+sudo ./takana kernel kexec ./kernel --initrd ./takana-instalador.cpio.gz \
+     --cmdline 'console=ttyS0,115200 console=tty0' --jump
+```
+
+El usuario lo dijo en una línea:
+
+> *pero esos son comandos a lo poweruser.*
+
+Tiene razón, y el diagnóstico es preciso: eso no es un instalador, es **el motor con la tapa
+abierta**. Quien lo teclea tiene que saber que existe `kexec`, que el payload va por `--initrd`, que
+sin `--jump` sólo se carga y qué consola pedirle al kernel nuevo. **Ninguna de esas cuatro cosas es
+una decisión del que instala** — son detalles de implementación que se filtraron a la superficie.
+
+Y la segunda mitad de su observación es la que importa para este ADR:
+
+> *churay era, la herramienta de instalar? ya tiene varias cosas adelantadas.*
+
+Sí. `churay` (quechua: *poner*) existe desde el 2026-06-24 en tawasuyu y está lejos de ser un
+esqueleto: instalador **por grafo** con GUI (`churay-llimphi`) y CLI (`churay-cli`), modo sistema y
+modo local, bundles precompilados firmados con ed25519, repo remoto multi-origen con clave anclada y
+anillo rotable, actualizador con `rollback`, vigía de espejos en cron, y una puerta de entrada desde
+la web (`tawasuyu.net/instalar.sh`). Lo que **no** hace es instalar takana en metal.
+
+Entonces la pregunta no es «¿hay que escribir un instalador?» sino **quién es dueño de qué**.
+
+## Decisión
+
+> **takana es dueño del MOTOR y expone un verbo estable. churay es dueño de la CARA y lo llama.
+> Ninguna cara vuelve a leer discos por su cuenta.**
+
+### 1. El verbo vive en takana, y es `takana metal`
+
+```
+takana metal check     ¿puede esta máquina instalarse sin USB?      (no toca nada)
+takana metal survey    ¿qué hay en los discos y qué se puede usar?  (no toca nada)
+takana metal install   el viaje entero: comprueba, avisa y salta
+```
+
+Tres verbos y el orden en que se usan es el orden en que se leen. `install` encuentra el paquete
+solo —junto al binario, no en el directorio actual, porque el paquete se copia a un pen drive y se
+corre desde cualquier lado—, corre el diagnóstico **antes** de prometer nada, dice en voz alta qué
+va a pasar y pide un «si» escrito. No acepta un *enter* pelado: el enter es lo que se aprieta sin
+leer, y lo que sigue mata la sesión.
+
+**Por qué en takana y no en churay.** El motor necesita root, `kexec_file_load`, dispositivos de
+bloque, las herramientas selladas del store y el rootfs ya construido. Nada de eso se puede mover a
+un instalador de usuario sin arrastrarlo entero. churay ya llegó a esa misma conclusión por su lado
+y lo dejó escrito en su LEEME: *«hammer-build/overlay (bwrap, zig, overlayfs, root) no encajan en un
+instalador de usuario, así que no se usan»*.
+
+### 2. El censo es UNO, y las caras lo consumen
+
+`takana metal survey` **no reimplementa nada**: ejecuta el mismo `takana-install-partition.sh
+--survey` que corre dentro del instalador. Con `--json` emite el contrato:
+
+```json
+{ "version": 1,
+  "raiz_viva": "8:2",
+  "particiones": [
+    {"dev": "/dev/sda2", "partuuid": "6F0B…", "mib": 6144, "fs": "ext4",
+     "etiqueta": "hammer-root", "montaje": "/", "veredicto": "RAIZ_VIVA",
+     "usable": true, "explicacion": "la raíz del sistema que está corriendo AHORA"} ] }
+```
+
+`veredicto` ∈ `CANDIDATA · LIBRE · RAIZ_VIVA · SOLO_ESP · EN_USO · NO_TOCAR`. El campo `usable` es
+la lectura de una sola pregunta —¿se puede instalar acá?— para que una cara **no tenga que
+reimplementar el `case` de los veredictos y equivocarse en el default**: lo que no reconozca no es
+usable.
+
+**Por qué esto es la parte que no se negocia.** El SDD 28 §8 ya nombra el riesgo —«un motor, tres
+frentes»— y este repo lo pagó dos veces (los `hydrate-*.sh` contra `targets.toml`; la unidad systemd
+del worker contra el repo). Acá el coste de divergir es peor que quedar desactualizado: **el censo
+es lo que decide qué NO se toca**. Una cara que clasifique por su cuenta y no conozca, por ejemplo,
+que una partición marcada LUKS en la tabla no se ofrece aunque `blkid` no vea cabecera adentro, no
+muestra de menos: **formatea de más**.
+
+### 3. Lo que churay hace, y lo que no
+
+| | churay | takana |
+|---|---|---|
+| pintar el terreno y preguntar | **sí** (consume `survey --json`) | modo texto, de respaldo |
+| decidir si una partición se puede tocar | **no** | sí, y es el único |
+| formatear, poblar la ESP, NVRAM | **no** | sí, dentro del instalador |
+| llegar a una máquina ajena (web, bundle, firma) | **sí, ya lo tiene** | no, y no lo va a tener |
+
+Lo último es el aporte real de churay y la razón por la que esto no es una capa de más: **el camino
+de la web al escritorio ya existe del lado de churay** —`instalar.sh`, `blobs/<hex>` verificados,
+manifiesto firmado, clave anclada, multi-origen, anillo rotable— y del lado de takana no existe
+ninguno. Hoy el paquete del instalador se copia a mano.
+
+## Las tres preguntas del SDD 28 §8, contestadas
+
+**(1) ¿Hidrata binarios o reproduce desde fuente en una máquina ajena?**
+Para **este** verbo, ninguna de las dos: el instalador **lleva el rootfs ya construido adentro**, en
+RAM, y lo copia. No hay build en la máquina del usuario y no hace falta red. La pregunta seguía
+teniendo sentido para *paquetes sueltos en cualquier distro*, que es otro verbo (`takana install`,
+SDD 27 §4) y **queda abierta ahí**. Confundirlas fue lo que hacía parecer que había una sola
+decisión.
+
+**(2) ¿Quién es dueño del verbo?** Contestado arriba: takana el motor, churay la cara. El verbo
+canónico es inglés (regla 4 de `CLAUDE.md`); lo que se lee, castellano.
+
+**(3) ¿Qué pasa con un host glibc?** **No es un problema, y hay que decir por qué para que nadie lo
+trate como si lo fuera.** Lo que el ADR 0015 aparta del store son *imágenes ajenas* —rootfs que no
+reproducen desde fuente—. Acá no entra nada al store: lo que corre en el host ajeno es **un ELF
+estático** (medido: `statically linked`, sin intérprete), así que el libc del anfitrión no
+participa. El instalador de verdad corre sobre **su propio kernel** después del salto, donde ya no
+hay host.
+
+## El salto, ejecutado de verdad (2026-09-23)
+
+Hasta este día el camino «sin USB» tenía **todo probado menos el salto**: el instalador en RAM se
+había arrancado a mano y hacía su trabajo, pero `kexec_file_load` **no se había ejecutado nunca** —
+ninguna de nuestras máquinas corre un kernel con `CONFIG_KEXEC_FILE`, así que el primer sitio donde
+el salto iba a pasar era la máquina del usuario. Eso convierte al usuario en el banco de pruebas.
+
+`scripts/metal-salto-test.sh` lo cierra: arranca una VM con `linux-metal-kexec` —la variante que sí
+trae el flag—, le da un disco con vecinos de verdad (una ESP con un `bootmgfw.efi` de Microsoft
+dentro, un destino vacío y un hueco sin asignar) y corre ahí **los tres verbos de usuario**. Las
+once comprobaciones en verde, y el veredicto sale del **disco**, no de lo que la pantalla dijo:
+
+```
+✓ el sistema origen arrancó            ✓ EL SALTO OCURRIÓ: el origen no volvió del kexec
+✓ metal check: dice que SÍ             ✓ el instalador arrancó DESPUÉS del salto
+✓ metal survey: el censo salió         ✓ el instalador terminó bien
+✓ metal install: anunció el salto      ✓ la tabla de particiones NO cambió
+✓ el destino quedó ext4 con LABEL=hammer-root
+✓ la ESP tiene \EFI\takana\takanax64.efi
+✓ el vecino de la ESP (bootmgfw.efi de Microsoft) quedó byte a byte igual
+```
+
+El log serial corta exactamente donde tiene que cortar —`⚡ saltando al instalador — esto no
+vuelve.`— y la línea siguiente ya es del otro kernel: `TAKANA-INSTALADOR: arrancado en RAM. El
+sistema viejo YA NO CORRE: su disco está libre.`
+
+⚠ Y el arnés nació con dos comprobaciones que fallaban **sin que el instalador tuviera nada que
+ver**, que es la forma más cara de tener un test: comparaba el listado de `mdir` del vecino contra sí
+mismo —y el listado trae el «bytes free» del volumen, que cambia en cuanto se copia cualquier otra
+cosa— y usaba `cmp`, que **no existe en este anfitrión musl**: `command not found` sale 127, el `if`
+lo toma como falso y el test acusaba «la tabla cambió» sin haber comparado nada. Un test que no
+puede correr tiene que decirlo, no fallar con el mensaje de otra cosa.
+
+## Lo medido (2026-09-23)
+
+| | |
+|---|---|
+| `metal check` como uid 1001 | dice «falta root», **y avisa que el diagnóstico no es concluyente** |
+| `metal check` como root del anfitrión | dice la causa real: el kernel no trae `CONFIG_KEXEC_FILE` |
+| `metal survey --json` en el hub | 6 entradas, JSON válido, una sola `usable` |
+| `metal install` sin paquete | nombra los tres directorios donde buscó |
+| `--unattended` sin destino | aborta: «nadie va a estar ahí para elegirlo» |
+| la cmdline sin respuestas | **idéntica al default**: no hay ninguna máquina horneada (test) |
+
+Dos defectos aparecieron al poner la cara, y los dos eran del motor:
+
+- **`--survey` exigía el kernel y mtools.** En una máquina ajena —sin store y sin kernel del
+  corpus— el censo moría con «no encuentro el kernel EFI-stub» **antes de imprimir una sola línea
+  del terreno**. Mirar no puede costar lo mismo que escribir.
+- **`--survey` salía con 1 habiendo funcionado.** Un `a && b && c` como último comando del bucle de
+  impresión decide el estado de salida del script entero; con `set -e`, `mostrar_censo` moría ahí si
+  la última fila no tenía PARTUUID (un disco sin tabla, un hueco libre). En este hub la última fila
+  es `/dev/sdc`. Un `if` no propaga; el `&&` sí.
+
+Y uno de clasificación, que es el que más se parece a lo que este ADR quiere evitar: el censo
+ofrecía `/dev/sda1` —**2 MiB**, el hueco de arranque— como «✓ se puede usar», porque el veredicto
+miraba **qué** hay dentro y nunca **cuánto** cabe. Elegirla formatea algo real y muere a mitad de la
+copia. El piso es ahora el mismo número con el que se talla un hueco (`mib_necesarios`), así que las
+dos puertas no pueden divergir.
+
+## Lo que este ADR NO decide
+
+- **Windows.** El instalador standalone desde Windows sigue donde lo dejó el runbook: WSL2 no tiene
+  efivarfs y un `.exe` nativo es un port, no una envoltura. churay tiene su propio frente ahí
+  (`PLAN-MULTIPLATAFORMA.md`, F4) y no se mezcla con éste.
+- **Firmar el kernel del instalador.** Con Secure Boot en modo `integrity` el salto no ocurre, y hoy
+  no firmamos. Es la decisión abierta del ADR 0018.
+- **Redimensionar.** Encoger un filesystem pide desmontarlo, y la raíz viva no se desmonta. Eso es
+  física, no una carencia: el redimensionado vive **después** del salto, dentro del instalador, y
+  todavía no está escrito.
+
+## Entrega a tawasuyu (churay)
+
+Lo que hace falta del otro lado, en orden, y nada de esto toca takana:
+
+1. Un nodo `takana (sistema en metal)` en el grafo, con `Scope::System`, que **no** se instala como
+   los demás: al activarlo abre la pantalla del terreno.
+2. Esa pantalla se pinta con `takana metal survey --json`. Las particiones con `usable: false` se
+   muestran —el usuario tiene que ver su disco entero— pero no se pueden elegir, y se muestra su
+   `explicacion` tal cual: está escrita para leerse.
+3. El botón llama a `takana metal install --root PARTUUID=… [--esp PARTUUID=…] --unattended --yes`,
+   y **avisa antes de que la sesión gráfica muere en el salto**.
+4. Si `takana metal check` sale ≠ 0, la pantalla ofrece el USB en vez del salto, mostrando los
+   motivos que imprime, que ya están redactados para el usuario.
+
+El binario `takana` que churay necesita es el estático, el mismo que ya viaja en el paquete del
+instalador. churay no necesita el store, ni el rootfs, ni root para el `survey` — sólo para el
+`install`, que ya sabe pedirlo.
diff --git a/docs/runbooks/instalador-laptop-tigerlake.md b/docs/runbooks/instalador-laptop-tigerlake.md
index a8b6ed7c..2ec926dd 100644
--- a/docs/runbooks/instalador-laptop-tigerlake.md
+++ b/docs/runbooks/instalador-laptop-tigerlake.md
@@ -250,6 +250,46 @@ instalador:
 | `\EFI\takana\takanax64.efi` | = el bzImage, hash exacto |
 | tabla de particiones | 3 entradas, sin cambios |
 
+## 5 quater. La superficie de usuario: `takana metal` (2026-09-23)
+
+El instalador estuvo funcionando un día entero **sin poder usarse**, y el usuario lo dijo mejor que
+nadie: *«pero esos son comandos a lo poweruser»*. La forma de invocarlo era `takana kernel kexec
+./kernel --initrd … --cmdline '…' --jump`: el motor con la tapa abierta. Quien lo teclea tiene que
+saber qué es kexec, que el payload va por `--initrd`, que sin `--jump` sólo se carga y qué consola
+pedirle al kernel nuevo — y **ninguna de esas cuatro cosas es una decisión del que instala**.
+
+La tapa es [ADR 0021](../adr/0021-dueno-del-verbo-de-instalar.md), y son tres verbos:
+
+```sh
+sudo takana metal check      # ¿se puede sin USB? No toca nada.
+sudo takana metal survey     # ¿qué hay en los discos? No toca nada.  (--json = el contrato)
+sudo takana metal install    # comprueba, avisa y salta.
+```
+
+`survey` **no es un censo nuevo**: llama al mismo `takana-install-partition.sh --survey` del §5. Esa
+es la parte que no se negocia — el censo es lo que decide qué NO se toca, y dos censos que divergen
+no muestran de menos: formatean de más.
+
+Al ponerle la cara aparecieron tres defectos que eran del motor, no de la cara, y los tres sólo se
+ven desde afuera:
+
+| defecto | cómo se veía |
+|---|---|
+| `--survey` exigía el kernel y mtools | en una máquina ajena moría con «no encuentro el kernel EFI-stub» **antes de imprimir una línea del terreno** |
+| `--survey` salía con **1** habiendo funcionado | un `a && b && c` como último comando del bucle decide el rc del script; con `set -e`, `mostrar_censo` moría si la última fila no tenía PARTUUID |
+| el censo ofrecía `/dev/sda1`, **2 MiB** | el veredicto miraba QUÉ hay dentro y nunca CUÁNTO cabe ⇒ formatea algo real y muere a mitad de la copia |
+
+### El salto ya no es una promesa (2026-09-23)
+
+`scripts/metal-salto-test.sh` ejecuta `kexec_file_load` **de verdad**, en una VM con
+`linux-metal-kexec` (el único kernel del corpus con `CONFIG_KEXEC_FILE`). Once comprobaciones en
+verde, y salen del disco: el origen muere en el salto, el instalador arranca en RAM, formatea sólo
+su destino, deja la ESP con `\EFI\takana\takanax64.efi` y **el `bootmgfw.efi` del vecino byte a
+byte igual**. La tabla de particiones no cambia.
+
+⇒ Cuando esto se corra en la laptop, el salto ya no es el paso sin probar. Lo que sigue sin
+ejercitarse en metal es el **firmware** (§3) y el menú interactivo con teclado real.
+
 ## 6. Instalar desde OTRO sistema operativo, sin medio live
 
 La pregunta del usuario (2026-09-23) es si se puede romper con la instalación tradicional y tener un
@@ -264,7 +304,9 @@ Eso ya es un instalador standalone; lo que falta es el envoltorio. Las piezas:
 
 | pieza | estado |
 |---|---|
-| leer el terreno | ✅ `takana boot entry survey` |
+| leer el terreno | ✅ `takana metal survey` (y `takana boot entry survey` para la NVRAM) |
+| decidir si se puede sin USB | ✅ `takana metal check` |
+| el viaje entero, con una sola orden | ✅ `takana metal install` (§5 quater) |
 | escribir la entrada de arranque | ✅ `takana boot entry add` (efivarfs, sin efibootmgr) |
 | deshacer si algo sale mal | ✅ `takana boot entry backup` / `restore` |
 | traer los bits | ✅ `takana mirror` (por hash, verificando `of_tree`) y `takana hydrate` |
@@ -301,8 +343,8 @@ cuando el objetivo sea que lo instale **otra gente** (SDD 19, lanzamiento públi
 2. **USB de prueba y arrancarlo en el metal.** Lo que se valida NO es el escritorio: es **el
    firmware** —que asocie el WiFi, que el display tome el DMC, que levante el DSP de audio— y, con
    el mismo viaje, **el EFI-stub soberano con el initrd chico** (§4), que es la prueba que falta.
-3. **El instalador no destructivo** del §5: primero el modo «partición destino» (1 y 2), después el
-   redimensionado (3).
+3. **El instalador no destructivo** del §5: el modo «partición destino» y el «al lado» están
+   cerrados y medidos (§5 quater y el arnés del salto); falta el **redimensionado** (§5.3).
 4. **Recién entonces** tocar el disco de la laptop.
 5. Y después, los escritorios con GPU real: hoy los cuatro corren por llvmpipe, que en QEMU es
    correcto y en una Iris Xe es tirar la GPU a la basura.