1# Runbook — el instalador para la laptop del usuario (TigerLake), y los dos muros que tiene delante
2
3> **Estado: ABIERTO — pero el §3 ya está PAGADO.** Las recetas se escribieron el 2026-09-16 desde el
4> propio laptop —que no tiene lab ni store— así que nacieron sin sellar; **el 2026-09-23 se verificó
5> que las cinco están selladas con su hash VIGENTE** (ver §3). Lo que queda abierto es el arranque en
6> el metal y, sobre todo, **el instalador no destructivo del §5**, que pasó a ser el trabajo central
7> del frente por decisión del usuario (2026-09-23). El §4 se REESCRIBIÓ: systemd-boot queda
8> descartado y no hacía falta.
9> Hermano de [`kde-qemu-desktop.md`](kde-qemu-desktop.md) y [`cosmic-desktop.md`](cosmic-desktop.md),
10> de los que reusa todo el andamiaje.
11
12## 0. Qué se quiere
13
14Un medio que arranque en la laptop del usuario e **instale takana reemplazando la Artix** que hay
15hoy. No un USB de pruebas —eso ya existe con `mirada-usb.sh`— sino una instalación a disco.
16
17## 1. La máquina, medida
18
19`lspci -nn`, `/proc/cpuinfo` y `lsblk` sobre el metal real, 2026-09-16:
20
21| pieza | qué es | driver | firmware |
22|---|---|---|---|
23| CPU | i7-11370H · family 6 · model 140 (0x8c) · stepping 1 | — | `intel-ucode/06-8c-01` |
24| GPU | TigerLake-LP GT2 [Iris Xe] `[8086:9a49]` | `i915` / `xe` | `i915/tgl_{dmc,guc,huc}*` |
25| WiFi | Intel Wi-Fi 6 AX201 `[8086:a0f0]` | `iwlwifi` | `iwlwifi-{QuZ-a0-hr-b0,cc-a0}-*.ucode` |
26| Audio | 500 Series HD Audio `[8086:a0c8]` | `snd_sof_pci_intel_tgl` | `intel/sof/sof-tgl.ri` |
27| Disco | NVMe 1,8 T | `nvme` | — |
28| Firmware del equipo | **UEFI** (`/sys/firmware/efi` presente) | — | — |
29
30## 2. Lo que se escribió para esto (sin sellar)
31
32Cinco recetas nuevas y un perfil. Todas parsean y `takana hash` las acepta; el grafo de deps cierra.
33
34| receta | qué es | nota |
35|---|---|---|
36| `linux-firmware` | tarball 20260910 pineado (648 MB) | sha256 contrastado contra el `sha256sums.asc` de kernel.org |
37| `sof-firmware` | sof-bin v2025.12.2 | **no está en linux-firmware** — medido, 0 coincidencias |
38| `intel-ucode` | microcode-20260812 | tampoco: Intel lo publica aparte |
39| `firmware-tigerlake` | derivada: recorta ~25 MB de las tres familias de este metal | la única que viaja en la imagen |
40| `zsh` | el shell de login del usuario | seis parches de musl de Alpine |
41| `[perfil.metal-tigerlake]` | la capa de hardware, componible con cualquier escritorio | `firmware-tigerlake` + `intel-ucode` |
42
43Esto cierra el `TODO(soberanía)` que `scripts/metal-firmware.sh` llevaba desde que se escribió: hasta
44hoy el firmware se **copiaba de `/lib/firmware` del host**, o sea de la Artix que veníamos a
45reemplazar. Un instalador que sólo se puede construir desde la distro que viene a sustituir no es un
46instalador.
47
48## 3. La tanda — qué construir, en orden
49
50> ✅ **HECHO y verificado el 2026-09-23.** Las cinco están selladas con el hash vigente en el store
51> del worker, y `firmware-tigerlake` re-selló el 2026-09-17 al ganar la dep `wireless-regdb`
52> (upstream sacó `regulatory.db` de `linux-firmware`). El perfil `metal-tigerlake` da **5/5** en
53> `docs/state/build-state.json`. Y el riesgo que este runbook anotaba **no se materializó**: `file`
54> sobre `bin/zsh` dice `statically linked`. Lo que sigue en esta sección queda como MÉTODO, para
55> cuando alguna de las cinco haya que rehacerla.
56
57El orden importa: `firmware-tigerlake` depende de las dos de firmware y falla ruidosa si no las
58encuentra materializadas.
59
60```sh
61cd /opt/takana
62flock -o work/.farm-build.lock bash -c '
63 for r in linux-firmware sof-firmware intel-ucode firmware-tigerlake zsh; do
64 ./target/release/takana --store ./store build "$r" || { echo "FALLÓ: $r"; break; }
65 done'
66```
67
68`flock -o`, no `flock` a secas — CLAUDE.md §1, y está medido: el lock lo sostiene la descripción de
69fichero abierta y los nietos la heredan.
70
71### Lo que puede salir mal, y cómo se ve
72
73- **`zsh` sale dinámico.** Es lo que le pasó a `bash`: si el `configure` mete `-rdynamic`, el wrapper
74 de cc del lab tira el `-static` y el binario queda contra `libncursesw.so.6`, que ningún artefacto
75 del corpus provee. Síntoma: `Error relocating ... symbol not found`. Arreglo conocido:
76 `make LOCAL_LDFLAGS=`. **Comprobar con `file`: debe decir `statically linked`.**
77- **`linux-firmware` tarda y pesa.** 648 MB de fetch, ~2 GB extraídos. El worker tiene 71 G libres.
78- **`firmware-tigerlake` aborta con `0 blobs de SOF`.** Significa que `sof-firmware` no se materializó
79 como dep. No ablandar la aserción: ese contador es la única defensa contra un `/lib/firmware` a
80 medias, que no falla al construir y falla meses después en el metal como «no hay WiFi».
81
82## 4. Muro 1 — CAE POR DECISIÓN: systemd-boot queda descartado, y no hacía falta
83
84> **Decisión del usuario, 2026-09-23: systemd-boot es systemd ⇒ NO.** Esa elección era circunstancial
85> — un crutch de bringup — y el arranque soberano ya existe. Lo que sigue corrige lo que este mismo
86> runbook recomendaba el 2026-09-16, que estaba mal orientado.
87
88Lo que decía el §4 viejo: «el instalador toma la rama EFI-stub y ésa es la que se cuelga; llevémoslo
89a systemd-boot». Al leer el instalador se ve que **la premisa estaba invertida**:
90
91- `scripts/takana-live-install.sh:275-283` — la rama UEFI es **EFI-stub SOBERANO**, ADR 0010 paso 5:
92 el `bzImage` ES el binario EFI, se copia a `\EFI\BOOT\BOOTX64.EFI`, y la cmdline va **horneada**
93 en `CONFIG_CMDLINE` porque la ruta fallback entrega `LoadOptions` VACÍO. Cierra en `:495` diciendo
94 «sin GRUB ni systemd-boot». **No hay nada que migrar: ya es soberano.**
95- El cuelgue histórico en este metal **no era del EFI-stub**, era del **initrd de 100 MB**
96 (ADR 0010 paso 2, y el «Measured initrd PCR 9» es justamente la medición TPM de ese initrd). El
97 initramfs de hoy es un **pivote de ~770 K** (`findfs LABEL=hammer-root` → `switch_root`), y con él
98 el EFI-stub directo está **validado VERDE en OVMF** (`scripts/efi-disk-boot-test.sh`).
99
100⇒ **El muro 1 no es un muro de diseño: es una prueba que falta.** Nadie arrancó todavía el EFI-stub
101con el initrd chico **en este metal**. Lo que colgó en su día fue otra cosa, ya arreglada.
102
103`scripts/metal-usb-sdboot.sh` se queda donde está, con su rol reducido y dicho en voz alta:
104**herramienta de diagnóstico** (permite editar la cmdline en vivo con `e`, que es oro cuando algo no
105arranca), **nunca el camino de instalación**. Su `systemd-bootx64.efi` sale de un rootfs de Arch del
106home del usuario: binario ajeno, sin hash, fuera del store.
107
108## 5. Muro 2 — el particionado es el trabajo de verdad: elegir, conservar, redimensionar
109
110> **Encargo explícito del usuario, 2026-09-23:** *«sí hay que poder no borrar particiones, elegir y
111> hasta redimensionar inteligentemente»*. Deja de ser una pregunta abierta y pasa a ser la unidad de
112> trabajo principal de este frente.
113
114Hoy el instalador hace lo contrario de eso, y conviene verlo exacto antes de cambiarlo
115(`takana-live-install.sh`, rama UEFI):
116
117| qué hace hoy | por qué no sirve para esta laptop |
118|---|---|
119| `busybox fdisk` con un guion de `o` (tabla NUEVA) + 4 primarias | **borra la tabla entera**; adiós a todo lo que haya |
120| 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í |
121| root/store/estado en **ext2** | el disco instalado se merece ext4; es la misma `mke2fs` |
122| tamaños por variables `ROOT_MB`/`STORE_MB`, resto = estado | no hay forma de decir «usá ESTA partición y no toques las otras» |
123
124**Lo que ya está de nuestro lado**, y es más de lo que parece:
125
126- `takana boot entry survey` **ya lee el terreno sin tocarlo**: firmware, ESPs, espacio libre y
127 **vecinos** (nombra un `\EFI\Microsoft` si lo hay). El instalador ya lo invoca antes de escribir.
128- `takana boot entry` completo —`list`/`add`/`backup`/`restore`/`reconcile`— escribe la NVRAM por
129 `efivarfs` **sin `efibootmgr`** (`crates/takana-cli/src/efi_boot.rs`). Es lo que permite instalar
130 **al lado** de otro sistema en vez de encima: entrada propia, `BootOrder` reconciliado, y un
131 `backup` del estado de arranque ANTES de tocarlo.
132- Las herramientas de disco **ya están selladas en el corpus**, no hay que construir nada nuevo:
133
134 | receta | versión | lo que aporta |
135 |---|---|---|
136 | `parted` | 3.7 | GPT, mover/crear particiones |
137 | `e2fsprogs` | 1.47.4 | `resize2fs` (encoger ext4), `mke2fs -d` |
138 | `util-linux` | 2.42.2 | `sfdisk` (editar la tabla sin recrearla), `lsblk`, `blkid`, `wipefs` |
139 | `dosfstools` | 4.2 | la ESP |
140 | `mtools` | 4.0.49 | poblar la ESP sin root ni loop |
141
142**Lo que falta, nombrado:**
143
1441. ✅ **HECHO (2026-09-23) — `scripts/takana-install-partition.sh`.** Recibe `--root-part` y
145 `--esp`, formatea **exactamente** esa partición, **no invoca fdisk/sfdisk/parted ni una vez**,
146 escribe sólo en `\EFI\takana\` y registra la entrada NVRAM tras un `boot entry backup`.
147 Puebla la ESP y el destino **sin montar nada** (`mcopy` y `mke2fs -d`): un `mount` mal apuntado
148 en un disco ajeno es justo el accidente que este instalador existe para no tener. Verificado por
149 `scripts/install-particion-test.sh` sobre un disco loop con vecinos reales —una ESP con Windows
150 simulado y una partición con datos— comprobando **hashes, no mensajes**: la vecina queda idéntica
151 byte a byte, la tabla no cambia, el `BOOTX64.EFI` ajeno no se pisa. Las tres guardas (disco
152 entero, partición montada, destino = ESP) se prueban **disparando**, no por inspección.
153
154 ⚠ **Dos hallazgos de la primera corrida, porque los dos parecen bugs del instalador y no lo son:**
155 (a) **un FAT puede declarar más espacio del que su partición tiene** —`mformat` sobre un fichero
156 con offset formatea con el tamaño del fichero entero, y cualquier ESP encogida sin reformatear
157 queda igual— y **el `df` del kernel repite la mentira**, así que el guardián de capacidad contrasta
158 contra el tamaño real del device y no contra el filesystem; (b) **montar un ext4 lo modifica**
159 (superbloque), así que la huella de la vecina se toma después de las guardas: medirla antes daba
160 un rojo que acusaba al inocente.
1612. **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.
1623. **Encoger** (`resize2fs` + `sfdisk` reescribiendo sólo esa entrada). ⚠ **Límite físico, no de
163 software: un filesystem montado NO se encoge** — ext4 sólo crece online. Encoger `/home` exige
164 que esté desmontada ⇒ **hay que correr el instalador desde otro medio**, no desde la Artix viva.
165 Eso no lo arregla ningún instalador: es la razón por la que el USB sigue haciendo falta.
1664. **NTFS: no lo podemos redimensionar hoy.** No hay receta de `ntfs-3g`/`ntfsprogs` (⇒ sin
167 `ntfsresize`) y los cuatro kernels traen `-d NTFS3_FS` deshabilitado. Para esta laptop da igual
168 —es Artix— pero es un bloqueo duro para «instalar al lado de Windows» de cara al público.
1695. **`backup` de la NVRAM y de la ESP antes de escribir**, ofrecido por defecto. Ya existe el
170 subcomando; falta que el instalador lo llame.
171
172## 5 bis. La política del «saltito»: qué pasa si el destino ES el sistema que corre
173
174> **Decisión del usuario, 2026-09-23:** *«que se utilice primero [kexec], y si falla por las
175> condiciones que dices, que use el método del espacio libre. Pero eso, cuando la partición elegida
176> sea la actual; si no, normal.»* Implementada en `--on-active-root` (`auto` por defecto).
177
178```
179 destino = la raíz viva ?
180 │
181 ├── no → flujo normal: formatear esa partición y nada más ← el §5.1
182 │
183 └── sí → 1. ¿puede esta máquina kexec? (takana kernel kexec --probe)
184 │
185 ├── sí → instalador en RAM + salto. El sistema viejo muere en el salto
186 │ ⇒ su disco queda libre ⇒ su raíz SÍ se puede formatear
187 │
188 └── no → instalar AL LADO, en espacio sin asignar. La raíz viva no se toca
189```
190
191**Por qué no se puede «desde adentro» y sí «desde el salto».** Un filesystem montado y en uso no se
192formatea — eso lo impone el kernel, no una política nuestra. Pero se puede *salir* de adentro sin
193pasar por el firmware: `kexec_file_load` (ADR 0017 §1) carga otro kernel y salta. Lo que corre
194después es un **initramfs-instalador que vive en RAM** (`scripts/instalador-initramfs.sh`), y para él
195el disco entero es ajeno y está libre.
196
197**Las condiciones que pueden cerrar el camino, y cómo se ven:**
198
199| condición | cómo se comprueba | si falla |
200|---|---|---|
201| el kernel que corre implementa `kexec_file_load` | `takana kernel kexec --probe` | espacio libre |
202| somos root (`CAP_SYS_BOOT`) | idem | correr con `sudo` |
203| el kernel no está en *lockdown* | idem (lee `/sys/kernel/security/lockdown`) | Secure Boot: sólo acepta kernels firmados ⇒ espacio libre |
204| el payload entra en la RAM | lo mide `instalador-initramfs.sh` (aborta >40 %) | instalar desde un medio |
205
206⚠ **El sondeo no es concluyente sin root, y esto está medido sobre el MISMO kernel (2026-09-23):**
207como uid 1001 dijo «el syscall existe» y como root dijo `ENOSYS`. El kernel contesta `EPERM` antes de
208llegar a decir si implementa la llamada. Un «sólo te falta ser root» puede estar tapando un «y además
209tu kernel no lo trae» ⇒ **repetir el probe como root antes de creerle**.
210
211En la laptop hay que medir dos cosas, y son dos comandos:
212
213```sh
214sudo takana kernel kexec --probe # el veredicto completo
215cat /sys/kernel/security/lockdown # [none] = Secure Boot no está estorbando
216```
217
218## 5 ter. ⚠ Los nombres `/dev/nvme1n1p6` NO son estables — se direcciona por PARTUUID
219
220**Medido el 2026-09-23 en QEMU con dos NVMe**, replicando el layout real de la laptop (un disco con
221Windows, otro con ESP + `/home` + la raíz de Artix en los slots 1, 3 y 6):
222
223- el disco declarado **primero** en la línea de comandos apareció como `nvme1n1` y el segundo como
224 `nvme0n1` — cruzados;
225- y **entre dos arranques del mismo QEMU se volvieron a cruzar**: en una corrida la raíz de Artix
226 fue `nvme0n1p6` y en la siguiente `nvme1n1p6`.
227
228Los nombres salen del orden de sondeo del bus, no del orden físico ni del que muestra `lsblk` hoy.
229Para un instalador eso no es una molestia: es la diferencia entre formatear la raíz vieja y
230formatear el disco de Windows. Y **el salto por kexec lo agrava**, porque quien enumera después del
231salto es un kernel distinto del que te dio los nombres antes.
232
233⇒ `takana-install-partition.sh` acepta `PARTUUID=`, `UUID=` y `LABEL=` además de un `/dev/…`, y
234**la forma correcta de nombrar el destino es `PARTUUID=`**: es el identificador de la TABLA GPT y
235**no cambia al formatear la partición**. El `UUID=` es del filesystem y muere en el formateo.
236
237```sh
238lsblk -o NAME,SIZE,LABEL,PARTUUID /dev/nvme1n1 # de dónde salen
239```
240
241La prueba con el layout replicado terminó así, verificado por hashes y no por la salida del propio
242instalador:
243
244| qué | resultado |
245|---|---|
246| disco entero de Windows | **idéntico byte a byte** (8 GiB comparados) |
247| `/home` | idéntica |
248| la raíz de Artix (p6) | reemplazada: `LABEL=hammer-root` en el superbloque |
249| `\EFI\grub\` y `\EFI\BOOT\` de la ESP de Artix | intactos |
250| `\EFI\takana\takanax64.efi` | = el bzImage, hash exacto |
251| tabla de particiones | 3 entradas, sin cambios |
252
253## 5 quater. La superficie de usuario: `takana metal` (2026-09-23)
254
255El instalador estuvo funcionando un día entero **sin poder usarse**, y el usuario lo dijo mejor que
256nadie: *«pero esos son comandos a lo poweruser»*. La forma de invocarlo era `takana kernel kexec
257./kernel --initrd … --cmdline '…' --jump`: el motor con la tapa abierta. Quien lo teclea tiene que
258saber qué es kexec, que el payload va por `--initrd`, que sin `--jump` sólo se carga y qué consola
259pedirle al kernel nuevo — y **ninguna de esas cuatro cosas es una decisión del que instala**.
260
261La tapa es [ADR 0021](../adr/0021-dueno-del-verbo-de-instalar.md), y son tres verbos:
262
263```sh
264sudo takana metal check # ¿se puede sin USB? No toca nada.
265sudo takana metal survey # ¿qué hay en los discos? No toca nada. (--json = el contrato)
266sudo takana metal install # comprueba, avisa y salta.
267```
268
269`survey` **no es un censo nuevo**: llama al mismo `takana-install-partition.sh --survey` del §5. Esa
270es la parte que no se negocia — el censo es lo que decide qué NO se toca, y dos censos que divergen
271no muestran de menos: formatean de más.
272
273Al ponerle la cara aparecieron tres defectos que eran del motor, no de la cara, y los tres sólo se
274ven desde afuera:
275
276| defecto | cómo se veía |
277|---|---|
278| `--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** |
279| `--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 |
280| 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 |
281
282### El salto ya no es una promesa (2026-09-23)
283
284`scripts/metal-salto-test.sh` ejecuta `kexec_file_load` **de verdad**, en una VM con
285`linux-metal-kexec` (el único kernel del corpus con `CONFIG_KEXEC_FILE`). Once comprobaciones en
286verde, y salen del disco: el origen muere en el salto, el instalador arranca en RAM, formatea sólo
287su destino, deja la ESP con `\EFI\takana\takanax64.efi` y **el `bootmgfw.efi` del vecino byte a
288byte igual**. La tabla de particiones no cambia.
289
290⇒ Cuando esto se corra en la laptop, el salto ya no es el paso sin probar. Lo que sigue sin
291ejercitarse en metal es el **firmware** (§3) y el menú interactivo con teclado real.
292
293## 6. Instalar desde OTRO sistema operativo, sin medio live
294
295La pregunta del usuario (2026-09-23) es si se puede romper con la instalación tradicional y tener un
296**instalador standalone** que corra desde Windows o desde otro Linux. La respuesta se parte en dos, y
297son muy distintas.
298
299### Desde otro Linux (incluida la Artix que está instalada): casi todo existe
300
301`target/release/takana` es un **ELF estático** (`statically linked`, musl): se copia a cualquier
302Linux x86-64 y corre **sin instalar nada, sin deps, sin root de esa distro más que para el disco**.
303Eso ya es un instalador standalone; lo que falta es el envoltorio. Las piezas:
304
305| pieza | estado |
306|---|---|
307| leer el terreno | ✅ `takana metal survey` (y `takana boot entry survey` para la NVRAM) |
308| decidir si se puede sin USB | ✅ `takana metal check` |
309| el viaje entero, con una sola orden | ✅ `takana metal install` (§5 quater) |
310| escribir la entrada de arranque | ✅ `takana boot entry add` (efivarfs, sin efibootmgr) |
311| deshacer si algo sale mal | ✅ `takana boot entry backup` / `restore` |
312| traer los bits | ✅ `takana mirror` (por hash, verificando `of_tree`) y `takana hydrate` |
313| elegir partición / redimensionar | ❌ el §5 |
314
315Caso de uso inmediato: instalar takana **en una partición libre** desde la Artix corriendo, y
316reiniciar eligiendo en el firmware. Sin USB y sin viaje físico. El límite es el del §5.3: lo que no
317se puede es tocar la partición que estás usando.
318
319### Desde Windows: posible, pero es un proyecto propio
320
321Tres caminos, de menor a mayor honestidad sobre lo que cuestan:
322
3231. **WSL2** — corre el binario estático tal cual, y `wsl --mount --bare` le da el disco crudo. Pero
324 WSL2 es una VM **sin firmware UEFI real: no hay `efivarfs`**, así que la entrada de arranque no
325 se puede escribir desde ahí; habría que crearla desde Windows con `bcdedit` (que sí sabe añadir
326 una *firmware application*). Es el camino barato y deja una costura fea.
3272. **Un ejecutable Windows nativo** — Rust compila a `windows-msvc`, pero toda la capa de disco y de
328 NVRAM es Linux-específica: habría que reescribirla sobre `\\.\PhysicalDrive` y
329 `SetFirmwareEnvironmentVariable`. Es *el* instalador standalone de verdad, y es su propia unidad
330 de trabajo — no una sesión.
3313. **Fichero-imagen en NTFS al estilo Wubi** (no tocar particiones en absoluto) — **bloqueado hoy**:
332 el kernel se construye con `-d NTFS3_FS`, así que el pivote no podría montar el contenedor. Pide
333 rebuild del kernel y un initramfs que sepa hacer loop.
334
335Para **esta** laptop no hace falta nada de Windows: el sistema que hay es Linux. Windows entra
336cuando el objetivo sea que lo instale **otra gente** (SDD 19, lanzamiento público).
337
338## 7. Orden de trabajo
339
3401. **Hidratar el hub.** Las cinco del §3 están selladas con el hash vigente **en el worker**, no en
341 el `/store` de esta caja (acá quedan versiones viejas, p. ej. `14789737…-firmware-tigerlake`).
342 Sin eso no se arma ninguna imagen desde el hub.
3432. **USB de prueba y arrancarlo en el metal.** Lo que se valida NO es el escritorio: es **el
344 firmware** —que asocie el WiFi, que el display tome el DMC, que levante el DSP de audio— y, con
345 el mismo viaje, **el EFI-stub soberano con el initrd chico** (§4), que es la prueba que falta.
3463. **El instalador no destructivo** del §5: el modo «partición destino» y el «al lado» están
347 cerrados y medidos (§5 quater y el arnés del salto); falta el **redimensionado** (§5.3).
3484. **Recién entonces** tocar el disco de la laptop.
3495. Y después, los escritorios con GPU real: hoy los cuatro corren por llvmpipe, que en QEMU es
350 correcto y en una Iris Xe es tirar la GPU a la basura.
351
352## 8. Lo que este runbook NO resuelve
353
354- **La laptop sigue sin poder hidratar.** Su `.dev-fs/alpine` tiene 104 paquetes contra los 110 de
355 `lab-toolchain.lock`, así que calcula `ArtifactHash` distintos y no encuentra nada del store
356 compartido. Hasta que `farm-lab-sync.sh` corra hacia allá, todo pasa por el worker.
357- **`kitty`, el terminal del usuario, no tiene receta.** `foot` sí está sellada y es el terminal
358 Wayland del proyecto; sirve de puente, pero no es lo que él usa.
359- **Los escritorios en metal con aceleración.** Distinto problema del firmware: es mesa con el
360 driver Iris real en vez de llvmpipe.
instalador-laptop-tigerlake.md
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 y 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.
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:
-
✅ 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.
-
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.
-
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.
-
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.
-
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:
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.
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, y son tres verbos:
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:
- 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.
- 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.
- 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
- 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.
- 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.
- 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).
- Recién entonces tocar el disco de la laptop.
- 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.