atuq: la bóveda ENTREGA una contraseña, y sólo cuando alguien dice que sí — las seis etapas en verde

A ✓ sin la app dueña, el host contesta locked:true — y con ok:true, que es la trampa
    B ✓ con la app, vault.status dice ABIERTA: el socket sube en 1-2 s
    C ✓ el diálogo ABRE en el compositor y se lo CONTESTA: «1» ⇒ yes, «2» ⇒ no
    D ✓ vault.save con consentimiento real guarda, y vault.match la encuentra SIN contraseña
    E ✓ el navegador real, sobre una página servida por HTTP: la contraseña llega al CAMPO
    F ✓ contestando que NO, al campo no llega NADA

Dos corridas seguidas en verde, 6 min cada una. La unidad 12 del SDD queda CERRADA.

La E es la promesa entera del §6 y sola no probaría nada —una bóveda que entrega siempre se ve
idéntica a una que entrega con permiso—; por eso la F corre lo mismo diciendo que no. Y lo que se
mide no es lo que el host contesta sino lo que hay EN EL CAMPO: la página delata al servidor lo que
le pongan. Es la lección del §7.quater aplicada al otro extremo.

Tres fallos del ARNÉS, los tres disfrazados de fallo del producto:
· `wait` a secas esperaba también a la app de la bóveda ⇒ la jaula se comía su timeout y a la app la
  mataba un KILL, y con ella lo recién guardado (sled no alcanzaba a volcar). La etapa siguiente no
  encontraba la credencial: «la bóveda no guarda», el diagnóstico contrario al verdadero;
· el diálogo NO responde al teclado mientras la app de la bóveda inicializa su GPU — dos llimphi
  arrancando a la vez sobre render por software se pisan: doce teclas sin efecto y `denied` por
  timeout con la ventana abierta. Ahora se espera a que la app PINTE (12-13 s), no a su socket;
· una sola tecla no alcanza y el borde se mueve: el contestador insiste hasta que la ventana se va,
  que es la única señal que no depende de adivinar cuánto tarda.

El guardián entra ENTERO a la suite (era `--hasta A`): 20 en verde, ~38 min.

