tawasuyu

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1534Shell 199Rust 109Markdown 993713 Sutichaykuna

atuq 0.1.0: la receta del envoltorio, derivada de firefox

Primera receta del navegador de la distro (SDD 26). No compila nada: depende de `firefox`, copia su
árbol y le pone encima cuatro ficheros. Hash b3:2d6e4dcf.

LA CAPA SON CUATRO FICHEROS Y CADA UNO ESTÁ DONDE ESTÁ POR UNA RAZÓN:
- `defaults/pref/autoconfig.js` — el único gancho que corre ANTES de que exista un perfil.
- `atuq.cfg` — prefs de fábrica + carga del chrome. Con `defaultPref` y no `lockPref`: son valores
  de arranque, no una cárcel; lo que de verdad se bloquea va en policies.json.
- `chrome/atuq.css` — el aspecto.
- `distribution/policies.json` — telemetría, updates, primer arranque.

POR QUÉ EL CSS NO ES `userChrome.css`: ese fichero vive en el PERFIL y exige que el usuario prenda
`toolkit.legacyUserProfileCustomizations.stylesheets`. atuq tiene que verse como atuq en el primer
arranque, con un perfil recién creado, sin que nadie prenda nada. El único gancho a nivel de
APLICACIÓN es nsIStyleSheetService desde autoconfig — y llegar a él es la razón de
`sandbox_enabled = false`. Se registra como USER_SHEET, el mismo nivel de cascada que userChrome,
para que el usuario pueda seguir pisándolo.

DOS TRAMPAS DEL FORMATO, ESCRITAS EN EL PROPIO FICHERO:
- la PRIMERA LÍNEA de un autoconfig se ignora siempre, por diseño (herencia de Netscape). Código en
  la línea 1 no corre y no da error: el fallo más caro de ese fichero es el que no dice nada.
- el CSS va acotado con @-moz-document a browser.xhtml. Una hoja USER sin acotar alcanza CUALQUIER
  documento chrome —visor de PDF, inspector, diálogos— y un selector como `toolbar` termina pintando
  ventanas que nadie miró.

`extensions.autoDisableScopes = 0` es la otra mitad del `--with-unsigned-addon-scopes` que ya viaja
en el build de firefox: sin los dos, las extensiones de la distro se instalan DESACTIVADAS.

LAS ASERCIONES VAN PRIMERO Y FALLAN RUIDOSAS. Un derivado que no encuentra su base produciría un
directorio con cuatro ficheros de config, `Store::has` lo daría por presente y el fallo aparecería
el día que alguien abra el navegador (regla 3 del CLAUDE.md). Y al final imprime el inventario de la
capa con sus tamaños, que es la primera pregunta del diagnóstico cuando «atuq se ve como Firefox».

LO QUE LA v0.1 NO HACE, A PROPÓSITO: no renombra el binario ni toca application.ini, y no
re-empaqueta omni.ja. Las dos cosas necesitan mirar la forma real del árbol que selle `firefox`, y
adivinarla desde acá daría un artefacto que arranca en una máquina y en ninguna otra.

No se puede construir todavía: su dep `firefox` está en vuelo.

aeebb194dc1368739d35ebd08023b0df0ce12373 · ruwaq Sergio · (1 killa ñawpaq) · tayta 54fa4a941 · kay commitpi sach'ata qhaway

Semántico sayk'uy

Mana ima sutichaypas t'ikrakurqanchu: qillqalla, yachachiylla utaq willaylla t'ikrakurqan.

6 qillqakuna · +238 −0

