atuq v0.5: proxy por contenedor — el primer diferenciador del §6 que se paga entero

Es el único de la lista del SDD 26 §6 que no pasa por el host de native
messaging del §7: es API de Firefox y nada más. Cada contenedor —Personal,
Trabajo, Banco, Compras— puede salir por su propio proxy.

Tres piezas y ninguna alcanza sola: `atuq.cfg` prende los contenedores (vienen
apagados), `policies.json` los CREA con `Containers.Default` —el único
mecanismo que los pone en un perfil NUEVO— y deja la config de fábrica en
`3rdparty.Extensions`, y `extensions/proxy/` los enruta con `proxy.onRequest`,
que es lo único que ve el `cookieStoreId` de la petición.

Las cuatro se comprobaron DENTRO del artefacto de firefox antes de escribir una
línea, no en la documentación de Mozilla: `Containers` y `3rdparty` en el
`policies-schema.json` de browser/omni.ja, `cookieStoreId` en el
`schemas/proxy.json` de omni.ja, y `storage.managed` leyendo
`getExtensionPolicy(id)` en ext-storage.js. Es la regla del §2.sexies: la
pregunta no es si Mozilla lo tiene, es si NUESTRO build lo tiene.

Tres decisiones que valen más que el código:

1. La config se indexa por NOMBRE de contenedor y no por `cookieStoreId`: el id
   depende del orden en que se crearon, así que la misma configuración aplicada
   a otro perfil apuntaría a otro contenedor.
2. FAIL CLOSED. Un contenedor con proxy configurado que no se pudo honrar no
   sale directo: va a un destino cerrado y el navegador muestra el error. Salir
   directo sería una fuga silenciosa — la misma familia que el artefacto vacío
   de la regla 3, el fallo que llega hasta el final diciendo que todo fue bien.
3. `proxyDNS` PRENDIDO por defecto: sin él la consulta DNS sale por la línea que
   se quería evitar. Es la fuga clásica de esta configuración.

Y lo que no promete está arriba de todo en la página de opciones, no en un pie:
separación de tráfico, NO anonimato; para anonimato, Tor Browser. El §4 cumplido
donde el usuario lo lee.

De paso, una verdad que estaba escrita en dos sitios pasa a tener un dueño: el
id de cada extensión sale ahora del `manifest.json` y no de una constante de
rebrand.py, el nombre del XPI se deriva de él, y el `install_url` de la política
se cruza contra la ruta donde el fichero quedó escrito de verdad. El icono se
inyecta desde branding/icons/ en vez de estar copiado byte a byte dentro de cada
extensión. Agregar una tercera extensión es ahora un directorio.

PROBADO, corriendo el árbol en la misma jaula que atuq-nested.sh:
  · captura de about:preferences#containers con los cuatro contenedores y sus
    iconos, más el containers.json del perfil;
  · extensions.json del perfil nombra las dos extensiones;
  · `console.info: "atuq/proxy: 4 contenedor(es) enrutado(s)"` — leyó la config
    de fábrica por storage.managed Y la casó con los contenedores de la política;
  · scripts/test-atuq-politica.py: cinco formas de desincronizar política y XPI,
    las cinco matan el build, y el control con la política intacta pasa.

NO probado y dicho por su nombre: que una petición hecha en «Banco» salga por el
proxy de «Banco». Pide automatizar la UI y queda pendiente.

Dos obstáculos del método, que valen para la próxima. La consola de una
extensión es CONTENIDO: `devtools.console.stdout.chrome` (que viene en true) no
la incluye, hace falta `...stdout.content`. Y un `moz-extension://` NO se abre
desde la línea de comandos —muere con `NS_NOINTERFACE [nsIFileURL.file]` y abre
la home en su lugar—, además de que `--screenshot` dispara al `load`, que puede
ocurrir antes de que arranquen las extensiones. Eso último destapó un fallo real
y arreglado: la página de opciones confundía «no hay contenedores» con «la API
no está» y mostraba un mensaje FALSO.

