SDD 26: atuq, el envoltorio Gecko — derivado, no fork; y la puerta es clang

La pregunta era «nuestro propio envoltorio en vez de zen». La respuesta tiene tres hallazgos que
cambian el precio, y el documento los pone antes que la lista de features.

1. NO HAY ENVOLTORIO FUERA DEL CHROME. Gecko no tiene API de embebido en escritorio desde que murió
   XULRunner (GeckoView es Android) ⇒ una carcasa Llimphi con el motor adentro no es posible. El
   envoltorio es chrome-level o no es. Que es exactamente lo que Zen es.

2. ARTEFACTO DERIVADO, NO FORK DE FUENTE. Si atuq parchea el árbol, cada iteración de UI cuesta
   cuatro horas de Gecko y el diseño de chrome es iterativo por naturaleza. Como no distribuimos un
   binario sino que sellamos artefactos, atuq puede depender de `firefox` e inyectar marca, prefs,
   policies.json y omni.ja encima: itera en segundos, no mueve el ArtifactHash del corpus, y hereda
   las CVE gratis — que es lo que hundió a los forks de Firefox. Con sus dos gotchas escritos: el
   omni.ja es un zip y hay que re-empacarlo determinista, y sin invalidar el startup cache el
   overlay PARECE no agarrar.

3. LAS OPTIMIZACIONES NO SON UN FLAG, SON UNA TOOLCHAIN. PGO/LTO/BOLT son cadena de clang en Gecko,
   y el corpus no tiene clang usable como compilador: clang18.toml compila SÓLO libclang.so (para
   bindgen) y llvm18 se selló con PROJECTS="". La unidad real es una receta llvm-toolchain con
   clang+lld+libc++ musl — la misma pieza que resuelve el muro 3 de firefox.toml. Es la mayor
   palanca del frente, y por eso encabeza el plan.

Lo que NO se promete va escrito ANTES de la lista de features, con el criterio del modelo de
adversario de qullqa: atuq no es Tor Browser y no va a tener «modo Tor» — es un binario único en el
mundo (musl, wayland-only, branding propio), así que salir por Tor desde acá identifica MÁS, no
menos. Se ofrece proxy por contenedor, que es separación de tráfico y no anonimato.

Los diferenciadores van con una columna «quién más lo tiene» para no contarnos un cuento: sct
(transparencia de scripts, BLAKE3 + testigo) no lo tiene nadie y ya está escrito en tawasuyu;
torrent SÍ lo tiene Vivaldi, y lo nuestro sólo vale por dónde cae lo bajado (el CAS). Todo lo que
no es CSS pasa por UNA costura: un host de native messaging en Rust.