diff --git a/recipes/atuq.toml b/recipes/atuq.toml
new file mode 100644
index 00000000..6a243c75
--- /dev/null
+++ b/recipes/atuq.toml
@@ -0,0 +1,89 @@
+# atuq 0.1.0 — el navegador de la distro, como ARTEFACTO DERIVADO de firefox.
+#
+# ══ POR QUÉ DERIVADO Y NO UN FORK DE FUENTE ════════════════════════════════════════════════════
+# Zen —el fork de Firefox al que esto reemplaza en nuestro catálogo— parchea el árbol de Gecko y
+# recompila. Nosotros no distribuimos un binario: sellamos artefactos. Entonces atuq puede DEPENDER
+# de `firefox` y poner su capa encima, y eso cambia el precio de todo:
+#   · una iteración de chrome cuesta SEGUNDOS, no las cuatro horas de un build de Gecko;
+#   · no mueve el `ArtifactHash` de `firefox` ⇒ el corpus no se invalida;
+#   · las CVE se heredan gratis: cuando sube `firefox`, atuq se reconstruye solo.
+# El razonamiento completo, con lo que se promete y lo que NO (spoiler: no habrá «modo Tor»), está
+# en `docs/26-atuq-envoltorio-gecko.md`.
+#
+# ══ SU FUENTE ES ESTE REPO ═════════════════════════════════════════════════════════════════════
+# `source.dir` es el modo de fuente que se agregó para esto (hammer-core/src/recipe.rs): el árbol
+# vive en `recipes/atuq/` y se hashea por CONTENIDO con `ArtifactHash::of_tree`, igual que ya se
+# hacía con los `patches`. No hay commit que pinear y no hay fetch: editar un CSS mueve el hash.
+#
+# ══ LO QUE ESTA v0.1 NO HACE, A PROPÓSITO ══════════════════════════════════════════════════════
+# No renombra el binario ni toca `application.ini`. El branding necesita mirar la forma real del
+# árbol que sella `firefox` —qué es `firefox-bin`, cómo se resuelve el appdir— y adivinarlo desde
+# acá produciría un artefacto que arranca en la máquina de quien lo escribió y en ninguna otra.
+# Tampoco re-empaqueta `omni.ja`, que es donde vive el chrome de verdad; eso es la v0.2 y trae dos
+# condiciones ya sabidas (re-empacar determinista y preservar el orden del `jarlog` del PGO).
+name    = "atuq"
+version = "0.1.0"
+# El artefacto CONTIENE Firefox, así que hereda su licencia. El overlay de `recipes/atuq/` es
+# nuestro, pero eso no cambia lo que se distribuye.
+license = "MPL-2.0"
+
+[source]
+dir = "atuq"
+
+[build]
+# No se compila nada: esta receta copia y edita ficheros. `compiler` y `link` entran igual en el
+# hash, así que se dejan en el default del corpus para no inventar una identidad que no significa
+# nada.
+target = "x86_64-linux-musl"
+
+[build.phases]
+configure = "echo 'atuq: nada que configurar — es un artefacto derivado'"
+compile   = "echo 'atuq: nada que compilar — el motor lo pone la dep firefox'"
+install = '''
+set -e
+SRC=/usr/lib/firefox
+DST=/out/usr/lib/atuq
+
+# ── LAS ASERCIONES VAN PRIMERO, Y FALLAN RUIDOSAS ─────────────────────────────────────────────
+# Un derivado que no encuentra su base no debe producir un artefacto flaco: produciría un
+# directorio con cuatro ficheros de configuración, `Store::has` lo daría por presente y el fallo
+# aparecería el día que alguien intente abrir el navegador. Un ausente falla fuerte; un vacío llega
+# hasta el final diciendo que todo fue bien (regla 3 del CLAUDE.md).
+[ -d "$SRC" ] || { echo "atuq: no está el árbol de firefox en $SRC — ¿la dep se materializó?" >&2; exit 1; }
+[ -x "$SRC/firefox" ] || { echo "atuq: $SRC/firefox no existe o no es ejecutable" >&2; exit 1; }
+[ -d "$SRC/browser" ] || { echo "atuq: falta $SRC/browser — el árbol no tiene la forma esperada" >&2; exit 1; }
+
+mkdir -p "$DST"
+cp -a "$SRC"/. "$DST"/
+
+# ── LA CAPA DE atuq ───────────────────────────────────────────────────────────────────────────
+# `defaults/pref/` es el único sitio desde el que se puede pedir el autoconfig ANTES de que exista
+# un perfil; el resto cuelga de ahí.
+mkdir -p "$DST/defaults/pref" "$DST/chrome" "$DST/distribution"
+cp /src/prefs/autoconfig.js       "$DST/defaults/pref/autoconfig.js"
+cp /src/atuq.cfg                  "$DST/atuq.cfg"
+cp /src/chrome/atuq.css           "$DST/chrome/atuq.css"
+cp /src/distribution/policies.json "$DST/distribution/policies.json"
+
+# El comando que teclea la gente. Apunta al binario SIN renombrar (ver la nota de la cabecera):
+# renombrarlo es branding y branding es la v0.2.
+mkdir -p /out/usr/bin
+ln -sf ../lib/atuq/firefox /out/usr/bin/atuq
+
+# ── INVENTARIO: LO QUE SE MIRA CUANDO «ATUQ SE VE COMO FIREFOX» ───────────────────────────────
+# Los cuatro ficheros de la capa son el 100% de lo que distingue este artefacto de su base, y son
+# los que un `cp` silencioso podría no haber puesto. Se listan para que el log del build responda
+# solo la primera pregunta del diagnóstico.
+echo "atuq: capa aplicada —"
+for f in defaults/pref/autoconfig.js atuq.cfg chrome/atuq.css distribution/policies.json; do
+  [ -s "$DST/$f" ] || { echo "atuq: $f quedó VACÍO o ausente" >&2; exit 1; }
+  echo "  $f ($(wc -c < "$DST/$f") bytes)"
+done
+'''
+
+[deps]
+# `firefox` como dep de BUILD y no de runtime: su árbol se materializa en el sandbox, se copia
+# dentro del artefacto de atuq y a partir de ahí atuq no lo necesita más. El precio es que el
+# artefacto pesa lo que pesa Firefox otra vez; la salida barata (una granja de symlinks) rompe la
+# resolución del appdir por /proc/self/exe, así que la v0.1 paga el disco y lo dice.
+build = ["firefox"]
diff --git a/recipes/atuq/README.md b/recipes/atuq/README.md
new file mode 100644
index 00000000..aac8c05c
--- /dev/null
+++ b/recipes/atuq/README.md
@@ -0,0 +1,33 @@
+# atuq — el envoltorio Gecko de la distro
+
+`atuq` (quechua: *zorro*) es el navegador de la distro: **un artefacto DERIVADO de `firefox`**, no un
+fork de su fuente. El diseño completo está en [`docs/26-atuq-envoltorio-gecko.md`](../../docs/26-atuq-envoltorio-gecko.md).
+
+Este directorio ES la fuente de atuq — `recipes/atuq.toml` lo declara con `source.dir` y hammer lo
+hashea por CONTENIDO. Editar un fichero de acá mueve el `ArtifactHash`; no hay commit que pinear.
+
+## Qué hay acá y dónde cae
+
+| Acá | En el artefacto | Qué hace |
+|---|---|---|
+| `prefs/autoconfig.js` | `defaults/pref/autoconfig.js` | Le dice a Gecko que lea `atuq.cfg`. Es el único gancho posible antes de que arranque el perfil. |
+| `atuq.cfg` | `<appdir>/atuq.cfg` | Prefs de fábrica + carga la hoja de estilo del chrome. |
+| `chrome/atuq.css` | `<appdir>/chrome/atuq.css` | El aspecto. Se inyecta como AGENT_SHEET desde `atuq.cfg`. |
+| `distribution/policies.json` | `<appdir>/distribution/policies.json` | Política de fábrica: telemetría, updates, scope de extensiones. |
+
+## Por qué el CSS no es `userChrome.css`
+
+`userChrome.css` vive en el PERFIL del usuario y exige que el usuario prenda
+`toolkit.legacyUserProfileCustomizations.stylesheets`. Una distro no puede depender de eso: el
+navegador tiene que verse como atuq la primera vez que arranca, con un perfil recién creado. La vía
+que sí funciona a nivel de aplicación es el `nsIStyleSheetService` desde autoconfig, que es lo que
+hace `atuq.cfg`.
+
+## Lo que TODAVÍA no está
+
+- **Branding** (nombre, iconos, `application.ini`): v0.2. Necesita mirar el artefacto real de
+  `firefox` para no adivinar la forma del árbol.
+- **Re-empaque de `omni.ja`**: es donde va el chrome de verdad (split view, dientes propios). Trae
+  dos condiciones ya sabidas: re-empacar determinista, y **preservar el orden del `jarlog`** cuando
+  el PGO exista, o se tira a la basura la optimización de arranque.
+- **La extensión de `sct`** y el host de native messaging (SDD 26 §6.1 y §7).
diff --git a/recipes/atuq/atuq.cfg b/recipes/atuq/atuq.cfg
new file mode 100644
index 00000000..31124a25
--- /dev/null
+++ b/recipes/atuq/atuq.cfg
@@ -0,0 +1,66 @@
+// ⚠ LA PRIMERA LÍNEA DE UN FICHERO AUTOCONFIG SE IGNORA — SIEMPRE, POR DISEÑO.
+// El parser de Gecko salta la línea 1 sin mirarla (herencia del formato de Netscape). Si acá
+// hubiera código en vez de este comentario, no correría y no habría ningún error: el fallo más caro
+// de este fichero es el que no dice nada. Por eso la línea 1 es, deliberadamente, un comentario.
+
+// ── PREFS DE FÁBRICA ───────────────────────────────────────────────────────────────────────────
+// `defaultPref` y no `lockPref`: son valores de arranque, no una cárcel. Una distro que decide POR
+// el usuario y no lo deja cambiar de idea es exactamente lo que atuq no quiere ser. Lo que sí se
+// bloquea con política de verdad va en `distribution/policies.json`.
+
+// Los dientes: pestañas verticales, que Firefox trae nativas desde la 133 y vienen apagadas.
+defaultPref("sidebar.revamp", true);
+defaultPref("sidebar.verticalTabs", true);
+
+// La distro se actualiza con hammer. Un navegador que se auto-actualiza por su cuenta rompería el
+// invariante entero: el binario dejaría de ser el que el artefacto dice que es.
+defaultPref("app.update.auto", false);
+defaultPref("browser.shell.checkDefaultBrowser", false);
+
+// Telemetría. No es «apagar una casilla»: es que la distro no reporta, punto. Lo verificable de
+// verdad —que la clausura no tenga a dónde reportar— es otro frente (SDD 26 §6); esto es el suelo.
+defaultPref("datareporting.healthreport.uploadEnabled", false);
+defaultPref("datareporting.policy.dataSubmissionEnabled", false);
+defaultPref("toolkit.telemetry.enabled", false);
+defaultPref("toolkit.telemetry.unified", false);
+defaultPref("toolkit.telemetry.archive.enabled", false);
+
+// Extensiones que la distro deja en `distribution/extensions/`: sin esto Gecko las instala
+// DESACTIVADAS y el usuario ve una lista de complementos apagados que nunca pidió apagar.
+// El otro lado de esto es `--with-unsigned-addon-scopes=app,system`, que va en el CONFIGURE de
+// `recipes/firefox.toml` y por eso no se puede resolver desde acá.
+defaultPref("extensions.autoDisableScopes", 0);
+
+// ── EL CHROME ──────────────────────────────────────────────────────────────────────────────────
+// Por qué acá y no en `userChrome.css`: ese fichero vive en el PERFIL y exige que el usuario prenda
+// `toolkit.legacyUserProfileCustomizations.stylesheets`. atuq tiene que verse como atuq en el
+// primer arranque, con un perfil recién creado, sin que nadie prenda nada. El único gancho a nivel
+// de APLICACIÓN es nsIStyleSheetService, y llegar a él es la razón de `sandbox_enabled = false`.
+//
+// USER_SHEET y no AGENT_SHEET: es el mismo nivel de cascada que usa `userChrome.css`, así que lo
+// que se escriba en `chrome/atuq.css` se comporta como la gente espera y el usuario puede seguir
+// pisándolo con el suyo. AGENT_SHEET ganaría siempre y además alcanzaría al CONTENIDO de las
+// páginas, que no es lo que queremos tocar.
+try {
+  const { classes: Cc, interfaces: Ci } = Components;
+  const sss = Cc["@mozilla.org/content/style-sheet-service;1"]
+    .getService(Ci.nsIStyleSheetService);
+  const ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+  const dirs = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
+  // "GreD" = el directorio de la aplicación (donde vive el binario). En una instalación normal es
+  // el mismo que "XCurProcD"; se usa GreD porque es el que no cambia si algún día atuq se lanza
+  // con un `-app`.
+  const css = dirs.get("GreD", Ci.nsIFile);
+  css.append("chrome");
+  css.append("atuq.css");
+  if (css.exists()) {
+    const uri = ios.newFileURI(css);
+    if (!sss.sheetRegistered(uri, sss.USER_SHEET)) {
+      sss.loadAndRegisterSheet(uri, sss.USER_SHEET);
+    }
+  }
+} catch (e) {
+  // Un fallo acá NO debe impedir que el navegador arranque: sin estilo es feo, sin navegador no hay
+  // nada. Queda en la consola de errores, que es donde se mira cuando «atuq se ve como Firefox».
+  Components.utils.reportError("atuq.cfg: no pude registrar chrome/atuq.css: " + e);
+}
diff --git a/recipes/atuq/chrome/atuq.css b/recipes/atuq/chrome/atuq.css
new file mode 100644
index 00000000..98f23bf3
--- /dev/null
+++ b/recipes/atuq/chrome/atuq.css
@@ -0,0 +1,29 @@
+/* atuq.css — el aspecto de atuq. Se registra como USER_SHEET desde `atuq.cfg`.
+ *
+ * ⚠ TODO lo de acá va acotado con @-moz-document a la ventana del navegador. Una hoja de nivel
+ * USER sin acotar alcanza CUALQUIER documento chrome —el visor de PDF, el inspector, los diálogos—
+ * y un selector genérico como `toolbar` termina pintando ventanas que nadie miró nunca.
+ *
+ * v0.1: deliberadamente conservador. El chrome de verdad (dientes propios, split view) vive en el
+ * re-empaque de `omni.ja`, que es la v0.2 y necesita el artefacto real de firefox delante.
+ */
+
+@-moz-document url("chrome://browser/content/browser.xhtml") {
+  :root {
+    /* El acento de la distro. Un solo token para que el resto cuelgue de acá y no haya seis
+     * literales de color desperdigados que se desincronizan en la primera edición. */
+    --atuq-acento: #b5651d;
+    --atuq-radio: 8px;
+  }
+
+  /* La pestaña activa: identidad visible sin reescribir la barra entera. */
+  #tabbrowser-tabs .tabbrowser-tab[selected] .tab-background {
+    border-radius: var(--atuq-radio);
+    box-shadow: inset 0 -2px 0 0 var(--atuq-acento);
+  }
+
+  /* La barra de direcciones enfocada usa el acento en vez del azul de Firefox. */
+  #urlbar[focused="true"] > #urlbar-background {
+    outline-color: var(--atuq-acento);
+  }
+}
diff --git a/recipes/atuq/distribution/policies.json b/recipes/atuq/distribution/policies.json
new file mode 100644
index 00000000..d929a12d
--- /dev/null
+++ b/recipes/atuq/distribution/policies.json
@@ -0,0 +1,11 @@
+{
+  "policies": {
+    "DisableTelemetry": true,
+    "DisableFirefoxStudies": true,
+    "DisableAppUpdate": true,
+    "DontCheckDefaultBrowser": true,
+    "DisableProfileImport": false,
+    "OverrideFirstRunPage": "",
+    "OverridePostUpdatePage": ""
+  }
+}
diff --git a/recipes/atuq/prefs/autoconfig.js b/recipes/atuq/prefs/autoconfig.js
new file mode 100644
index 00000000..96712f7d
--- /dev/null
+++ b/recipes/atuq/prefs/autoconfig.js
@@ -0,0 +1,10 @@
+// Le dice a Gecko que lea `atuq.cfg` del directorio de la aplicación.
+//
+// `general.config.obscure_value = 0` porque el formato histórico esperaba el fichero "ofuscado"
+// con un ROT-13 que no protege nada; ponerlo en 0 es lo que permite que `atuq.cfg` sea texto plano
+// legible y revisable, que es lo que queremos para una política de distro.
+// `sandbox_enabled = false` da acceso a los componentes XPCOM desde el .cfg: sin eso no se puede
+// registrar la hoja de estilo del chrome, que es la razón principal de que este fichero exista.
+pref("general.config.filename", "atuq.cfg");
+pref("general.config.obscure_value", 0);
+pref("general.config.sandbox_enabled", false);