# Runbook — el instalador para la laptop del usuario (TigerLake), y los dos muros que tiene delante > **Estado: ABIERTO — pero el §3 ya está PAGADO.** Las recetas se escribieron el 2026-09-16 desde el > propio laptop —que no tiene lab ni store— así que nacieron sin sellar; **el 2026-09-23 se verificó > que las cinco están selladas con su hash VIGENTE** (ver §3). Lo que queda abierto es el arranque en > el metal y, sobre todo, **el instalador no destructivo del §5**, que pasó a ser el trabajo central > del frente por decisión del usuario (2026-09-23). El §4 se REESCRIBIÓ: systemd-boot queda > descartado y no hacía falta. > Hermano de [`kde-qemu-desktop.md`](kde-qemu-desktop.md) y [`cosmic-desktop.md`](cosmic-desktop.md), > de los que reusa todo el andamiaje. ## 0. Qué se quiere Un medio que arranque en la laptop del usuario e **instale takana reemplazando la Artix** que hay hoy. No un USB de pruebas —eso ya existe con `mirada-usb.sh`— sino una instalación a disco. ## 1. La máquina, medida `lspci -nn`, `/proc/cpuinfo` y `lsblk` sobre el metal real, 2026-09-16: | pieza | qué es | driver | firmware | |---|---|---|---| | CPU | i7-11370H · family 6 · model 140 (0x8c) · stepping 1 | — | `intel-ucode/06-8c-01` | | GPU | TigerLake-LP GT2 [Iris Xe] `[8086:9a49]` | `i915` / `xe` | `i915/tgl_{dmc,guc,huc}*` | | WiFi | Intel Wi-Fi 6 AX201 `[8086:a0f0]` | `iwlwifi` | `iwlwifi-{QuZ-a0-hr-b0,cc-a0}-*.ucode` | | Audio | 500 Series HD Audio `[8086:a0c8]` | `snd_sof_pci_intel_tgl` | `intel/sof/sof-tgl.ri` | | Disco | NVMe 1,8 T | `nvme` | — | | Firmware del equipo | **UEFI** (`/sys/firmware/efi` presente) | — | — | ## 2. Lo que se escribió para esto (sin sellar) Cinco recetas nuevas y un perfil. Todas parsean y `takana hash` las acepta; el grafo de deps cierra. | receta | qué es | nota | |---|---|---| | `linux-firmware` | tarball 20260910 pineado (648 MB) | sha256 contrastado contra el `sha256sums.asc` de kernel.org | | `sof-firmware` | sof-bin v2025.12.2 | **no está en linux-firmware** — medido, 0 coincidencias | | `intel-ucode` | microcode-20260812 | tampoco: Intel lo publica aparte | | `firmware-tigerlake` | derivada: recorta ~25 MB de las tres familias de este metal | la única que viaja en la imagen | | `zsh` | el shell de login del usuario | seis parches de musl de Alpine | | `[perfil.metal-tigerlake]` | la capa de hardware, componible con cualquier escritorio | `firmware-tigerlake` + `intel-ucode` | Esto cierra el `TODO(soberanía)` que `scripts/metal-firmware.sh` llevaba desde que se escribió: hasta hoy el firmware se **copiaba de `/lib/firmware` del host**, o sea de la Artix que veníamos a reemplazar. Un instalador que sólo se puede construir desde la distro que viene a sustituir no es un instalador. ## 3. La tanda — qué construir, en orden > ✅ **HECHO y verificado el 2026-09-23.** Las cinco están selladas con el hash vigente en el store > del worker, y `firmware-tigerlake` re-selló el 2026-09-17 al ganar la dep `wireless-regdb` > (upstream sacó `regulatory.db` de `linux-firmware`). El perfil `metal-tigerlake` da **5/5** en > `docs/state/build-state.json`. Y el riesgo que este runbook anotaba **no se materializó**: `file` > sobre `bin/zsh` dice `statically linked`. Lo que sigue en esta sección queda como MÉTODO, para > cuando alguna de las cinco haya que rehacerla. El orden importa: `firmware-tigerlake` depende de las dos de firmware y falla ruidosa si no las encuentra materializadas. ```sh cd /opt/takana flock -o work/.farm-build.lock bash -c ' for r in linux-firmware sof-firmware intel-ucode firmware-tigerlake zsh; do ./target/release/takana --store ./store build "$r" || { echo "FALLÓ: $r"; break; } done' ``` `flock -o`, no `flock` a secas — CLAUDE.md §1, y está medido: el lock lo sostiene la descripción de fichero abierta y los nietos la heredan. ### Lo que puede salir mal, y cómo se ve - **`zsh` sale dinámico.** Es lo que le pasó a `bash`: si el `configure` mete `-rdynamic`, el wrapper de cc del lab tira el `-static` y el binario queda contra `libncursesw.so.6`, que ningún artefacto del corpus provee. Síntoma: `Error relocating ... symbol not found`. Arreglo conocido: `make LOCAL_LDFLAGS=`. **Comprobar con `file`: debe decir `statically linked`.** - **`linux-firmware` tarda y pesa.** 648 MB de fetch, ~2 GB extraídos. El worker tiene 71 G libres. - **`firmware-tigerlake` aborta con `0 blobs de SOF`.** Significa que `sof-firmware` no se materializó como dep. No ablandar la aserción: ese contador es la única defensa contra un `/lib/firmware` a medias, que no falla al construir y falla meses después en el metal como «no hay WiFi». ## 4. Muro 1 — CAE POR DECISIÓN: systemd-boot queda descartado, y no hacía falta > **Decisión del usuario, 2026-09-23: systemd-boot es systemd ⇒ NO.** Esa elección era circunstancial > — un crutch de bringup — y el arranque soberano ya existe. Lo que sigue corrige lo que este mismo > runbook recomendaba el 2026-09-16, que estaba mal orientado. Lo que decía el §4 viejo: «el instalador toma la rama EFI-stub y ésa es la que se cuelga; llevémoslo a systemd-boot». Al leer el instalador se ve que **la premisa estaba invertida**: - `scripts/takana-live-install.sh:275-283` — la rama UEFI es **EFI-stub SOBERANO**, ADR 0010 paso 5: el `bzImage` ES el binario EFI, se copia a `\EFI\BOOT\BOOTX64.EFI`, y la cmdline va **horneada** en `CONFIG_CMDLINE` porque la ruta fallback entrega `LoadOptions` VACÍO. Cierra en `:495` diciendo «sin GRUB ni systemd-boot». **No hay nada que migrar: ya es soberano.** - El cuelgue histórico en este metal **no era del EFI-stub**, era del **initrd de 100 MB** (ADR 0010 paso 2, y el «Measured initrd PCR 9» es justamente la medición TPM de ese initrd). El initramfs de hoy es un **pivote de ~770 K** (`findfs LABEL=hammer-root` → `switch_root`), y con él el EFI-stub directo está **validado VERDE en OVMF** (`scripts/efi-disk-boot-test.sh`). ⇒ **El muro 1 no es un muro de diseño: es una prueba que falta.** Nadie arrancó todavía el EFI-stub con el initrd chico **en este metal**. Lo que colgó en su día fue otra cosa, ya arreglada. `scripts/metal-usb-sdboot.sh` se queda donde está, con su rol reducido y dicho en voz alta: **herramienta de diagnóstico** (permite editar la cmdline en vivo con `e`, que es oro cuando algo no arranca), **nunca el camino de instalación**. Su `systemd-bootx64.efi` sale de un rootfs de Arch del home del usuario: binario ajeno, sin hash, fuera del store. ## 5. Muro 2 — el particionado es el trabajo de verdad: elegir, conservar, redimensionar > **Encargo explícito del usuario, 2026-09-23:** *«sí hay que poder no borrar particiones, elegir y > hasta redimensionar inteligentemente»*. Deja de ser una pregunta abierta y pasa a ser la unidad de > trabajo principal de este frente. Hoy el instalador hace lo contrario de eso, y conviene verlo exacto antes de cambiarlo (`takana-live-install.sh`, rama UEFI): | qué hace hoy | por qué no sirve para esta laptop | |---|---| | `busybox fdisk` con un guion de `o` (tabla NUEVA) + 4 primarias | **borra la tabla entera**; adiós a todo lo que haya | | tabla **MBR**, con la ESP como tipo `0xEF` | el NVMe es de 1,8 T — MBR direcciona hasta 2 T: el borde exacto del formato, sin razón para quedarse ahí | | root/store/estado en **ext2** | el disco instalado se merece ext4; es la misma `mke2fs` | | tamaños por variables `ROOT_MB`/`STORE_MB`, resto = estado | no hay forma de decir «usá ESTA partición y no toques las otras» | **Lo que ya está de nuestro lado**, y es más de lo que parece: - `takana boot entry survey` **ya lee el terreno sin tocarlo**: firmware, ESPs, espacio libre y **vecinos** (nombra un `\EFI\Microsoft` si lo hay). El instalador ya lo invoca antes de escribir. - `takana boot entry` completo —`list`/`add`/`backup`/`restore`/`reconcile`— escribe la NVRAM por `efivarfs` **sin `efibootmgr`** (`crates/takana-cli/src/efi_boot.rs`). Es lo que permite instalar **al lado** de otro sistema en vez de encima: entrada propia, `BootOrder` reconciliado, y un `backup` del estado de arranque ANTES de tocarlo. - Las herramientas de disco **ya están selladas en el corpus**, no hay que construir nada nuevo: | receta | versión | lo que aporta | |---|---|---| | `parted` | 3.7 | GPT, mover/crear particiones | | `e2fsprogs` | 1.47.4 | `resize2fs` (encoger ext4), `mke2fs -d` | | `util-linux` | 2.42.2 | `sfdisk` (editar la tabla sin recrearla), `lsblk`, `blkid`, `wipefs` | | `dosfstools` | 4.2 | la ESP | | `mtools` | 4.0.49 | poblar la ESP sin root ni loop | **Lo que falta, nombrado:** 1. ✅ **HECHO (2026-09-23) — `scripts/takana-install-partition.sh`.** Recibe `--root-part` y `--esp`, formatea **exactamente** esa partición, **no invoca fdisk/sfdisk/parted ni una vez**, escribe sólo en `\EFI\takana\` y registra la entrada NVRAM tras un `boot entry backup`. Puebla la ESP y el destino **sin montar nada** (`mcopy` y `mke2fs -d`): un `mount` mal apuntado en un disco ajeno es justo el accidente que este instalador existe para no tener. Verificado por `scripts/install-particion-test.sh` sobre un disco loop con vecinos reales —una ESP con Windows simulado y una partición con datos— comprobando **hashes, no mensajes**: la vecina queda idéntica byte a byte, la tabla no cambia, el `BOOTX64.EFI` ajeno no se pisa. Las tres guardas (disco entero, partición montada, destino = ESP) se prueban **disparando**, no por inspección. ⚠ **Dos hallazgos de la primera corrida, porque los dos parecen bugs del instalador y no lo son:** (a) **un FAT puede declarar más espacio del que su partición tiene** —`mformat` sobre un fichero con offset formatea con el tamaño del fichero entero, y cualquier ESP encogida sin reformatear queda igual— y **el `df` del kernel repite la mentira**, así que el guardián de capacidad contrasta contra el tamaño real del device y no contra el filesystem; (b) **montar un ext4 lo modifica** (superbloque), así que la huella de la vecina se toma después de las guardas: medirla antes daba un rojo que acusaba al inocente. 2. **GPT en vez de MBR** cuando la tabla es nueva (sigue pendiente en `takana-live-install.sh`): `sfdisk`/`parted` ya están; `busybox fdisk` sale. 3. **Encoger** (`resize2fs` + `sfdisk` reescribiendo sólo esa entrada). ⚠ **Límite físico, no de software: un filesystem montado NO se encoge** — ext4 sólo crece online. Encoger `/home` exige que esté desmontada ⇒ **hay que correr el instalador desde otro medio**, no desde la Artix viva. Eso no lo arregla ningún instalador: es la razón por la que el USB sigue haciendo falta. 4. **NTFS: no lo podemos redimensionar hoy.** No hay receta de `ntfs-3g`/`ntfsprogs` (⇒ sin `ntfsresize`) y los cuatro kernels traen `-d NTFS3_FS` deshabilitado. Para esta laptop da igual —es Artix— pero es un bloqueo duro para «instalar al lado de Windows» de cara al público. 5. **`backup` de la NVRAM y de la ESP antes de escribir**, ofrecido por defecto. Ya existe el subcomando; falta que el instalador lo llame. ## 5 bis. La política del «saltito»: qué pasa si el destino ES el sistema que corre > **Decisión del usuario, 2026-09-23:** *«que se utilice primero [kexec], y si falla por las > condiciones que dices, que use el método del espacio libre. Pero eso, cuando la partición elegida > sea la actual; si no, normal.»* Implementada en `--on-active-root` (`auto` por defecto). ``` destino = la raíz viva ? │ ├── no → flujo normal: formatear esa partición y nada más ← el §5.1 │ └── sí → 1. ¿puede esta máquina kexec? (takana kernel kexec --probe) │ ├── sí → instalador en RAM + salto. El sistema viejo muere en el salto │ ⇒ su disco queda libre ⇒ su raíz SÍ se puede formatear │ └── no → instalar AL LADO, en espacio sin asignar. La raíz viva no se toca ``` **Por qué no se puede «desde adentro» y sí «desde el salto».** Un filesystem montado y en uso no se formatea — eso lo impone el kernel, no una política nuestra. Pero se puede *salir* de adentro sin pasar por el firmware: `kexec_file_load` (ADR 0017 §1) carga otro kernel y salta. Lo que corre después es un **initramfs-instalador que vive en RAM** (`scripts/instalador-initramfs.sh`), y para él el disco entero es ajeno y está libre. **Las condiciones que pueden cerrar el camino, y cómo se ven:** | condición | cómo se comprueba | si falla | |---|---|---| | el kernel que corre implementa `kexec_file_load` | `takana kernel kexec --probe` | espacio libre | | somos root (`CAP_SYS_BOOT`) | idem | correr con `sudo` | | el kernel no está en *lockdown* | idem (lee `/sys/kernel/security/lockdown`) | Secure Boot: sólo acepta kernels firmados ⇒ espacio libre | | el payload entra en la RAM | lo mide `instalador-initramfs.sh` (aborta >40 %) | instalar desde un medio | ⚠ **El sondeo no es concluyente sin root, y esto está medido sobre el MISMO kernel (2026-09-23):** como uid 1001 dijo «el syscall existe» y como root dijo `ENOSYS`. El kernel contesta `EPERM` antes de llegar a decir si implementa la llamada. Un «sólo te falta ser root» puede estar tapando un «y además tu kernel no lo trae» ⇒ **repetir el probe como root antes de creerle**. En la laptop hay que medir dos cosas, y son dos comandos: ```sh sudo takana kernel kexec --probe # el veredicto completo cat /sys/kernel/security/lockdown # [none] = Secure Boot no está estorbando ``` ## 5 ter. ⚠ Los nombres `/dev/nvme1n1p6` NO son estables — se direcciona por PARTUUID **Medido el 2026-09-23 en QEMU con dos NVMe**, replicando el layout real de la laptop (un disco con Windows, otro con ESP + `/home` + la raíz de Artix en los slots 1, 3 y 6): - el disco declarado **primero** en la línea de comandos apareció como `nvme1n1` y el segundo como `nvme0n1` — cruzados; - y **entre dos arranques del mismo QEMU se volvieron a cruzar**: en una corrida la raíz de Artix fue `nvme0n1p6` y en la siguiente `nvme1n1p6`. Los nombres salen del orden de sondeo del bus, no del orden físico ni del que muestra `lsblk` hoy. Para un instalador eso no es una molestia: es la diferencia entre formatear la raíz vieja y formatear el disco de Windows. Y **el salto por kexec lo agrava**, porque quien enumera después del salto es un kernel distinto del que te dio los nombres antes. ⇒ `takana-install-partition.sh` acepta `PARTUUID=`, `UUID=` y `LABEL=` además de un `/dev/…`, y **la forma correcta de nombrar el destino es `PARTUUID=`**: es el identificador de la TABLA GPT y **no cambia al formatear la partición**. El `UUID=` es del filesystem y muere en el formateo. ```sh lsblk -o NAME,SIZE,LABEL,PARTUUID /dev/nvme1n1 # de dónde salen ``` La prueba con el layout replicado terminó así, verificado por hashes y no por la salida del propio instalador: | qué | resultado | |---|---| | disco entero de Windows | **idéntico byte a byte** (8 GiB comparados) | | `/home` | idéntica | | la raíz de Artix (p6) | reemplazada: `LABEL=hammer-root` en el superbloque | | `\EFI\grub\` y `\EFI\BOOT\` de la ESP de Artix | intactos | | `\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 **instalador standalone** que corra desde Windows o desde otro Linux. La respuesta se parte en dos, y son muy distintas. ### Desde otro Linux (incluida la Artix que está instalada): casi todo existe `target/release/takana` es un **ELF estático** (`statically linked`, musl): se copia a cualquier Linux x86-64 y corre **sin instalar nada, sin deps, sin root de esa distro más que para el disco**. Eso ya es un instalador standalone; lo que falta es el envoltorio. Las piezas: | pieza | estado | |---|---| | 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` | | elegir partición / redimensionar | ❌ el §5 | Caso de uso inmediato: instalar takana **en una partición libre** desde la Artix corriendo, y reiniciar eligiendo en el firmware. Sin USB y sin viaje físico. El límite es el del §5.3: lo que no se puede es tocar la partición que estás usando. ### Desde Windows: posible, pero es un proyecto propio Tres caminos, de menor a mayor honestidad sobre lo que cuestan: 1. **WSL2** — corre el binario estático tal cual, y `wsl --mount --bare` le da el disco crudo. Pero WSL2 es una VM **sin firmware UEFI real: no hay `efivarfs`**, así que la entrada de arranque no se puede escribir desde ahí; habría que crearla desde Windows con `bcdedit` (que sí sabe añadir una *firmware application*). Es el camino barato y deja una costura fea. 2. **Un ejecutable Windows nativo** — Rust compila a `windows-msvc`, pero toda la capa de disco y de NVRAM es Linux-específica: habría que reescribirla sobre `\\.\PhysicalDrive` y `SetFirmwareEnvironmentVariable`. Es *el* instalador standalone de verdad, y es su propia unidad de trabajo — no una sesión. 3. **Fichero-imagen en NTFS al estilo Wubi** (no tocar particiones en absoluto) — **bloqueado hoy**: el kernel se construye con `-d NTFS3_FS`, así que el pivote no podría montar el contenedor. Pide rebuild del kernel y un initramfs que sepa hacer loop. Para **esta** laptop no hace falta nada de Windows: el sistema que hay es Linux. Windows entra cuando el objetivo sea que lo instale **otra gente** (SDD 19, lanzamiento público). ## 7. Orden de trabajo 1. **Hidratar el hub.** Las cinco del §3 están selladas con el hash vigente **en el worker**, no en el `/store` de esta caja (acá quedan versiones viejas, p. ej. `14789737…-firmware-tigerlake`). Sin eso no se arma ninguna imagen desde el hub. 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: 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. ## 8. Lo que este runbook NO resuelve - **La laptop sigue sin poder hidratar.** Su `.dev-fs/alpine` tiene 104 paquetes contra los 110 de `lab-toolchain.lock`, así que calcula `ArtifactHash` distintos y no encuentra nada del store compartido. Hasta que `farm-lab-sync.sh` corra hacia allá, todo pasa por el worker. - **`kitty`, el terminal del usuario, no tiene receta.** `foot` sí está sellada y es el terminal Wayland del proyecto; sirve de puente, pero no es lo que él usa. - **Los escritorios en metal con aceleración.** Distinto problema del firmware: es mesa con el driver Iris real en vez de llvmpipe.