Verificado contra firefox b3:352d7880; se reconstruye contra el firefox con
RLBox cuando selle. La otra mitad de esta unidad —el rename de
recipes/atuq/extension/ a extensions/inicio/— entró sin querer en ff0b556, que
es de otro frente.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UgNtJEFetMYbXax5dVUjZZ
This commit is contained in:
Sergio
2026-09-06 01:57:44 +00:00
co-authored by Claude Opus 5
parent df4111c57d
commit c5ebbda933
11 changed files with 915 additions and 38 deletions
+71 -2
View File
@@ -269,7 +269,7 @@ La columna «quién más lo tiene» es lo que evita que nos contemos un cuento.
| 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.8 | **Proxy por contenedor****v0.5** | 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
@@ -309,6 +309,74 @@ eso; el modo incógnito es teatro. ⚠ Con la advertencia que el propio SDD de `
**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.
### 6.8 Proxy por contenedor — HECHO (v0.5, 2026-09-06)
**Es el primer diferenciador del §6 que se paga entero**, y se pudo pagar ahora porque es el único
de la lista que no pasa por el host del §7: es API de Firefox y nada más. Cada contenedor
—Personal, Trabajo, Banco, Compras— puede salir por su propio proxy.
Tres piezas, en tres ficheros, y **ninguna alcanza sola**:
| Pieza | Dónde | Qué aporta |
|---|---|---|
| `privacy.userContext.enabled` | `atuq.cfg` | prende los contenedores, que Firefox trae apagados |
| `Containers.Default` | `distribution/policies.json` | los CREA — es el único mecanismo que los pone en un perfil NUEVO |
| `3rdparty.Extensions` | `distribution/policies.json` | la config de fábrica, que la extensión lee como `storage.managed` |
| `proxy.onRequest` | `extensions/proxy/fondo.js` | lo único que ve el `cookieStoreId` de cada petición |
**Las cuatro se comprobaron DENTRO del artefacto antes de escribir una línea**, que es la regla que
dejó el §2.sexies: `Containers` y `3rdparty` están en el `policies-schema.json` de `browser/omni.ja`,
`cookieStoreId` en el `schemas/proxy.json` de `omni.ja`, y `storage.managed` lee
`Services.policies.getExtensionPolicy(id)` en `ext-storage.js`. Ninguna de las cuatro se leyó de la
documentación de Mozilla: se leyeron de **nuestro** build, que es el que las tiene que traer.
**Tres decisiones de diseño que valen más que el código:**
1. **La configuración se indexa por NOMBRE de contenedor, no por `cookieStoreId`.** El id
(`firefox-container-3`) depende del ORDEN en que se crearon, así que la misma configuración
aplicada a otro perfil apuntaría a otro contenedor. Un identificador que cambia de significado
entre máquinas no sirve para configurar una distro.
2. **Fail closed.** Un contenedor que TIENE proxy configurado y no se pudo honrar —entrada
inválida, API que falla, mapa a medio construir— **no sale directo**: va a un destino cerrado y
el navegador muestra el error. Salir directo sería una fuga silenciosa, y es la misma familia
que el artefacto vacío de la regla 3: el fallo que llega hasta el final diciendo que todo fue
bien. Un usuario que pidió que «Banco» no salga por su línea tiene que ver un error, no navegar.
3. **`proxyDNS` viene PRENDIDO.** Sin él, Gecko resuelve el nombre por su cuenta antes de hablar
con el proxy: la consulta DNS sale justo por la línea que se quería evitar. Es la fuga clásica de
esta configuración, así que el default es el seguro y hay que apagarlo a mano.
**Y lo que NO promete está en la propia página de opciones, arriba de todo y no en un pie**:
separación de tráfico, no anonimato; no toca la huella del navegador; para anonimato, Tor Browser.
Es el §4 cumplido en el sitio donde el usuario lo va a leer.
#### Lo que quedó PROBADO, y con qué
Corriendo el árbol de atuq en la misma jaula que usa `scripts/atuq-nested.sh`:
| Afirmación | Prueba |
|---|---|
| La política CREA los cuatro contenedores | captura de `about:preferences#containers` con Personal/Trabajo/Banco/Compras y sus iconos, más el `containers.json` del perfil |
| Las dos extensiones se INSTALAN | `extensions.json` del perfil nombra `inicio@atuq.tawasuyu` y `proxy@atuq.tawasuyu` |
| El ruteo se arma contra los contenedores reales | `console.info: "atuq/proxy: 4 contenedor(es) enrutado(s)"` — o sea que leyó la config de fábrica por `storage.managed` Y la casó con los contenedores que creó la política |
| Los guardianes del cruce política↔XPI FALLAN cuando deben | `scripts/test-atuq-politica.py`: 5 formas de romperlo, las 5 matan el build, y el control con la política intacta pasa |
**Lo que NO está probado, dicho por su nombre:** que una petición hecha en el contenedor «Banco»
salga por el proxy de «Banco». Eso pide automatizar la UI —una pestaña de contenedor no se abre
desde la línea de comandos— y queda pendiente en vez de darse por hecho.
**Dos obstáculos del método, que valen para la próxima:**
- **La consola de una extensión es CONTENIDO, no chrome.** `devtools.console.stdout.chrome` viene
en `true` de fábrica y NO la incluye; hace falta `devtools.console.stdout.content`. Sin esa pref
los mensajes de la extensión sólo viven en la consola del navegador, que en headless no mira
nadie.
- **Un `moz-extension://` NO se puede abrir desde la línea de comandos.** El manejador intenta
resolverlo como fichero y muere con `NS_NOINTERFACE [nsIFileURL.file]`, abriendo la home en su
lugar — o sea que la captura sale de otra página y parece que la tuya no funciona. Y `--screenshot`
dispara al `load`, que puede ocurrir ANTES de que termine el arranque de las extensiones: ahí el
global `browser` todavía no está inyectado. Eso destapó un fallo real y se arregló: la página de
opciones distinguía mal «no hay contenedores» de «la API no está» y mostraba un mensaje FALSO.
### 2.ter Dónde vive la fuente de `atuq` — hizo falta un modo de `[source]` nuevo
Escribir la receta destapó un muro que este documento no había visto: `[source]` era
@@ -461,7 +529,8 @@ siguiente.
| 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 |
| 9 | Torrent, medios, foco | 6.56.7, 6.9 | 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
toolchain. Si hay dos frentes, van juntas.