Y atuq no abandona a puriy: el host, el testigo y el archivo con RAG son agnósticos del motor, así
que cuando puriy madure se enchufan del mismo lado. atuq es su andamio, no su desvío.
This commit is contained in:
Sergio
2026-09-05 02:54:23 +00:00
parent 1a3b62b1f6
commit 1fcad3f6de
2 changed files with 238 additions and 0 deletions
+237
View File
@@ -0,0 +1,237 @@
# SDD 26 — `atuq`: el envoltorio Gecko de la distro
Escrito 2026-09-05, a partir de la pregunta del usuario: *«ya habiendo compilado firefox y waterfox,
qué tan complicado vs provechoso suena crear nuestro propio envoltorio, en vez de zen?»*, y de su
aclaración de por qué la pregunta existe: **`puriy` quedó muy verde y es difícil sacarlo de ahí**.
`atuq` es *zorro* en quechua. El nombre estaba libre —verificado con `grep -ri atuq` sobre
`/mnt/vvv/hammer` y `/mnt/vvv/tawasuyu`, cero coincidencias— y dice exactamente lo que la cosa es:
otro fox, con marca propia y sin usar la de Mozilla (ver el §branding de `recipes/firefox.toml`).
**Este documento no reemplaza a `puriy` ni lo cancela.** `puriy` sigue siendo el motor soberano; el
§9 explica por qué `atuq` es su andamio y no su desvío.
---
## 1. La corrección que ordena todo: no hay envoltorio fuera del chrome
La intuición natural —una carcasa Llimphi con nuestros widgets, y Gecko adentro pintando la página—
**no es posible**. Gecko no tiene API de embebido en escritorio desde que murió XULRunner; GeckoView
existe y es de Android. No hay forma soportada de meter el motor en una ventana de `mirada`.
⇒ un envoltorio de Gecko es, necesariamente, **chrome-level**: JS/CSS/XHTML *dentro* del propio
Firefox, más extensiones, más procesos externos que hablen con él.
Y eso es exactamente lo que Zen es. Zen no toca el motor: su diferencia entera —workspaces, split
view, glance, compact mode— vive en el chrome, más una herramienta (`surfer`) que reaplica sus
parches sobre el árbol de Firefox y recompila. **Su costo no es el motor: es la cinta de correr del
rebase cada cuatro semanas.** Copiar su método sería heredar su costo sin heredar su equipo.
## 2. La decisión de arquitectura: artefacto DERIVADO, no fork de fuente
Si `atuq` parchea el árbol, **cada iteración de UI cuesta un build de Gecko de cuatro horas**. Eso
mata el proyecto en la primera semana de diseño, porque el diseño de chrome es iterativo por
naturaleza.
Pero nosotros no distribuimos un binario: sellamos artefactos. Entonces `atuq` es una receta cuyo
`[deps]` incluye `firefox`, y cuyas fases copian el árbol instalado e inyectan encima:
| Qué se inyecta | Mecanismo de Firefox | Sin recompilar |
|---|---|---|
| Marca, iconos, nombre | `distribution/`, `application.ini` | ✅ |
| Prefs de fábrica | `defaults/pref/*.js` + autoconfig (`.cfg`) | ✅ |
| Política de empresa | `distribution/policies.json` | ✅ |
| Chrome propio (dientes, layout) | `userChrome.css` + re-empaque de `omni.ja` | ✅ |
| Extensiones nuestras | `distribution/extensions/` | ✅ |
**Ventajas que esto tiene sobre el método de Zen, y que son estructurales:**
1. **Iteración en segundos, no en horas.** El chrome se toca sin volver a compilar C++.
2. **No mueve el `ArtifactHash` de `firefox`.** El corpus no se invalida; `waterfox` y cualquier otro
fork siguen compartiendo el mismo artefacto base.
3. **Las CVE se heredan gratis.** Un fork de fuente te vuelve dueño del reloj de seguridad del
binario más atacado de la máquina — es lo que hundió a casi todos los forks de Firefox, y el
reproche histórico a Waterfox. Un derivado se reconstruye solo cuando sube `firefox`.
**Los dos gotchas reales del camino, que son trabajo pero chico:**
- **`omni.ja` es un zip ⇒ hay que re-empacarlo determinista** (mtimes fijos, orden estable) o el
artefacto deja de reproducir bit a bit. Es la misma disciplina que ya exige la imagen del lab.
- **Hay que invalidar el startup cache** tras tocarlo (fichero `.purgecaches` junto al binario, o
bump del BuildID). Si no, los cambios no se ven y **parece que el overlay no agarró**: un falso
negativo de los caros, de la misma familia que el cache-hit que congela regresiones.
### 2.bis Cuándo SÍ hay que parchear el árbol
Cuando algo necesite tocar C++ del motor. Hoy conocemos un caso: `sct` en su forma fuerte (§6.1).
La regla es: **nada baja al árbol antes de que exista la toolchain del §3**, porque hasta entonces
cada intento cuesta cuatro horas. Y cuando baje, el vigía de parches (`scripts/vigia-parches.py`,
commit `aa200a4`) es lo que convierte esas cuatro horas en un minuto para saber si el parche agarra.
## 3. La puerta de las optimizaciones: no es un flag, es una toolchain
El usuario decidió habilitar las optimizaciones. Lo que hay que saber antes de intentarlo:
`recipes/firefox.toml` va con `compiler = "gcc"` por el **muro 3** (zig enlaza `libc++` estática para
musl y el `configure` de Mozilla exige encontrar un `NEEDED …libc++`; es negativa de upstream, no una
perilla). El problema es que **PGO, LTO y BOLT son cadena de clang en Gecko**:
- `--enable-lto=cross` quiere clang + lld. El LTO de GCC sobre Gecko no está soportado upstream.
- `--enable-profile-use` espera `-fprofile-instr-use` (clang), no `-fprofile-use` (gcc).
- BOLT necesita `llvm-bolt`.
Y **el corpus no tiene clang usable como compilador**. Verificado en las recetas, no supuesto:
- `recipes/clang18.toml` compila `ninja -C build libclang.so clang-resource-headers` y su fase
`install` copia **sólo** `build/lib/libclang.so*` y las cabeceras `clang-c`. Está ahí porque
`bindgen` carga `libclang.so` en runtime. **No hay driver `clang`, ni `lld`, ni `libc++`.**
- `llvm18` se selló con `-DLLVM_ENABLE_PROJECTS=""` — LLVM a secas.
⇒ **la unidad de trabajo real es una receta `llvm-toolchain` con clang + lld + `libc++` compartida
para musl.** Es la misma pieza que resuelve el muro 3, así que destraba de una sola vez: LTO, PGO,
BOLT, y el regreso de Firefox a una toolchain más cercana a la del resto del corpus. **Es la mayor
palanca por unidad de trabajo que hay en este frente**, y por eso encabeza el plan del §8.
**Riesgos de PGO, escritos antes de empezar:**
- El PGO de Mozilla **corre el navegador** para juntar el perfil (`profileserver.py`, headless +
marionette). Dentro de un sandbox hermético, sin red y sin X11, eso hay que hacerlo andar.
Headless no necesita display, pero sí necesita que el árbol de perfil se genere adentro.
- **El perfil resultante es un input del artefacto.** O entra en `hash_inputs`, o `atuq` deja de ser
determinista y no lo vamos a notar: sería otra forma del lab que no está en `hash_inputs`.
- Ganancia esperable, para calibrar expectativas: PGO+LTO en Gecko son del orden de 10-25% en carga
de página y JS. **Hoy estamos ATRÁS de Zen en este eje, no adelante** — Zen hereda la
configuración de Mozilla tal cual. Esto no es una ventaja nuestra: es una deuda que se salda.
## 4. Lo que `atuq` NO promete
Esta sección existe antes que la lista de features a propósito, con el mismo criterio que el modelo
de adversario de `qullqa`: **lo que no se promete se escribe primero, porque una promesa falsa de
privacidad es peor que no ofrecer nada.**
**`atuq` no es Tor Browser y no va a tener «modo Tor».** Tor Browser no es «Firefox + SOCKS»: su
valor son las contramedidas de huella (RFP, letterboxing, normalización de fuentes/canvas/timers) y
sobre todo **un conjunto de anonimato donde todos se ven idénticos**. `atuq` es, por construcción, un
binario único en el mundo: musl, Wayland-only, branding propio, extensiones propias. Alguien saliendo
por Tor desde `atuq` es **más** identificable, no menos, y su conjunto de anonimato son diez
personas. Para anonimato: Tor Browser, y lo decimos nosotros primero.
Lo que sí se ofrece, y es honesto porque es otra cosa: **proxy por contenedor** (§6.8) — separación
de tráfico, no anonimato.
**`atuq` tampoco promete** anti-fingerprinting propio (es un programa de investigación con equipo
full-time) ni bloqueo de anuncios propio (se shipea uBO y listo).
## 5. Contexto verificado al escribir (2026-09-05)
De `docs/state/build-state.json`, no de memoria:
| Dato | Valor |
|---|---|
| Recetas / nodos | 838 / 840 |
| Selladas | 837 |
| `firefox` 154.0 | **sealed**, cola `corpus` |
| `waterfox` 6.7.1.1 | **never** — primer build en curso |
| Plataforma compartida | `gtk3`, `atk`, `nodejs`, `clang18`, `cbindgen`: todas sealed |
**`atuq` no arranca hasta que `waterfox` selle.** Waterfox es la prueba de la tesis del reúso —«las
diferencias reales con `firefox.toml` son TRES: la fuente, el branding y la versión»—. Si esa tesis
falla, el precio de todo este documento cambia y hay que releerlo.
## 6. Los diferenciadores, priorizados
La columna «quién más lo tiene» es lo que evita que nos contemos un cuento.
| # | Qué | Quién más lo tiene | Pieza que ya existe | Costo |
|---|---|---|---|---|
| 6.1 | **`sct` — transparencia de scripts** | **nadie** | `puriy-sct`, `puriy-sct-testigo` | medio |
| 6.2 | **Descargas direccionadas por contenido** | nadie | store CAS de hammer, `tejido` | bajo |
| 6.3 | **Archivo personal + RAG local** | Rewind/Recall (nube, Windows); SingleFile (guarda, no busca) | `khipu`, `rag-motor`, `willay-rag` | medio |
| 6.4 | **Historial y perfil sobre `qullqa`** | nadie | `qullqa-core`, `qullqa-pozo` | medio |
| 6.5 | **Foco por `cortafuegos`, no por extensión** | nadie (nadie es dueño del navegador *y* del sistema) | `cortafuegos`, `pacha` | bajo |
| 6.6 | **Medios por fuera del navegador** | extensiones sueltas; de fábrica no | `foreign-ytdlp`, `-platform`, `-dlna` | bajo |
| 6.7 | **IA local en la barra lateral** | Chrome/Edge son nube; Zen no tiene | `rimay`, `iniy` | bajo |
| 6.8 | **Proxy por contenedor** | nadie de fábrica | — (API de Firefox) | bajo |
| 6.9 | **Torrent adentro** | **Vivaldi lo trae; Brave tuvo WebTorrent** | `shared/foreign-torrent` (SDD propio, sobre librqbit) | bajo |
### 6.1 `sct` es la joya
BLAKE3 a todo lo que se ejecuta, y **negarse a correr código que ningún testigo vio nunca**. Es lo
más cerca que hay de «Certificate Transparency, pero para el JavaScript que te corre en la cara», y
no lo tiene nadie: ni Zen, ni Brave, ni Tor Browser.
En tawasuyu ya está escrito y —dice su README— la lógica es **agnóstica del transporte** y está
certificada sin red; `puriy-sct-testigo` es sólo el cable HTTP.
- **v1, sin tocar C++:** extensión con `webRequest` bloqueante que hashea cada respuesta de script y
consulta al testigo antes de dejarla pasar.
- **v2, forma fuerte:** gancho en el script loader de Gecko. Eso sí es parche de árbol ⇒ §2.bis ⇒
después de la toolchain del §3.
### 6.2 Una descarga con identidad
Lo bajado no es un archivo en `~/Downloads`: es un objeto BLAKE3 en el store — deduplicado,
verificable, y re-compartible por `tejido`. El torrent (6.9) alimenta lo mismo. **Ningún navegador
trata una descarga como algo con identidad**; para todos es un blob con nombre, y por eso lo bajás
dos veces y no lo sabés.
Ésa, y no «tener torrent», es la razón por la que 6.9 vale: Vivaldi ya tiene torrent, pero termina en
una carpeta.
### 6.3 El archivo personal es la razón por la que alguien usaría `atuq`
Cada página visitada se congela en el CAS y entra al DAG de `khipu`; después le preguntás a tu
propio historial en lenguaje natural, **todo local**. Es la feature que más justifica el proyecto
entero, y sale de piezas que ya están escritas.
### 6.4 `qullqa` con su advertencia puesta
Almacenamiento con negación plausible y modelo de adversario **escrito** — ningún navegador tiene
eso; el modo incógnito es teatro. ⚠ Con la advertencia que el propio SDD de `qullqa` ya anotó y que
**hay que repetir acá, no esconder**: en una máquina con swap sin cifrar, el documento no aplica.
Se promete lo que se puede probar.
## 7. La costura: un host de native messaging en Rust
Todo lo del §6 que no es CSS pasa por **un solo mecanismo**: un proceso Rust que habla native
messaging con la extensión de `atuq` y, del otro lado, con la suite (`agora` para identidad Ed25519,
`khipu`, el store, `foreign-*`, `cortafuegos`).
Que sea **uno** y no siete es la decisión: cada feature del §6 pasa a ser un verbo de ese host, no un
proyecto nuevo. El binario vive en tawasuyu (es suite, no build system); `atuq` sólo lo declara como
dep y le deja el manifiesto de native messaging en su sitio. **El nombre del crate se decide en
tawasuyu y con su regla 10 puesta** — acá no se bautiza nada de allá.
## 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
siguiente.
| # | Unidad | Puerta que abre | Bloqueada por |
|---|---|---|---|
| 1 | `waterfox` sella | valida la tesis del reúso | build en curso |
| 2 | **`llvm-toolchain`** (clang+lld+libc++ musl) | LTO, PGO, BOLT **y** el muro 3 | — |
| 3 | `firefox` con LTO+PGO (perfil en `hash_inputs`) | el eje de velocidad | 2 |
| 4 | **`atuq`: receta derivada + overlay de chrome** | el andamio de todo lo demás | 1 |
| 5 | Host de native messaging (tawasuyu) | los verbos del §6 | 4 |
| 6 | `sct` v1 (extensión + testigo) | el diferenciador que nadie tiene | 5 |
| 7 | Descargas al CAS | 6.2, y alimenta 6.9 | 5 |
| 8 | Archivo + RAG | 6.3 | 5, 7 |
| 9 | Proxy por contenedor, torrent, medios, foco | 6.56.9 | 5 |
Las unidades 2 y 4 son **paralelizables**: la toolchain no toca el chrome y el chrome no toca la
toolchain. Si hay dos frentes, van juntas.
## 9. Por qué esto no abandona a `puriy`
`puriy` es el motor DOM/CSS propio con JS real (QuickJS-NG sobre `wasmi`) cuyo límite declarado no es
el lenguaje sino **el wiring nativo**: DOM bindings completos, red, render. Eso no se acelera
escribiendo otro navegador; se acelera con tiempo.
`atuq` no le compite porque no comparte una sola línea con él, y **le sirve** porque todo lo del §6
vive del lado Rust: el host del §7, el testigo de `sct`, las descargas al CAS, el archivo con RAG.
Son **agnósticos del motor**. Cuando `puriy` madure, se enchufan del mismo lado sin reescribirse.
Dicho al revés, que es como conviene recordarlo: **`atuq` es el navegador que se puede usar mientras
`puriy` crece, y el andamio de las features que `puriy` va a heredar.** Si dentro de dos años `atuq`
se apaga, lo que se tira es una receta derivada y una hoja de CSS; todo lo demás sobrevive.
+1
View File
@@ -21,6 +21,7 @@ discrepancia, o se corrige el código o se actualiza el SDD con un commit que ex
| 11 | [Bootstrap from-scratch](11-bootstrap.md) | Track posterior: Stage 0/1/2, auto-alojamiento, semilla pinned |
| 12 | [`arje` como init real del Stage 1](12-init-real.md) | Contrato de runtime: seed card, hammerd supervisado, `CRASHED` real |
| 16 | [harkaq: la jaula de hammer](16-harkaq-jaula.md) | Landlock+seccomp sobre el bwrap actual; política = clausura; evidencia negativa de hermeticidad |
| 26 | [`atuq`: el envoltorio Gecko](26-atuq-envoltorio-gecko.md) | Navegador propio como artefacto DERIVADO de `firefox` (no fork de fuente); la toolchain clang como puerta de PGO/LTO; qué se promete y qué no |
### Runbooks (operativos)