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 604Rust 56Shell 36Markdown 281227 Sutichaykuna

firefox: BuildID determinista — se cierra la deuda de reproducibilidad, con guardián

Medido y probado hoy: `atuq` REPRODUCE bit a bit (`why-differs`: 85 entradas idénticas, 0 divergen),
lo que valida el re-empaque determinista del omni.ja, el XPI, los iconos copiados y el BuildID
derivado del contenido. El que NO reproducía era la BASE: el `BuildID` de firefox era la fecha y
hora del build (`20260905035306` en el artefacto sellado), así que dos construcciones con el mismo
ArtifactHash daban bytes distintos.

Es el agujero exacto que el invariante de reproducibilidad existe para cerrar, y `build-state.json`
no lo puede ver: el hash es input-addressed y no se mueve por esto. Verde y mintiendo.

El valor sale de `SOURCE_DATE_EPOCH`, que el sandbox de hammer YA fija a 1 para todas las recetas —
mejor que una constante escrita a mano, porque usa el mecanismo de hermeticidad que ya existe en vez
de inventar un segundo. Alpine hace lo mismo en su APKBUILD. BuildID esperado: 19700101000001.

Va en LAS TRES FASES, por la lección que costó 50 minutos esta misma tarde con MOZ_REQUIRE_SIGNING:
son shells distintos y `mach build` re-ejecuta el configure cuando le parece.

Y con su GUARDIÁN, hermano del de la firma: la fase install compara el BuildID del artefacto contra
el que SOURCE_DATE_EPOCH obliga, y falla el build si no coinciden. Los dos fallos de esta familia
—flag que el configure acepta y el artefacto ignora— son mudos y viajan lejos; la única defensa es
comprobarlos donde importa, que es dentro del artefacto.

5e489e974ef0429d90b13a2a4939ec1347616fb6 · ruwaq Sergio · (1 killa ñawpaq) · tayta db7fd8123 · kay commitpi sach'ata qhaway

Semántico sayk'uy

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

1 qillqakuna · +27 −10

diff --git a/recipes/firefox.toml b/recipes/firefox.toml
index a46d7e04..4d16baf5 100644
--- a/recipes/firefox.toml
+++ b/recipes/firefox.toml
@@ -133,16 +133,16 @@ flags    = []
 # Entra en `hash_inputs` A PROPÓSITO (cambia el contenido del artefacto), y usa `zig objcopy`, que
 # siempre está en el sandbox ⇒ no agrega una dep de build.
 strip_debug = true
