atuq: la bóveda entró a las imágenes y NADIE puede abrirla — y al navegador le servía una TEMPORAL

Seguir con lo que el §7.decies dejó abierto —quién levanta la app, de dónde sale la raíz de las
claves— terminó en una respuesta incómoda y en un bug que valía la pena encontrar.

La cadena, medida hacia atrás desde la app:

· `raiz_de_identidad()` saca la seed del llavero del KERNEL (`pacha_llavero::SEED_IDENTIDAD`);
· la escriben dos lugares en todo tawasuyu: `agora-cli identity unlock` y `churay-welcome-runner`;
· `agora-cli` está sellado y en `perfiles: []` — en ninguna imagen. Y aunque se declarara, su pin es
  del 2026-06-18, donde no existen ni `SEED_IDENTIDAD` ni `desbloquear` (git grep sobre el pin, con
  HEAD de control: 4 y 1 aciertos);
· `churay-welcome` no tiene receta en takana.

⇒ en ninguna imagen hay un binario capaz de sembrar la identidad. No es que el usuario no la
desbloqueó: es que no tiene con qué.

Y lo que el navegador veía NO era «cerrada». Cuando `abrir()` falla, la app levantaba el socket
igual, sobre `bóveda_imposible()` —un sled en /tmp/boveda-sin-abrir-<pid>—, así que la extensión
recibía `locked:false, count:0` y un `vault.save` consentido guardaba en un temporal que muere con
el proceso. La ventana decía la verdad; el navegador, lo contrario. Arreglado en tawasuyu
(`7917fbb96`) con test de regresión en los dos sentidos, y probado al revés: con el `if`
neutralizado, el test falla con el mensaje que corresponde.

Pin de las dos recetas a `6f0408d40`, que trae además el `Cargo.lock` cerrado. Ahí apareció la
vuelta que no estaba escrita: la «operación mínima» del §7.octies (`cargo metadata` sin `--locked`)
corrida en una jaula con el registro de cargo INCOMPLETO devuelve 0 y deja un lock al que le faltan
dos miembros del workspace — y su diff se ve MÁS mínimo que el correcto (−15 líneas contra +1). Que
diera byte a byte igual al de otra sesión no lo confirmaba: las dos se calcularon con el mismo
instrumento roto.

Y una trampa más, medida hoy: la guarda de receta del §7.quinquies.bis NO alcanza para una TANDA.
Puesta una vez al principio, `shuma-pregunta` selló bien y `boveda` —once minutos después— dio
«artefacto ya en el store» sobre la receta VIEJA, porque el latido revierte el árbol del worker en
el medio. La guarda va pegada a CADA build, y la cura de fondo es publicar la receta antes de
construir.

De paso, dos correcciones a lo que este mismo frente escribió hace unas horas:
· el `app_id`: llimphi SÍ lo pone (`with_name`, con el nombre del ejecutable de piso) ⇒ la ventana
  es `boveda`, igual que el basename del `.desktop`, y por eso no hace falta `StartupWMClass`. Lo
  que sí queda mal es el TÍTULO, que dice «llimphi»;
· el GL: que las imágenes sean iris-only no es una decisión pendiente sino una escrita (SDD 14), y
  el caso de la VM ya tiene camino — los runbooks montan `mesa-llvmpipe` como capa.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sergio
