From 5fd87464454108b31e463886df2f060f5cc87733 Mon Sep 17 00:00:00 2001 From: Sergio Date: Mon, 21 Sep 2026 21:41:43 +0000 Subject: [PATCH] =?UTF-8?q?atuq=20=C2=A77.terdecies:=20=C2=ABno=20hay=20id?= =?UTF-8?q?entidad=C2=BB=20era=20tambi=C3=A9n=20=C2=ABno=20pude=20pregunta?= =?UTF-8?q?r=C2=BB=20=E2=80=94=20y=20en=20pacha-cli=20eso=20guardaba=20los?= =?UTF-8?q?=20dotfiles=20EN=20CLARO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contesta el segundo pendiente del §7.duodecies: `pacha`/`pacha-secretos` en `perfil.servidor`. La respuesta a la pregunta de origen es **NO**, y es la CONTRARIA a la del escritorio. Premisa medida, no supuesta: la Card de `pacha-secretos` es `scope=system` y entra al `genesis`, o sea que arje arranca el daemon EN EL ARRANQUE. De ahí se sigue que un sembrador en la imagen no llega a tiempo por construcción — en el arranque no hay nadie logueado; `abrir_almacen()` decide una vez en `main()` y no vuelve a mirar; y `agora-cli unlock` de una sesión ssh siembra en el llavero de ESA sesión. Declararlo dejaría la función PARECIENDO cerrada, que es peor que el hueco. Queda escrito al lado de las dos raíces, en `targets.toml`, para que nadie copie la decisión del escritorio. Y al ir a medirlo, el control POSITIVO salió rojo — con la seed en `/proc/keys` el daemon decía que no había identidad — y eso destapó el defecto de verdad: los CINCO lectores de la seed hacían `.ok().flatten()` (o un `_ =>`), que convierte «el llavero no se pudo consultar» en «no hay identidad desbloqueada». La causa del rojo la mide la sonda de syscalls: en el LXC `add_key` funciona y `keyctl(KEYCTL_SEARCH)` da `ENOSYS` ⇒ se puede sembrar y no cosechar. El peor de los cinco no era un mensaje feo: `pacha-cli` guardaba los dotfiles SIN CIFRAR, en silencio, con la identidad del usuario desbloqueada. Arreglado en tawasuyu (`4f2b5eac7`) con `pacha_llavero::SeedDeSesion` —tres estados, tres textos— y `Reason` en `net.tawasuyu.Secretos1`. Pin de las dos recetas `23a292863` → `cd9acd0d9` ⇒ `b3:e8038352` (4,6 M) y `b3:a9fb8c17` (8,5 M), mirados por dentro y probados como artefacto: el daemon sellado ahora dice «motivo=el llavero de sesión NO se pudo consultar (… os error 38) — esto no es «no hay identidad»». Tres cosas más que quedaron medidas por el camino: - la guarda pegada al build **se disparó sola por primera vez**: el latido revirtió `pacha.toml` entre las dos construcciones de la misma tanda; - el muro del `Cargo.lock`, quinta vez, y la arista que faltaba era la mía de esa mañana: se la llevó un «merge de git en «main» (import)». Bisecado con `git log -S`; - el índice Y el árbol compartidos de tawasuyu tenían una versión de `pacha-boveda-llimphi` ANTERIOR al arreglo de `7917fbb96`. Lo delató el test de regresión, no el diff. --- docs/26-atuq-envoltorio-gecko.md | 192 ++++++++++++++++++++++++++++++- docs/state/targets.toml | 28 +++++ 2 files changed, 219 insertions(+), 1 deletion(-) diff --git a/docs/26-atuq-envoltorio-gecko.md b/docs/26-atuq-envoltorio-gecko.md index e1706ad3..cc0fdbe7 100644 --- a/docs/26-atuq-envoltorio-gecko.md +++ b/docs/26-atuq-envoltorio-gecko.md @@ -3768,6 +3768,196 @@ habría hecho. Los cuatro controles negativos, en verde. dueño y otra decisión; 3. **el wizard**, que es el camino de diseño y hoy no tiene receta. +#### 7.terdecies «No hay identidad» era también «no pude preguntar» — y en un sitio eso guardaba los dotfiles EN CLARO (2026-09-21) + +El §7.duodecies cerró con tres pendientes, y el segundo era `pacha`/`pacha-secretos` en +`perfil.servidor`: los dos LEEN `SEED_IDENTIDAD`, ninguno la siembra, y nadie en esa imagen puede +sembrarla. Esto es ir a contestar eso. La respuesta a la pregunta de origen cambió poco; lo que +apareció por el camino, mucho. + +##### El control positivo salió ROJO, y eso es lo que encontró el defecto + +El plan era medir con el daemon de verdad: sembrar la identidad, arrancar `pacha-secretos` y ver si +dice persistente. Cuatro sondas, con su control: + +| | sembrado | cómo arranca el daemon | esperado | salió | +|---|---|---|---|---| +| P0 control negativo | no | normal | modo memoria | modo memoria ✓ | +| **P1 control POSITIVO** | **sí, misma sesión, mismo uid** | normal | **persistente** | **modo memoria ✗** | +| P2 el caso del servidor | sí | `setsid` (otra sesión) | modo memoria | modo memoria | +| P3 otro uid | sí | `su nobody` | — | modo memoria | + +Con el control en rojo, P2 y P3 **no prueban nada**: los cuatro contestan igual, que es +indistinguible de un guardián que siempre dice lo mismo. Así que antes de escribir una sola +conclusión hubo que preguntar por qué, y la sonda de syscalls lo dijo en veinte segundos: + +``` +add_key -> 679788099 errno=0 +keyctl SEARCH -> -1 errno=38 Function not implemented +``` + +En el LXC del worker —seccomp de contenedor— **se puede sembrar y no se puede cosechar**. La seed +estaba en `/proc/keys`, sus 32 bytes, puesta por el `agora-cli` del §7.duodecies; y el Secret +Service arrancaba igual diciendo «SIN identidad agora desbloqueada». (En la jaula qorpa las dos dan +`EPERM`, que es por qué esto no se puede medir desde acá.) + +##### Los cinco lugares donde `Option` tenía dos estados y el llavero tres + +La causa no es el contenedor: es que el lector traduce mal. Estaba en **cinco** sitios, todos con la +misma forma —`.recuperar(…).ok().flatten()`, o un `_ =>`—, que convierte **«no se pudo preguntar»** +en **«no hay identidad desbloqueada»**: + +| dónde | qué se ve cuando el llavero falla | gravedad | +|---|---|---| +| `pacha-cli` (dotfiles) | **los dotfiles se guardan SIN CIFRAR**, sin una palabra, con la identidad del usuario desbloqueada | la peor: es pérdida de confidencialidad, no un mensaje feo | +| `pacha-secretos` | «SIN identidad agora desbloqueada — modo memoria» | los secretos no persisten y la culpa apunta al usuario | +| `pacha-cli` (`dotfiles pubkey`) | «identidad bloqueada: no hay seed» | acusa de no haber desbloqueado | +| `pacha-boveda-llimphi` · `pata-llimphi` | «La bóveda está cerrada. **Se abre al entrar a la sesión.**» | consejo falso: entrar a la sesión no lo arregla | +| `wawa-panel-llimphi` | «ninguna identidad desbloqueada» | — | + +**Y esto ya había pasado, con cicatriz escrita en el propio crate.** El doc de +`pacha_llavero::SEED_IDENTIDAD` cuenta que hasta el 2026-08-19 había dos nombres de clave que no se +encontraban, y que la consecuencia fue *«el Secret Service arrancaba SIEMPRE en modo memoria aunque +la identidad estuviera desbloqueada, y no persistía un solo secreto»* — semanas, sin que nadie lo +viera. Se arregló el nombre; **lo que lo hizo invisible seguía puesto**. + +**Arreglado en tawasuyu** (`4f2b5eac7`): `pacha-llavero` gana `SeedDeSesion` —`Hay` / `NoHay` / +`Fallo`— con un `motivo()` cuyos tres textos son distintos a propósito, y los cinco lectores pasan +por ahí. `pacha-secretos` gana además `reason` en su interfaz propia `net.tawasuyu.Secretos1`, al +lado de `persistent`, que existía exactamente por esto: «desde afuera es indistinguible». Test en +los dos sentidos —`los_tres_estados_no_se_colapsan`, con un llavero que siempre rompe y la +exigencia de que los textos difieran—, y probado al revés: con `Err(e) => Fallo(e)` neutralizado a +`NoHay`, falla en «un Err del llavero TIENE que llegar como fallo». + +##### La pregunta de origen: ¿entra `agora-cli` en `perfil.servidor`? **NO** + +Y no por lo que uno supondría. La premisa está medida: + +``` +scripts/targets.py --service-paths servidor ⇒ incluye recipes/pacha-secretos.toml +takana service-cards recipes/pacha-secretos.toml ⇒ Card de scope system, "lifecycle": "daemon" +``` + +O sea que la Card entra en el `genesis` de la imagen y **arje arranca el daemon en el arranque**, +con `setuidgid pacha`. De ahí se sigue, sin necesidad de medir ningún llavero: + +1. **en el arranque no hay nadie logueado**, así que no hay seed que sembrar todavía — un sembrador + en la imagen no llega a tiempo, por construcción; +2. `abrir_almacen()` se llama **una sola vez, en `main()`**, y el resultado queda en `Estado`: aunque + el operador desbloquee después, el daemon ya decidió y no vuelve a mirar; +3. y el `agora-cli unlock` de una sesión ssh siembra en **el llavero de ESA sesión**, que no es el + del daemon. + +⇒ declarar `agora-cli` en `servidor` no cerraría nada y dejaría la función **pareciendo** cerrada, +que es peor que el hueco. En el escritorio (§7.duodecies) el sembrador sí tiene sentido porque la +persona está delante; acá no hay persona en el momento que importa. **La misma raíz, dos respuestas +distintas, y por eso no se copia la decisión de un perfil al otro.** + +Lo que SÍ lo cerraría, nombrado y no decidido —el propio `pacha-llavero` ya lo tiene previsto: *«la +elección de backend de desbloqueo (kernel hoy; mañana PAM/TPM/passphrase+Argon2/greeter) NO está +cementada: se cambia la impl de [`Llavero`]»*—: + +- un backend de llavero que un daemon pueda usar al arrancar (fichero con permisos recortados, TPM + sellado), en vez del llavero de sesión, que es de sesión por diseño; +- o que el daemon **vuelva a mirar** en vez de decidir una vez, y que el operador lo destrabe + después del arranque. Hoy ni siquiera eso alcanzaría, por el punto 3. + +Hasta entonces el Secret Service de `servidor` es efímero —que es lo que su propia receta ya decía— +y ahora **lo dice con el motivo correcto** y lo publica por D-Bus. Que es toda la diferencia entre +un hueco conocido y uno silencioso. + +##### Lo sellado, y el antes/después medido contra el artefacto + +Las dos recetas suben de `23a292863` a `cd9acd0d9` —`4f2b5eac7` más el cierre de su `Cargo.lock`, ver +abajo— y sellan en el worker con la guarda pegada a cada build: `pacha-secretos` `b3:e8038352` (4,6 M) +y `pacha` `b3:a9fb8c17` (8,5 M). Miradas por dentro, que es la regla 3 —el arreglo en el commit no +dice que esté en el binario—: + +``` +pacha-secretos «NO se pudo consultar» 1 · «no hay identidad … en esta sesión» 1 · Reason 1 + control: «SIN identidad agora desbloqueada» (el texto viejo) → 0 +pacha «los dotfiles se guardan SIN cifrar» 1 · control: «identidad bloqueada: no hay seed» → 0 +``` + +Y corriendo el daemon sellado, que es la prueba que importa: + +``` +antes: WARN SIN identidad agora desbloqueada — modo memoria, los secretos NO persisten +ahora: WARN modo memoria — los secretos NO persisten + motivo=el llavero de sesión NO se pudo consultar + (keyring del kernel: Function not implemented (os error 38)) + — esto no es «no hay identidad» +``` + +⚠ **Hasta dónde llega esto.** En el LXC las DOS sondas dan ahora «fallo», porque ahí `keyctl` es +`ENOSYS` siempre: lo que quedó probado contra el artefacto es que el caso de error **deja de +disfrazarse de ausencia**. Que la rama `NoHay` siga diciendo «no hay» sólo lo cubre el test unitario +—con un llavero que rompe a propósito—, porque para ejercitarla hace falta una máquina donde el +llavero funcione, y ni la jaula ni el worker lo son. + +##### ℹ La guarda pegada al build volvió a salvar una tanda, y en vivo + +Las dos recetas se lanzaron en un solo comando, con la guarda por receta que el §7.undecies dejó +escrita. La primera selló; la segunda no llegó a construirse: + +``` +GUARDA [recipes/pacha.toml]: esperaba b3:a9fb8c17… y hay b3:ea0c664f… +``` + +`b3:ea0c664f` es el hash ANTERIOR: el latido (`rsync --delete` cada 30 min) había revertido la +receta **entre las dos construcciones de la misma tanda** — comprobado por el mtime de los dos +ficheros en el worker, idéntico y de ese ciclo. Sin la guarda eso habría sido un acierto de caché en +cero segundos sobre el artefacto viejo, o sea un éxito falso. Con ella, un error y un recopiado. +**Es la primera vez que esa guarda se dispara sola**, y confirma que el capítulo que la pidió no +estaba siendo paranoico. + +##### ⚠ Y otra vez el árbol compartido, ahora con un arreglo publicado DESHECHO en el índice + +Al ir a commitear, `pacha-boveda-llimphi/src/lib.rs` estaba `MM` — y las dos versiones, la del +índice y la del árbol, eran **anteriores al arreglo de `7917fbb96`**: la que levanta el socket del +navegador sin preguntar si la bóveda abrió (§7.undecies), con su comentario y todo borrado. Mis +cambios se habían escrito encima de ESA base sin que nada avisara. + +Se rehízo sobre HEAD y se commiteó con `commit-tree`, sin tocar índice ni árbol. **Lo que lo +delató no fue mirar el diff: fue que el test de regresión +`el_socket_del_navegador_sale_solo_si_la_boveda_abrio` estaba en la lista de los que tenían que +pasar.** Es el tercer episodio del mismo tipo en dos días (el `Cargo.lock` dos veces, esto una), y +la regla que sale es la misma que el §7.duodecies: cuando el fichero que necesitás es justo el que +otro tiene a medias, el pathspec no alcanza — hay que salirse del índice, y hay que tener un test +que grite si la base era la equivocada. + +##### ⚠ El `Cargo.lock`, quinta vez — y ahora la arista que faltaba era la MÍA de esa mañana + +`cargo vendor --locked` volvió a morir. Esta vez la arista ausente era `rpassword` en `agora-cli`, +publicada unas horas antes en `da5fb8968` (§7.duodecies): se la llevó `338d4a9b9 "merge de git en +«main» (import)"`, que resolvió el lock quedándose con el lado viejo. Bisecado y no adivinado: + +``` +git log -S'"rpassword",' da5fb8968..origin/main -- Cargo.lock ⇒ 338d4a9b9 +``` + +El blob repuesto es **byte a byte** el que ya estaba publicado. Con esto la regla del §7.duodecies +queda confirmada del peor modo: **este muro no lo levanta quien toca el lock, lo levanta cualquiera +que toque un `Cargo.toml` del monorepo — o un merge que lo resuelva mal.** Y deja un aviso práctico: +`recipes/agora-cli.toml` está pineada a `da5fb8968`, que SÍ tiene la arista, así que su artefacto +sigue construyendo; quien la suba por encima de `338d4a9b9` sin el commit de reparación se come el +muro, y el mensaje de cargo no nombra ni `rpassword` ni el merge. + +ℹ Y una reparación de paso, porque costó media hora entender el síntoma: en el clon compartido de +tawasuyu, **cuatro directorios de `.git/objects` eran de `nobody`** (uid sin mapear, del 2026-09-18), +así que cualquier `fetch` cuyos objetos cayeran en `05/`, `a5/`, `b4/` o `b8/` moría con +*«insufficient permission for adding an object»* — intermitente, según el sha. Reparados copiando +los cuatro objetos a directorios nuevos; `git fsck` limpio. + +##### Lo que queda, y lo barato que sería + +`boveda` y `shuma-pregunta` siguen pineadas en `6f0408d40`, o sea **sin** este arreglo: la ventana +de la bóveda todavía dice «se abre al entrar a la sesión» cuando el llavero es el que falló. Subirlas +a `4f2b5eac7` las arregla **y de paso colapsa dos árboles de fuentes en uno** —ahora comparten +commit con `pacha`/`pacha-secretos`—, así que el vendoreo de 2,4 G ya estaría pagado. No se hizo acá +porque son dos builds de llimphi GUI y esto era la unidad del servidor; queda dicho con su precio +para que sea una decisión. + ## 8. Plan, por unidades de trabajo Cada una cierra sola, se commitea y se pushea. El orden no es preferencia: cada una destraba a la @@ -3795,7 +3985,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.5–6.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. **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-`, 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. **Y EL SEMBRADOR ENTRA el 2026-09-21** (§7.duodecies), que es lo que esa respuesta dejaba pendiente: de las dos formas posibles se elige `agora-cli` —el wizard `churay-welcome-llimphi` SÍ tiene binario, pero trae consigo backend de IA, dotfiles, fondo y chasqui, o sea la experiencia de primer arranque entera, y eso no se decide dentro de una unidad del navegador—. Y antes de poder declararlo apareció lo que lo volvía imposible: **sin `AGORA_PASSPHRASE`, `Sesion::abrir()` caía en la frase de desarrollo `"agora-dev"`** con un aviso por stderr y un ✓ en pantalla ⇒ en una imagen de escritorio, la seed de identidad de todo el mundo —que es la RAÍZ con la que la bóveda se descifra— cifrada con una palabra que está escrita en el fuente. Arreglado en tawasuyu (`fd08dc03a`): variable > terminal (se pregunta, sin eco, y DOS veces en la génesis) > desarrollo sólo si no hay a quién preguntarle, con la decisión en una función pura, cuatro tests y el control al revés. Pin `9967b02c` → `da5fb8968` (con el muro del `Cargo.lock` por cuarta vez, y esta vez la arista que faltaba era de OTRO agente: `shuma-taller`) ⇒ `b3:46529e14`, 1,9 M, declarado en los cuatro perfiles, **medido como artefacto**: `identity new` + `unlock` deja `user pacha:id:default: 32` en `/proc/keys`, y con la frase equivocada falla y no re-siembra. El guardián de coherencia pasa de SEIS lugares a SIETE, con su cuarto control negativo. **Queda**: la herencia del llavero de SESIÓN entre procesos HERMANOS (sin medir — y `/proc/keys` como root no la mide, sólo dice que la clave existe), y `pacha`/`pacha-secretos` en `perfil.servidor` con el mismo hueco sin sembrador | 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-`, 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. **Y EL SEMBRADOR ENTRA el 2026-09-21** (§7.duodecies), que es lo que esa respuesta dejaba pendiente: de las dos formas posibles se elige `agora-cli` —el wizard `churay-welcome-llimphi` SÍ tiene binario, pero trae consigo backend de IA, dotfiles, fondo y chasqui, o sea la experiencia de primer arranque entera, y eso no se decide dentro de una unidad del navegador—. Y antes de poder declararlo apareció lo que lo volvía imposible: **sin `AGORA_PASSPHRASE`, `Sesion::abrir()` caía en la frase de desarrollo `"agora-dev"`** con un aviso por stderr y un ✓ en pantalla ⇒ en una imagen de escritorio, la seed de identidad de todo el mundo —que es la RAÍZ con la que la bóveda se descifra— cifrada con una palabra que está escrita en el fuente. Arreglado en tawasuyu (`fd08dc03a`): variable > terminal (se pregunta, sin eco, y DOS veces en la génesis) > desarrollo sólo si no hay a quién preguntarle, con la decisión en una función pura, cuatro tests y el control al revés. Pin `9967b02c` → `da5fb8968` (con el muro del `Cargo.lock` por cuarta vez, y esta vez la arista que faltaba era de OTRO agente: `shuma-taller`) ⇒ `b3:46529e14`, 1,9 M, declarado en los cuatro perfiles, **medido como artefacto**: `identity new` + `unlock` deja `user pacha:id:default: 32` en `/proc/keys`, y con la frase equivocada falla y no re-siembra. El guardián de coherencia pasa de SEIS lugares a SIETE, con su cuarto control negativo. **Y EL CASO `servidor` CONTESTADO el 2026-09-21** (§7.terdecies), con la respuesta CONTRARIA a la del escritorio: **`agora-cli` NO va en `perfil.servidor`**. La premisa está medida —la Card de `pacha-secretos` es `scope=system` y entra al `genesis`, o sea que arje arranca el daemon EN EL ARRANQUE— y de ahí se sigue que un sembrador no llega a tiempo por construcción: en el arranque no hay nadie logueado, `abrir_almacen()` decide una sola vez en `main()`, y un `unlock` de una sesión ssh siembra en el llavero de ESA sesión. Declararlo dejaría la función PARECIENDO cerrada, que es peor que el hueco. Y al ir a medirlo apareció el defecto que sí se arregló: los **cinco** lectores de la seed hacían `.ok().flatten()`, que convierte «el llavero no se pudo consultar» en «no hay identidad desbloqueada» — y en `pacha-cli` eso guardaba **los dotfiles SIN CIFRAR, en silencio**. Lo encontró un control positivo en ROJO (con la seed presente en `/proc/keys` el daemon decía que no había), cuya causa mide la sonda de syscalls: en el LXC `add_key` funciona y `keyctl(KEYCTL_SEARCH)` da `ENOSYS`. Arreglado en tawasuyu (`4f2b5eac7`) con `SeedDeSesion` —tres estados, tres textos— más `reason` en `net.tawasuyu.Secretos1`; pin de las dos recetas `23a292863` → `cd9acd0d9`. **Queda**: la herencia del llavero de SESIÓN entre procesos HERMANOS (sin medir — y `/proc/keys` como root no la mide, sólo dice que la clave existe), y subir `boveda`/`shuma-pregunta` a ese mismo commit, que las arregla y de paso colapsa dos árboles de fuentes en uno | 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 diff --git a/docs/state/targets.toml b/docs/state/targets.toml index 7cd9a25d..60d5ad86 100644 --- a/docs/state/targets.toml +++ b/docs/state/targets.toml @@ -1639,6 +1639,34 @@ paquetes = [ # ARRANCAN solos es otra pregunta y está más abajo, en `servicios`. "matilda", "tupu", "sandokan-watch", "pacha", "pacha-secretos", "willay-daemon", "willay-crosscheck", "tejido", "thasnuna", + # ── ⚠ NO, `agora-cli` NO VA ACÁ — y la tentación es fuerte (SDD 26 §7.terdecies, 2026-09-21) ── + # Los cuatro perfiles de ESCRITORIO declaran `agora-cli` desde el §7.duodecies, porque es el único + # binario del corpus que siembra `pacha_llavero::SEED_IDENTIDAD`. Y `pacha` y `pacha-secretos`, que + # están acá arriba, leen esa misma seed: sin ella el Secret Service corre en modo memoria —los + # secretos NO sobreviven al reinicio— y `pacha dotfiles` no cifra. Parece el mismo hueco y parece + # que se cierra igual. **No se cierra**, y copiar la decisión del escritorio dejaría la función + # PARECIENDO arreglada, que es peor que el hueco. + # + # Lo medido, que es la premisa y no la conclusión: + # scripts/targets.py --service-paths servidor ⇒ incluye recipes/pacha-secretos.toml + # takana service-cards recipes/pacha-secretos.toml ⇒ Card scope=system, "lifecycle":"daemon" + # o sea que la Card entra al `genesis` y **arje arranca el daemon EN EL ARRANQUE**, con + # `setuidgid pacha`. De ahí se sigue, sin medir ningún llavero: + # 1. en el arranque no hay nadie logueado ⇒ no hay seed que sembrar todavía: un sembrador en la + # imagen no llega a tiempo, por construcción; + # 2. `abrir_almacen()` corre UNA sola vez en `main()` y el resultado queda en `Estado`: aunque el + # operador desbloquee después, el daemon ya decidió y no vuelve a mirar; + # 3. y `agora-cli unlock` en una sesión ssh siembra en el llavero de ESA sesión, no en el del + # daemon (`KEY_SPEC_SESSION_KEYRING` es de sesión por diseño). + # En el escritorio el sembrador sirve porque la persona está delante; acá no hay persona en el + # momento que importa. **Misma raíz, dos respuestas.** + # + # Lo que sí lo cerraría, nombrado y NO decidido —`pacha-llavero` ya lo tiene previsto: «kernel hoy; + # mañana PAM/TPM/passphrase+Argon2/greeter»—: un backend de llavero que un daemon pueda usar al + # arrancar (fichero con permisos recortados, TPM sellado), o que el daemon vuelva a mirar en vez de + # decidir una vez. Hasta entonces el almacén es efímero —lo que su propia receta ya decía— y desde + # el pin `cd9acd0d9` al menos lo DICE con el motivo correcto y lo publica por D-Bus (`Reason`, al + # lado de `Persistent`). # `curl`/`wget`: bajar del mirror y del repo. `takana install --repo https://…` los necesita del # lado del cliente, y son también la única forma de diagnosticar un origen caído desde la caja. "curl", "wget",