-# ⚠ DEUDA MEDIDA EL 2026-09-05: ESTE ARTEFACTO NO REPRODUCE BIT A BIT ═══════════════════════════
-# `application.ini` del artefacto sellado trae `BuildID=20260905035306`: la FECHA Y HORA del build.
-# Dos construcciones de esta misma receta, con el mismo hash, producen bytes distintos — que es
-# exactamente el agujero que el invariante de reproducibilidad existe para cerrar, y no se ve en
-# `build-state.json` porque el ArtifactHash es input-addressed y no se mueve.
-# El arreglo es el que ya usa Alpine: exportar `MOZ_BUILD_DATE` con un valor determinista en las
-# fases (su APKBUILD lo deriva de `SOURCE_DATE_EPOCH`; acá el candidato natural es un valor fijo,
-# que al vivir en el texto de la fase entra en `hash_inputs` y queda declarado).
-# NO se aplica de paso: cambiar una fase re-hashea la receta y cuesta otro build de cuatro horas
-# más el re-sellado de `atuq`, que cuelga de ella. Es su propia unidad de trabajo.
+# ══ `MOZ_BUILD_DATE`: SIN ESTO EL ARTEFACTO NO REPRODUCE ══════════════════════════════════════
+# El `BuildID` de `application.ini` es, por defecto, la FECHA Y HORA del build. Medido en el
+# artefacto sellado del 2026-09-05: `BuildID=20260905035306`. Dos construcciones de esta misma
+# receta, con el mismo ArtifactHash, producían bytes distintos — justo el agujero que el invariante
+# de reproducibilidad existe para cerrar, y que `build-state.json` no puede ver porque el hash es
+# input-addressed y no se mueve por esto.
+#
+# El valor sale de `SOURCE_DATE_EPOCH`, que el sandbox de hammer YA fija a 1 para todas las recetas.
+# Es mejor que una constante escrita a mano: usa el mecanismo de hermeticidad que ya existe, en vez
+# de inventar un segundo. Alpine hace exactamente lo mismo en su APKBUILD.
 
 [build.phases]
 configure = '''
@@ -183,6 +183,7 @@ export RUST_TARGET=x86_64-alpine-linux-musl
 # El `export` con valor vacío es lo que hace llegar la variable PRESENTE y vacía; desexportarla la
 # dejaría sin definir y volvería al default, que acá es «exigir firma».
 export MOZ_REQUIRE_SIGNING=
+export MOZ_BUILD_DATE="$(date -u -d "@${SOURCE_DATE_EPOCH:-1}" +%Y%m%d%H%M%S)"
 
 # ── LOS PARCHES ROMPEN EL CHECKSUM DE LOS CRATES VENDORIZADOS ─────────────────────────────────
 # `error: the listed checksum of third_party/rust/zeitstempel/src/unix.rs has changed`. Los crates
@@ -275,6 +276,7 @@ export RUST_TARGET=x86_64-alpine-linux-musl   # ver la nota en la fase configure
 # aceptó — 50 minutos de build para descubrirlo al abrir el navegador. Es exactamente la razón por
 # la que `RUST_TARGET` se repite en las tres fases.
 export MOZ_REQUIRE_SIGNING=
+export MOZ_BUILD_DATE="$(date -u -d "@${SOURCE_DATE_EPOCH:-1}" +%Y%m%d%H%M%S)"   # ídem: ver configure
 export MOZBUILD_STATE_PATH="$PWD/.mozbuild"
 export MACH_BUILD_PYTHON_NATIVE_PACKAGE_SOURCE=system
 export MOZ_NOSPAM=1
@@ -287,6 +289,7 @@ echo "mach build -j$j (MemTotal ${gib} GiB, nproc $n)"
 install = '''
 export RUST_TARGET=x86_64-alpine-linux-musl   # ver la nota en la fase configure
 export MOZ_REQUIRE_SIGNING=                   # ídem
+export MOZ_BUILD_DATE="$(date -u -d "@${SOURCE_DATE_EPOCH:-1}" +%Y%m%d%H%M%S)"   # ídem
 export MOZBUILD_STATE_PATH="$PWD/.mozbuild"
 export MACH_BUILD_PYTHON_NATIVE_PACKAGE_SOURCE=system
 DESTDIR=/out ./mach install
@@ -310,6 +313,20 @@ if "MOZ_REQUIRE_SIGNING: false" not in t:
              "las fases (son shells distintos y mach re-ejecuta configure).")
 print("guardián: MOZ_REQUIRE_SIGNING = false en el artefacto")
 PY
+
+# ── GUARDIÁN: QUE EL BuildID NO SEA UNA FECHA ────────────────────────────────────────────────
+# Mismo razonamiento que el de arriba, y el mismo modo de fallo: si `MOZ_BUILD_DATE` no llega, el
+# BuildID vuelve a ser la hora del build y el artefacto deja de reproducir SIN QUE NADA LO DIGA —
+# el ArtifactHash es input-addressed y no se mueve por esto, así que `build-state.json` seguiría en
+# verde. Se comprueba contra el valor que `SOURCE_DATE_EPOCH=1` obliga.
+esperado="$(date -u -d "@${SOURCE_DATE_EPOCH:-1}" +%Y%m%d%H%M%S)"
+real="$(sed -n 's/^BuildID=//p' /out/usr/lib/firefox/application.ini)"
+if [ "$real" != "$esperado" ]; then
+  echo "guardián: BuildID=$real y esperaba $esperado — el artefacto NO reproduce." >&2
+  echo "          Revisá que MOZ_BUILD_DATE se exporte en TODAS las fases." >&2
+  exit 1
+fi
+echo "guardián: BuildID = $real (determinista, de SOURCE_DATE_EPOCH)"
 '''
 
 [deps]