2026-09-21 03:41:14 +00:00
co-authored by Claude Opus 5
parent c33de60012
commit 557e127f6f
3 changed files with 235 additions and 45 deletions
+182 -15
View File
@@ -3363,13 +3363,15 @@ Entra `boveda.desktop` en la fase `install` de la receta. Tres cosas se midieron
dos formas que callan ese hint lo cambian por uno peor** —medido: `Utility;Security;System;` y
`Utility;Security;Settings;` traen dos categorías principales ⇒ «application might appear more
than once in the application menu»—. Se queda como está, que es además lo que usa KeePassXC;
- **⚠ y la ventana NO se va a poder emparejar con el lanzador, y no es de acá.** `llimphi_ui::run`
no llama nunca a `with_name`, y winit 0.30.13 sólo manda `set_app_id` `if let Some(name) =
attributes.platform_specific.name` ⇒ **la ventana sale sin `app_id`**, y con el título `"llimphi"`,
que es el default de `App::title` y `BovedaApp` no sobrescribe. Por eso el `.desktop` no lleva
`StartupWMClass`: no habría contra qué emparejarlo. Vale para **toda** app llimphi, no para ésta:
el dock las ve a todas iguales y sin nombre. Es de llimphi —como el muro del §7.septies— y se
arregla allá.
- **el `app_id` está, y el `.desktop` no necesita `StartupWMClass`.** `llimphi_ui` arma la ventana
con `with_name` —el `app_id` del xdg-toplevel en Wayland—: toma el `App::app_id()` que la app
declare y, si no declara ninguna (el caso de `BovedaApp`), cae al **nombre del ejecutable**, que
es el piso que llimphi se puso el día que 163 de sus 178 apps compartían el genérico del
compositor. ⇒ `app_id = "boveda"`, el mismo basename que el `.desktop`, y el emparejamiento
estándar ya funciona. Corroborado en metal: el guardián espera `'"app_id": *"boveda"'` en el
árbol de sway y lo encuentra a los 12-13 s. ⚠ Lo que sí queda mal es el **título**: `App::title()`
devuelve `"llimphi"` por defecto y `BovedaApp` no lo sobrescribe, así que la barra y el conmutador
dicen «llimphi». Es de la app, y es su propia unidad.
El `.desktop` mueve el hash de la receta: `b3:b0c6adc4` ⇒ `b3:3f1072cc`, reconstruida en el worker
con la guarda del §7.quinquies puesta (`### receta verificada 3f1072cc…` antes de compilar nada,
@@ -3410,13 +3412,178 @@ uno que funciona. Probado en los dos sentidos: los cuatro perfiles en verde, y e
pero nadie decidió todavía si debe autoarrancar con la sesión. No se decide de paso: autoarrancar
la ata a que la identidad del llavero esté desbloqueada en el login (la decisión 2 del §7.sexies,
que sigue abierta);
2. **el único proveedor de GL de las cuatro imágenes es `iris` (Intel).** `mesa-llvmpipe` no está en
ningún perfil y `mesa-swrast` sólo en `escritorio-mirada` — y softpipe no le alcanza a wgpu, que
pide compute (§7.septies). O sea que en metal sin GPU Intel el diálogo no pinta… igual que no
pintaría el escritorio entero, que también es cliente de GL: **la bóveda no agrega un hueco, lo
hereda**. Lo que taparía el caso de la VM es un proveedor por software en el perfil, y eso es una
decisión de tamaño con dos candidatos (`mesa-llvmpipe`, o lavapipe como receta nueva), no un
arreglo de paso.
2. **el único proveedor de GL de las cuatro imágenes es `iris` (Intel)** `mesa-llvmpipe` no está en
ningún perfil y `mesa-swrast` sólo en `escritorio-mirada`—, así que en metal sin GPU Intel el
diálogo no pinta. ⚠ Pero **esto no es un hueco que abra la bóveda, ni una decisión pendiente**:
el recorte iris-only es una decisión ESCRITA (SDD 14 §«Por qué iris-only»), el target declarado
es una laptop Intel Iris Xe, y `llvmpipe`/`radeonsi` piden la cola de LLVM que ese recorte existe
para no traer. El caso de la VM ya tiene camino y también está escrito: los runbooks de QEMU
montan `mesa-llvmpipe` COMO CAPA por fuera de la imagen, que es exactamente lo que hace el
guardián de metal. La bóveda hereda el alcance de hardware de la imagen y no le agrega nada: sin
GL tampoco pinta el escritorio, que también es cliente de GL.
#### 7.undecies La bóveda entró a las imágenes y NADIE puede abrirla — y lo que el navegador veía era una bóveda TEMPORAL (2026-09-21)
El §7.decies dejó dos cosas abiertas y las dos resultaron la misma: **quién levanta la app** y **de
dónde sale la raíz de las claves** (la decisión 2 del §7.sexies). La cadena, medida eslabón por
eslabón hacia atrás desde la app:
| eslabón | qué dice | medido con |
|---|---|---|
| `raiz_de_identidad()` | saca la seed del **llavero del kernel**, clave `pacha_llavero::SEED_IDENTIDAD` | el fuente de `pacha-boveda-llimphi` |
| quién la ESCRIBE | **dos lugares** en todo tawasuyu: `agora-cli identity unlock` y el onboarding de diseño, `churay-welcome-runner` (que la guarda bajo el mismo nombre) | `git grep` de los `guardar(…)` contra el llavero |
| `agora-cli` en las imágenes | receta sellada, **`perfiles: []`** — en ninguna | `build-state` |
| …y aunque se declarara | su pin es `9967b02c`, **del 2026-06-18**, donde no existen ni `SEED_IDENTIDAD` ni `desbloquear` | `git grep` sobre el pin, con HEAD de control (4 y 1 aciertos) |
| `churay-welcome` | **no tiene receta en takana**: no está en el corpus | `ls recipes/` |
⇒ **Hoy, en ninguna imagen de takana hay un binario capaz de sembrar la identidad.** No es que el
usuario no la desbloqueó: es que no tiene con qué. La bóveda de la app abre `Err(boveda-cerrada)`
siempre, en las cuatro.
##### Y lo que el navegador veía NO era «cerrada»: era una bóveda temporal que se traga contraseñas
Acá está la parte que importa, y es la que ningún guardián de ficheros podía ver. Cuando `abrir()`
falla, la ventana se levanta igual sobre `bóveda_imposible()` —un `sled` en
`/tmp/boveda-sin-abrir-<pid>`— **y dice el problema en pantalla**, que está bien y se queda. Lo que
estaba mal es la línea siguiente:
```rust
let compartida = Arc::new(Mutex::new(boveda));
atender_al_navegador(&compartida); // ← sin preguntar si abrió
```
El socket del navegador se levantaba **sobre esa bóveda temporal**. Y del lado del host, `acceso()`
prefiere al dueño si lo hay (`AccesoPrestado::Ajeno`), así que la extensión veía:
```
vault.status ⇒ {"ok":true,"locked":false,"count":0} ← ABIERTA, no «cerrada»
vault.save ⇒ guarda… en /tmp/boveda-sin-abrir-<pid>
```
O sea: una persona apretaba el botón, el diálogo de consentimiento abría de verdad, decía que sí, y
la contraseña se guardaba **en un temporal que muere con el proceso** — y que ni el arranque
siguiente de la misma app volvería a mirar, porque el nombre lleva el pid.
⚠ **Hasta dónde llega cada afirmación, porque no son todas del mismo tipo.** Que el socket se
levantaba sin preguntar si la bóveda abrió está MEDIDO: es el test de regresión, que falla con el
arreglo neutralizado. Que la extensión veía `locked:false` es LECTURA del host —`Costura::acceso()`
prefiere al dueño (`AccesoPrestado::Ajeno`) y `vault_status` contesta `locked:false` con el `count`
del dueño, sea cual sea la bóveda que ese dueño tenga—; medirlo de punta a punta pide el guardián de
metal, que no corre en esta máquina (no hay rootfs de sway ni store acá). La etapa que lo mediría es
una hermana de la B: lanzar la app SIN `BOVEDA_TEST_ROOT` y exigir `locked:true`. El comentario de
`bóveda_imposible` dice «nunca se escribe nada ahí»: es cierto para la ventana, y el navegador es el
OTRO cliente. Es la regla 3 del CLAUDE.md entera: **un ausente falla ruidosamente; esto llegaba hasta
el final diciendo que todo fue bien.**
**Arreglado en tawasuyu** (`7917fbb96`): `atender_al_navegador` toma el problema y **no levanta el
socket si la bóveda no abrió**; la extensión ve `locked:true`, que es la verdad y lo que ya sabe
mostrar. La ventana sigue levantándose y diciendo por qué, que era lo bueno del diseño y no se toca.
Con test de regresión en los dos sentidos —sin bóveda no hay socket; con bóveda abierta el socket
aparece, que es el control sin el cual un `atender_al_navegador` vacío pasaría igual—, y probado
además AL REVÉS: con el `if` neutralizado a propósito el test falla con el mensaje que corresponde.
##### ⚠⚠ Y el muro del `Cargo.lock`, por tercera vez — con una vuelta que NO estaba escrita
Antes de poder subir el pin: `cargo metadata --locked` sobre el HEAD publicado de tawasuyu muere con
«cannot update the lock file», el mismo modo de fallo del §7.quinquies.bis y del §7.octies. Se
aplicó la operación MÍNIMA que el §7.octies dejó escrita —`cargo metadata` SIN `--locked`—, dio un
diff de **15 líneas de borrado y cero checksums movidos**, y **resultó byte a byte idéntico** al que
otra sesión ya tenía sin commitear en el árbol compartido. Todo encajaba con la vez anterior. Se
publicó, se subió el pin… y el build murió con **el mismo error de siempre** sobre el árbol ya
pineado.
**La causa, y es la parte que hay que recordar: esa operación mínima sólo es correcta donde el
registro de cargo está COMPLETO.** Corrida en la jaula qorpa —cuyo registro local no tiene todos los
crates— `cargo metadata` falla una descarga, **termina con estado 0** y deja un lock al que le faltan
**dos miembros del workspace**. Las dos corridas, lado a lado:
| dónde | diff del lock | qué hizo |
|---|---|---|
| jaula (registro incompleto) | **15 líneas** | se llevó `cuentas-allichay` y `allichay` |
| worker (registro completo) | **+1 línea** | agregó `"llimphi-widget-panel"`, la que faltaba |
O sea que **el diff equivocado se ve MÁS mínimo que el correcto** —quince líneas contra una, y cero
checksums movidos en los dos—, que es exactamente el criterio con el que el §7.octies enseñó a
distinguir el arreglo bueno del malo. Y el «byte a byte idéntico al de la otra sesión», que la vez
anterior fue la confirmación de que estaba bien, acá sólo significaba que la otra sesión lo había
calculado **en la misma jaula**. Dos mediciones que coinciden no son dos mediciones independientes si
comparten el mismo instrumento roto.
Publicado el lock bueno en `6f0408d40`, y con él `cargo metadata --locked` pasa en el worker. El pin
de las dos recetas sube ahí —se mueven juntas porque comparten árbol de fuentes por `<repo>-<sha>`—
⇒ `boveda` `b3:5c43a827` y `shuma-pregunta` `b3:cd4277c5`, construidas en el worker con la guarda de
receta puesta.
##### ⚠ La guarda de receta del §7.quinquies.bis NO alcanza para una TANDA
Las dos recetas se construyeron en un solo comando, con la guarda puesta —hash del worker contra el
del hub— **una vez, al principio**. `shuma-pregunta` selló bien. Once minutos después, `boveda` dijo:
```
INFO takana_build: caché: artefacto ya en el store hash=b3:3f1072cc… name=boveda
```
que es el hash de la receta ANTERIOR: un acierto de caché en cero segundos sobre el artefacto viejo,
o sea el éxito falso que ese capítulo existe para evitar. La causa es la misma y el agujero es
nuevo: **el latido (`rsync --delete` hub→worker, cada 30 min) revierte la receta EN EL MEDIO de la
tanda**, y una guarda que corre al principio no puede ver lo que pasa diez minutos después.
Comprobado: `recipes/boveda.toml` en el worker había vuelto al pin `b80f7567c`, con mtime del ciclo
del latido.
⇒ **la guarda va PEGADA a cada build, no una vez por tanda.** Y la cura de fondo es empujar la
receta a `origin/main` ANTES de construir: el latido siembra desde el checkout del otro hub, así que
mientras el commit no esté publicado, cada ciclo deshace lo que uno acaba de subir por `rsync`.
Y un detalle que ahorra construcciones y que conviene tener medido: **los COMENTARIOS de una
receta no mueven su hash** —se hashean los campos declarados, no los bytes del fichero—. Comprobado
en los dos sentidos sobre `shuma-pregunta.toml`: con un comentario de más y sin él, el mismo
`b3:cd4277c5`. Lo que sí lo mueve es el contenido de una fase, que es por qué el `.desktop` del
§7.decies costó un build y corregir tres párrafos de comentario no costó ninguno.
##### El llavero es el de SESIÓN, y en takana no hay quien lo cree
Aun con un sembrador en la imagen quedaría una pregunta que conviene tener medida antes de
escribirla mal: `pacha-llavero` usa `KEY_SPEC_SESSION_KEYRING` (-3). Ese llavero **lo crea el
login**, y quien no lo tiene recibe uno propio en cuanto lo toca — o sea que dos procesos HERMANOS
pueden no compartirlo.
Lo medido, y dónde:
- **en el worker sí se comparte, y se ve por qué**: `/etc/pam.d/login` y `/etc/pam.d/sshd` traen
`session optional pam_keyinit.so force revoke`, y dos procesos hermanos de la misma sesión ssh
caen en el MISMO anillo — `grep "_ses: 1$" /proc/keys` da uno solo con la clave adentro, y el
censo de `_ses` no crece al repetir;
- **en takana no lo crea nadie**: el único `pam.d` que este repo escribe es el de
`scripts/mirada-usb.sh`, y es `pam_permit` en las cuatro líneas. Y más abajo todavía:
`recipes/shadow.toml` compila con `--without-libpam`, así que **el `login` de consola no pasa por
PAM** y `pam_keyinit` no tendría dónde correr aunque se configurara. El módulo SÍ está en la
imagen (`lib64/security/pam_keyinit.so`, dentro de `linux-pam`, que está en las cuatro).
⇒ de ahí se SIGUE —y esto es inferencia, no medición: ver el aviso de abajo— que el día que haya
sembrador el camino bueno sea el de la misma rama de procesos (desbloquear en la consola y lanzar el
compositor desde ahí, que hereda), y que el de «desbloquear en una terminal de adentro» no alcance,
porque la app del lanzador es hermana y no hija. **Es lo primero que habrá que medir** cuando exista
el sembrador, y se mide con el guardián de metal, que sí tiene dónde correr.
⚠ **Lo que NO se pudo medir, dicho como tal:** la herencia hermano/hijo no se probó con una sonda
directa. `add_key` da `EPERM` en la jaula qorpa y `keyctl` da `ENOSYS` en el LXC del worker (seccomp
de contenedor, las dos), así que el control de la sonda no encendía. Lo de arriba es el censo de
`/proc/keys` más la semántica documentada del kernel — no una medición de la herencia.
##### Entonces: ¿se quedan las dos recetas en las imágenes?
**Sí, y el motivo cambió.** La respuesta era NO cuando la app no podía pintar (§7.septies): eso era
una bóveda que niega todo *sin un error*. Hoy la app pinta, dice en su ventana que no hay identidad
desbloqueada, y —con el arreglo de arriba— el navegador contesta `locked:true`, que es la verdad.
Son 43 M que muestran un estado explicable en vez de uno silencioso. Si mañana se prefiere sacarlas
hasta que exista el sembrador, son dos líneas por perfil; queda dicho para que sea una decisión y no
un olvido.
**La unidad que queda**, con sus dos formas posibles y sin elegir de paso: un **sembrador de
identidad en la imagen** — o subir el pin de `agora-cli` a uno que tenga `identity unlock` (y es un
pin compartido por árbol de fuentes con `arje-zero` y `dominium-cli`), o traer `churay-welcome` al
corpus, que es el camino de diseño y es otra receta llimphi GUI como ésta. Y con cualquiera de los
dos, la pregunta del llavero de sesión de acá arriba.
## 8. Plan, por unidades de trabajo
@@ -3445,7 +3612,7 @@ siguiente.
| 9 | **Medios (6.6) ✅ y torrent (6.9) ✅ — 2026-09-10.** El medio lo abre `mpv`; el torrent lo toma `puriy-costura-torrent`, un daemon **propio, configurable y perezoso** que sobrevive al navegador y cosecha al CAS. Medido antes de decidir: la pila de librqbit compila para musl (226 crates), así que la decisión fue «no ahí» y no «no se puede». **Faltan** el foco (6.5) y la IA local (6.7), ésta bloqueada porque el corpus no tiene modelo ni embeddings | 6.56.7, 6.9 | 5 ✅ |
| 10 | **Foco (6.5) — 2026-09-10.** Entra al corpus la cadena `nftables` (libmnl + libnftnl + nft 1.1.6), que el grafo pedía y nadie había puesto, con dos arreglos de fábrica: el sello de tiempo que rompía la reproducción y un bashismo. Medido con root: el cgroup en foco no sale y el de al lado sí. Y del lado del navegador, la extensión `foco` MUESTRA el estado y **no tiene verbo para apagarlo**. **Falta** quién pone a `atuq` en un cgroup (delegación, decisión de arje) y quién aplica la política (root) | 6.5 | 6 ✅ |
| 11 | **Motor de inferencia local (6.7) — 2026-09-11.** Entra `recipes/llama-cpp.toml` (b10901, estática, 199 M, **REPRODUCE**), que resulta ser **un solo muro para dos pendientes**: el §6.7 y la mitad semántica del §6.3 no eran dos problemas, era que el corpus no tenía con qué correr un modelo (`pluma-llm` sólo tiene backends de nube; `rimay-verbo-fastembed` DESCARGA onnxruntime glibc + el modelo). ⚠ Y dejó medido lo que no se podía deducir: **`SOURCE_DATE_EPOCH` —la variable que nos da reproducibilidad— apagaba las SEIS perillas de ISA de ggml**, y el artefacto sellaba y corría con 0 `%ymm`. Ahora van declaradas y el `install` las comprueba. Guardián `scripts/test-llama-cpp.py` con control negativo vivo. **Faltan** el modelo (fuente pineada, no receta), los verbos del host y quién levanta el servidor | 6.7, y la mitad semántica de 6.3 | 5 ✅ |
| 12 | **La bóveda (SDD-BOVEDA §8) — a medias, 2026-09-15.** La décima extensión: ofrece la credencial del sitio abierto y no puede sacar una contraseña por su cuenta —`vault.match` contesta títulos y usuarios; la contraseña sale por `vault.fill`, que **pregunta en el escritorio**—. La dirección la pone el chrome, nunca la página. El gestor de Gecko se aparta como VALOR DE ARRANQUE y no como política. **Destrabada el 2026-09-16** (§7.quinquies.bis): el `Cargo.lock` de tawasuyu publicado —y resultó ser byte a byte el que la otra sesión ya tenía sin commitear—, el pin subido a `23a292863` ⇒ `b3:7d63655a`, construido en el worker (el vendoreo son 2,4 G y el hub estaba al 99%) y medido con el mismo `strings` que había diagnosticado el hueco: `vault` de **0 a 10**, con `cas`/`sct` de control. **Falta**, y no era lo que este renglón decía (§7.sexies, 2026-09-18): el host sellado contesta `vault.status → locked:true` porque **el dueño de la bóveda y el diálogo de consentimiento no estaban en el corpus** — la función está apagada de fábrica en las cuatro imágenes. Entran `recipes/boveda.toml` y `recipes/shuma-pregunta.toml`, **las dos SELLADAS el 2026-09-18** (`b3:59ffd74b` y `b3:99763eca`, construidas en el worker). ⚠ Y con eso apareció el muro de verdad, que no es de la bóveda: **ninguna app llimphi de escritorio abre ventana en esta distro** porque no hay Vulkan en ninguna imagen y el camino GL de llimphi arma la instancia sin display handle (§7.septies). **CERRADA el 2026-09-18** (§7.novies): el guardián de metal —`scripts/test-atuq-boveda-metal.py`— pasa sus **seis etapas**, con el navegador de verdad, el diálogo a la vista y el control de decir que NO. Para llegar hubo que arreglar **tres piezas ajenas al navegador**: el dueño y el diálogo no estaban en el corpus (§7.sexies), ninguna ventana llimphi podía pintar sin Vulkan (§7.septies, arreglado en llimphi) y el dueño atendía de a UN cliente, lo que con el navegador abierto dejaba la bóveda muda (§7.octies, arreglado en pacha con su test de regresión). **Y ENTRA A LAS IMÁGENES el 2026-09-21** (§7.decies): hasta ese día las dos estaban `sealed` con `perfiles: []` —o sea selladas y en ninguna imagen, la lección de `foot` que ese mismo fichero repetía quince veces—, así que la función seguía apagada de fábrica aunque anduviera. Se declaran en los cuatro perfiles de escritorio (~43 M por imagen) y la receta aprende a instalar `boveda.desktop`, porque estar en la imagen no es poder abrirla: sin entrada en `/usr/share/applications` a la app sólo se llega escribiendo su nombre en una terminal. El guardián de coherencia pasa de CINCO lugares a SEIS —el sexto es `targets.toml`— con su tercer control negativo. **Abierto**: quién la levanta con la sesión, y de dónde sale la raíz de las claves (decisión 2 del §7.sexies) | el gestor de contraseñas de la suite dentro del navegador | 5 ✅ |
| 12 | **La bóveda (SDD-BOVEDA §8) — a medias, 2026-09-15.** La décima extensión: ofrece la credencial del sitio abierto y no puede sacar una contraseña por su cuenta —`vault.match` contesta títulos y usuarios; la contraseña sale por `vault.fill`, que **pregunta en el escritorio**—. La dirección la pone el chrome, nunca la página. El gestor de Gecko se aparta como VALOR DE ARRANQUE y no como política. **Destrabada el 2026-09-16** (§7.quinquies.bis): el `Cargo.lock` de tawasuyu publicado —y resultó ser byte a byte el que la otra sesión ya tenía sin commitear—, el pin subido a `23a292863` ⇒ `b3:7d63655a`, construido en el worker (el vendoreo son 2,4 G y el hub estaba al 99%) y medido con el mismo `strings` que había diagnosticado el hueco: `vault` de **0 a 10**, con `cas`/`sct` de control. **Falta**, y no era lo que este renglón decía (§7.sexies, 2026-09-18): el host sellado contesta `vault.status → locked:true` porque **el dueño de la bóveda y el diálogo de consentimiento no estaban en el corpus** — la función está apagada de fábrica en las cuatro imágenes. Entran `recipes/boveda.toml` y `recipes/shuma-pregunta.toml`, **las dos SELLADAS el 2026-09-18** (`b3:59ffd74b` y `b3:99763eca`, construidas en el worker). ⚠ Y con eso apareció el muro de verdad, que no es de la bóveda: **ninguna app llimphi de escritorio abre ventana en esta distro** porque no hay Vulkan en ninguna imagen y el camino GL de llimphi arma la instancia sin display handle (§7.septies). **CERRADA el 2026-09-18** (§7.novies): el guardián de metal —`scripts/test-atuq-boveda-metal.py`— pasa sus **seis etapas**, con el navegador de verdad, el diálogo a la vista y el control de decir que NO. Para llegar hubo que arreglar **tres piezas ajenas al navegador**: el dueño y el diálogo no estaban en el corpus (§7.sexies), ninguna ventana llimphi podía pintar sin Vulkan (§7.septies, arreglado en llimphi) y el dueño atendía de a UN cliente, lo que con el navegador abierto dejaba la bóveda muda (§7.octies, arreglado en pacha con su test de regresión). **Y ENTRA A LAS IMÁGENES el 2026-09-21** (§7.decies): hasta ese día las dos estaban `sealed` con `perfiles: []` —o sea selladas y en ninguna imagen, la lección de `foot` que ese mismo fichero repetía quince veces—, así que la función seguía apagada de fábrica aunque anduviera. Se declaran en los cuatro perfiles de escritorio (~43 M por imagen) y la receta aprende a instalar `boveda.desktop`, porque estar en la imagen no es poder abrirla: sin entrada en `/usr/share/applications` a la app sólo se llega escribiendo su nombre en una terminal. El guardián de coherencia pasa de CINCO lugares a SEIS —el sexto es `targets.toml`— con su tercer control negativo. **Y la decisión 2 CONTESTADA el 2026-09-21** (§7.undecies), con la respuesta incómoda: **nadie puede abrirla**, porque ninguna imagen trae un binario capaz de sembrar `SEED_IDENTIDAD` — `agora-cli` está en `perfiles: []` y además pineado al 2026-06-18, donde el verbo `unlock` todavía no existe; `churay-welcome`, que es el camino de diseño, no tiene receta. Y al medirlo apareció lo que ningún guardián de ficheros veía: cuando la bóveda no abre, la app **igual le levantaba el socket al navegador** sobre una bóveda TEMPORAL ⇒ `vault.status` contestaba `locked:false` y un `vault.save` consentido guardaba en `/tmp/boveda-sin-abrir-<pid>`, que muere con el proceso. Arreglado en tawasuyu (`7917fbb96`) con test en los dos sentidos, y el pin de las dos recetas sube a `6f0408d40`, que trae además el `Cargo.lock` cerrado — con una vuelta nueva: la «operación mínima» del §7.octies, corrida en una jaula con el registro de cargo incompleto, devuelve 0 y BORRA dos miembros del workspace, y su diff se ve más mínimo que el correcto. **Queda**: un sembrador de identidad en la imagen (subir el pin de `agora-cli` o traer `churay-welcome`), y el llavero de SESIÓN, que en takana no lo crea nadie (`shadow` va `--without-libpam`) | el gestor de contraseñas de la suite dentro del navegador | 5 ✅ |
| 9.a | **Proxy por contenedor ✅ v0.5** — contenedores por política + extensión con `proxy.onRequest` | 6.8, y NO dependía de 5: es API de Firefox | — |
Las unidades 2 y 4 son **paralelizables**: la toolchain no toca el chrome y el chrome no toca la
+32 -18
View File
@@ -37,21 +37,30 @@ version = "0.1.0"
license = "MPL-2.0"
[source]
# ⚠ El pin subió otra vez el 2026-09-18, a `b80f7567c` (el arreglo es `cf3540460`; el commit de
# arriba es el que además publica el `Cargo.lock` cerrado, sin el cual `cargo vendor --locked`
# no corre): el dueño de la bóveda atendía de a UN
# cliente y con el navegador abierto eso la deja MUDA (Gecko lanza un host por extensión, ocho, y
# todos se conectan al arrancar). SDD 26 §7.octies. Las dos recetas se mueven juntas para no pagar
# un tercer vendoreo de 2,4 G: comparten árbol por `<repo>-<sha>`.
# ⚠ EL PIN NO ES EL DE SUS HERMANAS, Y ES A PROPÓSITO (2026-09-18). Las demás recetas del monorepo
# están en `23a292863`; ésta apunta a `eed3120b6`, que es el commit donde llimphi aprende a pasarle
# el **display handle** a wgpu en el camino de escritorio. Con el pin viejo este binario sella,
# ⚠ El pin subió el 2026-09-21 a `6f0408d40`: **una bóveda que NO abrió le levantaba el socket al
# navegador igual**, sobre la temporal `/tmp/boveda-sin-abrir-<pid>` ⇒ `vault.status` contestaba
# `locked:false` y un `vault.save` consentido se perdía con el proceso. Es el caso NORMAL en las
# cuatro imágenes, donde nada puede sembrar la identidad (SDD 26 §7.undecies). Arreglado con test
# de regresión en los dos sentidos (el arreglo es `7917fbb96`; éste trae además el `Cargo.lock`
# cerrado de VERDAD, sin el cual `cargo vendor --locked` muere con «cannot update the lock file»).
# ⚠ Y ojo con cómo se cierra ese lock: la operación mínima documentada (`cargo metadata` sin
# `--locked`) corrida en una jaula con el registro INCOMPLETO devuelve 0 y deja un lock al que le
# faltan dos miembros del workspace — y su diff se ve MÁS mínimo que el correcto (-15 líneas contra
# +1). Se cierra donde el registro está completo; acá, en el worker.
# El pin anterior fue `b80f7567c` (2026-09-18, §7.octies: el dueño atendía de a UN cliente y con el
# navegador abierto la bóveda quedaba MUDA). Las dos recetas se mueven juntas para no pagar un
# tercer vendoreo de 2,4 G: comparten árbol por `<repo>-<sha>`.
# ⚠ EL PIN NO ES EL DE SUS HERMANAS, Y ES A PROPÓSITO (desde el 2026-09-18). Las demás recetas del
# monorepo están en `23a292863`, que es ANTERIOR a `eed3120b6` — el commit donde llimphi aprende a
# pasarle el **display handle** a wgpu en el camino de escritorio—, y sin eso este binario sella,
# arranca y **no abre ventana**: wgpu cae a la plataforma EGL surfaceless, la surface no tiene un
# solo formato y la app panica con un «index out of bounds» que no nombra nada de esto (SDD 26
# §7.septies). El precio es un árbol de fuentes propio — otro vendoreo de 2,4 G, porque el árbol se
# comparte por `<repo>-<sha>`—; se paga hasta que las demás suban al mismo commit.
# §7.septies). Por eso ésta y `boveda`/`shuma-pregunta` van siempre a un commit posterior; cuál es
# hoy lo dice el bloque de arriba, que es el que se actualiza. El precio es un árbol de fuentes
# propio —otro vendoreo de 2,4 G, porque el árbol se comparte por `<repo>-<sha>`—; se paga hasta
# que las demás suban al mismo commit.
repo = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git"
commit = "b80f7567c189f0bcba988efb7b0c5c2049f9aca6"
commit = "6f0408d40d05d7961e811f9062ce5d5127987635"
# tawasuyu COMMITEA su propio `vendor/` (smithay parcheado por `[patch.crates-io]` POR RUTA); el
# `cargo vendor` de takana lo pisaría y el error hablaría de un crate cualquiera, no de esto.
# No entra en `hash_inputs`.
@@ -105,12 +114,17 @@ install -Dm755 target/release/boveda /out/usr/bin/boveda
# 2. El cuarto perfil (sway) lleva sólo `hicolor`, que por diseño no trae iconos: ahí el lanzador
# cae al genérico, que es degradarse, no romperse.
#
# ⚠ Lo que esta entrada NO puede hacer, y conviene saberlo antes de buscarlo acá: emparejar la
# VENTANA con el lanzador. `llimphi_ui::run` no llama nunca a `with_name`, así que winit 0.30.13
# sólo manda `set_app_id` `if let Some(name) = attributes.platform_specific.name` ⇒ la ventana sale
# **sin app_id**, y con el título `"llimphi"`, que es el default de `App::title` y `BovedaApp` no
# sobrescribe. Por eso acá no hay `StartupWMClass`: no habría contra qué emparejarlo. Es de llimphi
# —como el muro del §7.septies— y se arregla allá, no acá.
# No lleva `StartupWMClass`, y la razón es que NO HACE FALTA — no que no se pueda. `llimphi_ui`
# arma la ventana con `with_name`, que en Wayland es el `app_id` del xdg-toplevel: usa el
# `App::app_id()` que la app declare y, si no declara ninguna —que es el caso de `BovedaApp`—, cae
# al **nombre del ejecutable** (`app_id_del_ejecutable`, el piso que llimphi se puso cuando 163 de
# sus 178 apps compartían el genérico del compositor). O sea `app_id = "boveda"`, que es
# exactamente el basename de este `.desktop` ⇒ el emparejamiento estándar ya funciona.
# Medido, no leído: el guardián de metal espera `'"app_id": *"boveda"'` en el árbol de sway y lo
# encuentra a los 12-13 s (SDD 26 §7.novies).
# ⚠ Lo que sí sigue mal es el TÍTULO: `App::title()` devuelve `"llimphi"` por defecto y `BovedaApp`
# no lo sobrescribe, así que la barra y el conmutador de ventanas dicen «llimphi». Es de llimphi/la
# app, no de esta entrada.
mkdir -p /out/usr/share/applications
cat > /out/usr/share/applications/boveda.desktop <<'DESKTOP'
[Desktop Entry]
+21 -12
View File
@@ -31,21 +31,30 @@ version = "0.1.0"
license = "MIT OR Apache-2.0"
[source]
# ⚠ El pin subió otra vez el 2026-09-18, a `b80f7567c` (el arreglo es `cf3540460`; el commit de
# arriba es el que además publica el `Cargo.lock` cerrado, sin el cual `cargo vendor --locked`
# no corre): el dueño de la bóveda atendía de a UN
# cliente y con el navegador abierto eso la deja MUDA (Gecko lanza un host por extensión, ocho, y
# todos se conectan al arrancar). SDD 26 §7.octies. Las dos recetas se mueven juntas para no pagar
# un tercer vendoreo de 2,4 G: comparten árbol por `<repo>-<sha>`.
# ⚠ EL PIN NO ES EL DE SUS HERMANAS, Y ES A PROPÓSITO (2026-09-18). Las demás recetas del monorepo
# están en `23a292863`; ésta apunta a `eed3120b6`, que es el commit donde llimphi aprende a pasarle
# el **display handle** a wgpu en el camino de escritorio. Con el pin viejo este binario sella,
# ⚠ El pin subió el 2026-09-21 a `6f0408d40`: **una bóveda que NO abrió le levantaba el socket al
# navegador igual**, sobre la temporal `/tmp/boveda-sin-abrir-<pid>` ⇒ `vault.status` contestaba
# `locked:false` y un `vault.save` consentido se perdía con el proceso. Es el caso NORMAL en las
# cuatro imágenes, donde nada puede sembrar la identidad (SDD 26 §7.undecies). Arreglado con test
# de regresión en los dos sentidos (el arreglo es `7917fbb96`; éste trae además el `Cargo.lock`
# cerrado de VERDAD, sin el cual `cargo vendor --locked` muere con «cannot update the lock file»).
# ⚠ Y ojo con cómo se cierra ese lock: la operación mínima documentada (`cargo metadata` sin
# `--locked`) corrida en una jaula con el registro INCOMPLETO devuelve 0 y deja un lock al que le
# faltan dos miembros del workspace — y su diff se ve MÁS mínimo que el correcto (-15 líneas contra
# +1). Se cierra donde el registro está completo; acá, en el worker.
# El pin anterior fue `b80f7567c` (2026-09-18, §7.octies: el dueño atendía de a UN cliente y con el
# navegador abierto la bóveda quedaba MUDA). Las dos recetas se mueven juntas para no pagar un
# tercer vendoreo de 2,4 G: comparten árbol por `<repo>-<sha>`.
# ⚠ EL PIN NO ES EL DE SUS HERMANAS, Y ES A PROPÓSITO (desde el 2026-09-18). Las demás recetas del
# monorepo están en `23a292863`, que es ANTERIOR a `eed3120b6` — el commit donde llimphi aprende a
# pasarle el **display handle** a wgpu en el camino de escritorio—, y sin eso este binario sella,
# arranca y **no abre ventana**: wgpu cae a la plataforma EGL surfaceless, la surface no tiene un
# solo formato y la app panica con un «index out of bounds» que no nombra nada de esto (SDD 26
# §7.septies). El precio es un árbol de fuentes propio — otro vendoreo de 2,4 G, porque el árbol se
# comparte por `<repo>-<sha>`—; se paga hasta que las demás suban al mismo commit.
# §7.septies). Por eso ésta y `boveda`/`shuma-pregunta` van siempre a un commit posterior; cuál es
# hoy lo dice el bloque de arriba, que es el que se actualiza. El precio es un árbol de fuentes
# propio —otro vendoreo de 2,4 G, porque el árbol se comparte por `<repo>-<sha>`—; se paga hasta
# que las demás suban al mismo commit.
repo = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git"
commit = "b80f7567c189f0bcba988efb7b0c5c2049f9aca6"
commit = "6f0408d40d05d7961e811f9062ce5d5127987635"
cargo_vendor_dir = ".hammer-cargo-vendor"
[build]