diff --git a/docs/evidencia/atuq-notificacion-web-sway-2026-09-07.png b/docs/evidencia/atuq-notificacion-web-sway-2026-09-07.png new file mode 100644 index 00000000..047d5ef1 Binary files /dev/null and b/docs/evidencia/atuq-notificacion-web-sway-2026-09-07.png differ diff --git a/scripts/wlr/dunst-headless.sh b/scripts/wlr/dunst-headless.sh index 250c4fae..47c16555 100755 --- a/scripts/wlr/dunst-headless.sh +++ b/scripts/wlr/dunst-headless.sh @@ -3,6 +3,7 @@ # # scripts/wlr/dunst-headless.sh # sale 0 si la notificación se dibuja # scripts/wlr/dunst-headless.sh --negative-control # borra el .service y exige que NO se vea +# scripts/wlr/dunst-headless.sh --via-atuq # el emisor es una PÁGINA WEB en el navegador # # ── QUÉ PRUEBA, QUE NO ES LO MISMO QUE «ESTÁ INSTALADO» ─────────────────────────────────────── # Tener `libnotify` en la imagen es media función: el que la completa es un daemon escuchando @@ -18,6 +19,17 @@ # a otro lado, o dunst no supiera hablar wayland, este guion no vería nada. Es el mismo camino que # recorre una página web pidiendo notificar en `atuq`. # +# ── `--via-atuq`: LA MISMA CADENA, PERO EMPEZANDO EN UNA PÁGINA WEB ─────────────────────────── +# El modo por defecto prueba la cadena del SISTEMA. Éste prueba la que le importa al usuario y la +# que motivó todo el hilo del SDD 26 §6.10: una página llama a `new Notification(...)`, `libxul` +# hace `dlopen("libnotify.so.4")` —que no es NEEDED de ningún ELF, o sea invisible para todo +# auditor—, eso habla D-Bus, el bus activa dunst y dunst dibuja. Cinco piezas, y la única forma de +# saber que están las cinco es verlo. +# El veredicto acá NO puede ser sólo el diff de píxeles —el navegador ocupa la pantalla y cambia +# solo—, así que la captura «antes» se toma con atuq YA ABIERTO y la página espera 25 s antes de +# notificar. Y se exige además que el `onshow` del objeto Notification haya disparado, que es el +# motor diciendo que el sistema aceptó la notificación. +# # ── POR QUÉ notify-send Y NO dunstify ───────────────────────────────────────────────────────── # Porque `dunstify` no se shipea: mezcla la GLib ESTÁTICA del corpus con la COMPARTIDA que arrastra # `libnotify.so.4` y se cae con SIGSEGV hasta en `--help` (dos copias de GObject en un proceso — el @@ -35,7 +47,14 @@ ROOT="$(cd "$(dirname "$0")/../.." && pwd)"; cd "$ROOT" RFS="${RFS:-/mnt/cosecha/escritorios/sway-rootfs}" ALPINE="$ROOT/.dev-fs/alpine" CONTROL=0 -[ "${1:-}" = "--negative-control" ] && CONTROL=1 +ATUQ=0 +for a in "$@"; do + case "$a" in + --negative-control) CONTROL=1 ;; + --via-atuq) ATUQ=1 ;; + *) echo "✗ opción desconocida: $a" >&2; exit 1 ;; + esac +done [ -d "$RFS" ] || { echo "✗ no está el rootfs en $RFS — corré antes:" >&2 echo " scripts/hydrate-profile.py escritorio-sway --into $RFS" >&2; exit 1; } @@ -62,13 +81,26 @@ mkdir -p "$XDG_RUNTIME_DIR" /root; chmod 700 "$XDG_RUNTIME_DIR" # sesión de verdad. Al revés, dunst arrancaría sin display y saldría con status 1. eval "$(/usr/bin/dbus-daemon --session --fork --print-address=1 | { read a; echo "DBUS_SESSION_BUS_ADDRESS=$a"; })" export DBUS_SESSION_BUS_ADDRESS - /usr/bin/grim /salida/antes.png + [ "${ATUQ:-0}" = "1" ] || /usr/bin/grim /salida/antes.png echo "@@ dueño ANTES: $(timeout 10 /usr/bin/dbus-send --session --dest=org.freedesktop.DBus \ --print-reply=literal /org/freedesktop/DBus org.freedesktop.DBus.NameHasOwner \ string:org.freedesktop.Notifications 2>&1 | tr -d ' \n')" - timeout 25 /usr/bin/notify-send -u critical "hammer" "dunst lo activó el bus" 2>&1 - echo "@@ notify-send rc=$?" - sleep 5 + if [ "${ATUQ:-0}" = "1" ]; then + export MOZ_ENABLE_WAYLAND=1 GDK_BACKEND=wayland LIBGL_DRIVERS_PATH=/usr/lib/dri + # Los tres 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 entorno de prueba, no una + # preferencia del artefacto. + export MOZ_DISABLE_CONTENT_SANDBOX=1 MOZ_DISABLE_RDD_SANDBOX=1 MOZ_DISABLE_UTILITY_SANDBOX=1 + /usr/bin/atuq --no-remote --profile /salida/perfil file:///salida/notificar.html \ + > /salida/atuq.log 2>&1 & + sleep 22 # que el navegador ya esté pintado ANTES de la captura «antes» + /usr/bin/grim /salida/antes.png + sleep 12 # la página notifica a los 25 s y repite cada 5 + else + timeout 25 /usr/bin/notify-send -u critical "hammer" "dunst lo activó el bus" 2>&1 + echo "@@ notify-send rc=$?" + sleep 5 + fi echo "@@ servidor: $(timeout 10 /usr/bin/dbus-send --session --dest=org.freedesktop.Notifications \ --print-reply=literal /org/freedesktop/Notifications \ org.freedesktop.Notifications.GetServerInformation 2>&1 | tr -s ' ')" @@ -79,11 +111,50 @@ mkdir -p "$XDG_RUNTIME_DIR" /root; chmod 700 "$XDG_RUNTIME_DIR" exec /usr/bin/sway -d 2>&1 ADENTRO +if [ "$ATUQ" = "1" ]; then + mkdir -p "$OUT/perfil" + cat > "$OUT/perfil/user.js" <<'JS' +// `dump()` es el canal por el que la página le habla al log del proceso: sin red y sin servidor. +user_pref("browser.dom.window.dump.enabled", true); +user_pref("devtools.console.stdout.content", true); +// 1 = permitir sin preguntar. Sin esto la prueba mediría el POPUP DE PERMISOS del navegador, que +// se dibuja igual y no prueba nada del sistema de notificaciones. +user_pref("permissions.default.desktop-notification", 1); +user_pref("browser.shell.checkDefaultBrowser", false); +JS + cat > "$OUT/notificar.html" <<'HTML' +