Lo que costó llegar: el guardián se escribió para medir una función y destapó tres piezas rotas,
NINGUNA en atuq — el dueño y el diálogo no estaban en el corpus, ninguna ventana llimphi podía pintar
sin Vulkan, y el dueño atendía de a un cliente. Las tres se veían igual desde el navegador: una
bóveda que no ofrece nada, sin un solo error.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sergio
2026-09-18 15:57:20 +00:00
co-authored by Claude Opus 5
parent 057332363a
commit 4be4680310
3 changed files with 87 additions and 16 deletions
+23 -3
View File
@@ -406,6 +406,18 @@ def guion_dentro(cuerpo, con_boveda, log="pasos.log", salida_rel="/salida"):
[ -S "$XDG_RUNTIME_DIR/boveda.sock" ] \\
&& echo "@@ socket de la boveda LISTO en ${{i}}s" >> {salida_rel}/{log} \\
|| echo "@@ SIN socket de la boveda tras ${{i}}s" >> {salida_rel}/{log}
# ⚠ Y se espera además a que la app PINTE, no sólo a que atienda. El socket lo levanta un hilo
# antes de que haya ventana, así que seguir acá deja a la app inicializando su GPU al mismo
# tiempo que el diálogo inicializa la suya — y con render por software en cuatro núcleos eso se
# paga en el diálogo: aparece en el árbol del compositor, toma el foco y **no responde al
# teclado**. Medido: doce teclas seguidas sin efecto y `vault.save` volviendo `denied` por
# timeout, con la ventana todavía abierta.
j=0
while [ $j -lt 90 ]; do
/usr/bin/swaymsg -t get_tree -r 2>/dev/null | grep -q '"app_id": *"boveda"' && break
sleep 1; j=$((j+1))
done
echo "@@ ventana de la boveda a los ${{j}}s" >> {salida_rel}/{log}
"""
return f"""#!/bin/sh
export XDG_RUNTIME_DIR=/run/user/0 HOME={salida_rel}/hogar LANG=C XDG_DATA_DIRS=/usr/share
@@ -630,11 +642,12 @@ def correr_etapas(capas, dirs, salida, hasta, url, fallos):
for par in si:%s:1 no:%s:2; do
quien=${par%%%%:*}; resto=${par#*:}; tecla=${resto%%%%:*}; n=${resto#*:}
( python3 /salida/contestar.py "$tecla" 120 "pregunta|shuma" > /salida/contestador-C-$quien.log 2>&1 ) &
contestador=$!
/usr/bin/shuma-pregunta --title "Prueba del instrumento" --detail "esto no toca la boveda" \
--option "yes=Si, contesta" --option "no=No" \
> /salida/dialogo-C-$quien.json 2> /salida/dialogo-C-$quien.err
echo "@@ dialogo $quien salio con $? y dijo: $(cat /salida/dialogo-C-$quien.json)" >> /salida/pasos-C.log
wait
wait "$contestador"
done
""" % (TECLA_SI, TECLA_NO)
correr_jaula(capas, salida, guion_dentro(cuerpo, con_boveda=False, log="pasos-C.log"), segundos=420)
@@ -659,8 +672,14 @@ def correr_etapas(capas, dirs, salida, hasta, url, fallos):
print("\n== D · vault.save con el consentimiento dado, ¿guarda? ¿y después la encuentra?")
cuerpo = """
( python3 /salida/contestar.py "%s" 150 "pregunta|shuma" > /salida/contestador-D.log 2>&1 ) &
contestador=$!
python3 /salida/preguntar.py guardar >> /salida/pasos-D.log 2>&1
wait
# ⚠ `wait "$contestador"` y NO `wait` a secas: la app de la bóveda TAMBIÉN es un trabajo en
# segundo plano, así que un `wait` pelado no vuelve nunca, la jaula se come su timeout y a la
# app la mata un KILL. Y lo que se pierde ahí es lo que se acaba de guardar: `sled` no alcanza a
# volcar, la etapa siguiente no encuentra la credencial, y el cuadro que queda es «la bóveda no
# guardó» — el diagnóstico contrario al verdadero.
wait "$contestador"
""" % TECLA_SI
correr_jaula(capas, salida, guion_dentro(cuerpo, con_boveda=True, log="pasos-D.log"), segundos=420)
print(indentar(leer(salida, "pasos-D.log")))
@@ -753,13 +772,14 @@ def correr_navegador(capas, dirs, salida, url, tecla, etapa):
# El patrón del contestador NO es opcional acá: con el navegador abierto, teclear «la ventana
# enfocada» le contestaría a la PÁGINA. Se busca la ventana del diálogo por app_id/título.
( python3 /salida/contestar.py "%s" 150 "pregunta|shuma" > /salida/contestador-%s.log 2>&1 ) &
contestador=$!
export MOZ_ENABLE_WAYLAND=1 GDK_BACKEND=wayland
# Los sandboxes de Gecko se apagan porque esto YA corre dentro de una jaula sin userns anidado:
# con ellos puestos los procesos hijos mueren en bucle. Es el entorno de prueba, no el artefacto.
export MOZ_DISABLE_CONTENT_SANDBOX=1 MOZ_DISABLE_RDD_SANDBOX=1 MOZ_DISABLE_UTILITY_SANDBOX=1
timeout 150 /usr/bin/atuq --no-remote --profile /salida/perfil-%s '%s' >> /salida/atuq-%s.log 2>&1
echo "@@ atuq salio con $?" >> /salida/pasos-%s.log
wait
wait "$contestador"
""" % (tecla, etapa, etapa, url, etapa, etapa)
correr_jaula(capas + [capa], salida, guion_dentro(cuerpo, con_boveda=True, log=f"pasos-{etapa}.log"),
segundos=420)
+10 -12
View File
@@ -15,10 +15,9 @@
# Si falta el sello, esto NO corre nada: dice cómo construirlo y sale.
#
# ══ EL CONTROL DE ESTA CORRIDA ════════════════════════════════════════════════════════════════
# Hoy (2026-09-18) el resultado CORRECTO es **20 en verde** — el vigésimo entró ese día y corre
# TRUNCADO (`--hasta A`) a propósito: ver su renglón en la lista y el §7.septies. Un verde suyo
# NO dice que la bóveda ande; dice que el host contesta lo que tiene que contestar sin dueño.
# Lo fue «19 en verde» desde el 2026-09-16. Lo fue «17 y dos rojos» hasta esta
# Hoy (2026-09-18) el resultado CORRECTO es **20 en verde**. El vigésimo entró ese día: primero
# truncado —sólo su etapa A, porque las otras cinco chocaban con un muro ajeno (§7.septies)— y desde
# esa misma tarde ENTERO, con sus seis etapas. Lo fue «19 en verde» desde el 2026-09-16. Lo fue «17 y dos rojos» hasta esta
# tarde: los dos eran los verbos de la bóveda, y se apagaron al subir el pin de `puriy-costura`
# (SDD 26 §7.quinquies.bis). Si mañana hay un rojo, cambió algo que hay que ir a ver.
#
@@ -36,7 +35,8 @@
# envejece callado.** Si el cuadro no coincide con esto, el sospechoso es esta cabecera.
#
# ══ LO QUE CUESTA ═════════════════════════════════════════════════════════════════════════════
# **~34 min** en gioser, en serie (32,1 min medidos el 2026-09-16 sobre 19; el vigésimo suma ~2). Los cuatro primeros salen en 10 s y no tocan
# **~38 min** en gioser, en serie (32,1 min medidos el 2026-09-16 sobre 19; el vigésimo suma 6,
# medidos en dos corridas seguidas — levanta cuatro compositores y dos navegadores). Los cuatro primeros salen en 10 s y no tocan
# el navegador: para «¿rompí algo del cruce política↔XPI?» alcanza con `--only` y no hace falta la
# media hora. Los caros son `sct` (241 s, seis cargas), `archivo-semantico` (227 s, embeddings),
# `instalacion` (180 s, tres sesiones) y `ia` (180 s, levanta `llama-server`).
@@ -78,13 +78,11 @@ done
GUARDIANES=(
"vigia-atuq-verbos|python3 scripts/vigia-atuq-verbos.py|el host PINEADO atiende los verbos que mandan las diez extensiones (§7)"
"test-atuq-boveda-coherente|python3 scripts/test-atuq-boveda-coherente.py|la bóveda está enchufada en los CINCO lugares (SDD-BOVEDA §8)"
# ⚠ CORRE TRUNCADO, `--hasta A`, y el motivo está en el §7.septies: `boveda` y `shuma-pregunta`
# están sellados y NO ABREN VENTANA —ninguna app llimphi de escritorio puede, porque no hay Vulkan
# en ninguna imagen y el camino GL de llimphi arma la instancia sin display handle—, así que las
# etapas B..F miden un muro ajeno a la bóveda. Lo que la A afirma es la medición del §7.sexies, que
# es la que costó tres semanas de no ver: **sin la app dueña, el host contesta `locked:true` con
# `ok:true`**. Cuando llimphi se destrabe, se saca el `--hasta A` y esta nota se va con él.
"test-atuq-boveda-metal|python3 scripts/test-atuq-boveda-metal.py --hasta A|sin el dueño de la bóveda, el host contesta CERRADA — y «cerrada» es un ok (§7.sexies)"
# Corría truncado (`--hasta A`) mientras el muro del §7.septies tapaba las otras cinco etapas.
# Desde el 2026-09-18 corre ENTERO: seis etapas, ~6 min, con el navegador de verdad, el diálogo de
# consentimiento a la vista y el control de decir que NO. Es el único de la suite que mide la
# cadena hasta el CAMPO del formulario y no hasta la respuesta del host.
"test-atuq-boveda-metal|python3 scripts/test-atuq-boveda-metal.py|la bóveda entrega la contraseña, y SÓLO con el sí de una persona (§7.novies)"
"test-atuq-politica|python3 scripts/test-atuq-politica.py|el cruce XPI empaquetado ↔ política, con cinco roturas a propósito"
"test-atuq-rootfs|python3 scripts/test-atuq-rootfs.py|el rootfs resuelve todos los NEEDED y nada se cae al LAB"
"test-atuq-inicio|python3 scripts/test-atuq-inicio.py|la página de inicio y la pestaña nueva son las NUESTRAS (§4.e)"