gioser

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 815Rust 56Shell 37Markdown 291229 Sutichaykuna

granja: las tres `wanted` del perfil servidor, construidas — y popt, que faltaba debajo

`targets.toml` declaraba `chrony`, `cronie` y `logrotate` como raíces del perfil `servidor` con el
motivo escrito al lado, y ninguna tenía receta: el grafo las contaba como `wanted` y ésa era toda la
deuda que le quedaba al perfil. Ahora sellan las cuatro (popt es la hoja que `logrotate` exige:
incluye `<popt.h>` en la primera pantalla y su configure aborta sin él).

    servidor  97/97 → 101/101 listo, falta 0.  `wanted` desaparece de los totales.

Lo que se midió, no se supuso:
  · logrotate  estático, CERO NEEDED, corre, y las rutas de gzip quedan en `/bin` — que es donde
    busybox las deja de verdad; el default de upstream en Linux es `/usr/bin/gzip`, que en esta
    distro NO EXISTE y habría fallado en runtime diciendo «no se pudo comprimir».
  · chrony     `+CMDMON +REFCLOCK +RTC +PRIVDROP +IPV6`; `cap_set_proc` está en el ELF, o sea que
    libcap entró de verdad y chronyd suelta privilegios. Sale `-NTS -SECHASH` a propósito: NTS
    necesita nettle o gnutls y ninguna está en el corpus.
  · cronie     los cuatro binarios estáticos sin NEEDED, con inotify dentro, y `/bin/vi` pineado.

**El hilo que recorre las tres recetas es el mismo, y es el que valía la pena escribir:** los tres
`configure` DECIDEN MIRANDO EL LAB. `logrotate` trae `--with-selinux/--with-acl` en `[default=check]`;
`chrony` prueba nettle, gnutls, libcap, seccomp y editline; `cronie` resuelve el editor de
`crontab -e` con `AC_PATH_PROG([vi])` y lo graba en el binario. El lab NO entra en `hash_inputs` ⇒
dos labs distintos sellarían bytes distintos en la MISMA dirección del store y nada lo notaría.
Cada palanca va fijada en la receta —que sí entra en el hash— para que sea una decisión y no un
accidente del entorno.

Y una anotada en vez de tapada: cronie no reemplaza al `crond` de busybox por reflejo (busybox ya lo
pone en `/sbin`); agrega `@reboot`, crontabs por usuario, `/etc/cron.d` y anacron. Cuál va en cada
imagen es decisión de perfil — lo que faltaba era que la opción existiera en el corpus.

cc5aebd8c40949d99dbf674408c9a5e56c1a2afc · ruwaq Sergio · (1 killa ñawpaq) · tayta 8ad9f0490 · kay commitpi sach'ata qhaway

Semántico sayk'uy

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

5 qillqakuna · +304 −43

diff --git a/docs/state/build-state.json b/docs/state/build-state.json
index 6aa0aceb..1d76f3de 100644
--- a/docs/state/build-state.json
+++ b/docs/state/build-state.json
@@ -1,27 +1,24 @@
 {
  "schema": "hammer-build-state/1",
  "totals": {
-  "recipes": 877,
-  "nodes": 882,
-  "sealed": 877,
-  "ajeno": 2,
-  "wanted": 3
+  "recipes": 881,
+  "nodes": 883,
+  "sealed": 881,
+  "ajeno": 2
  },
  "by_class": {
   "ajeno": 2,
-  "c": 234,
+  "c": 238,
   "go": 362,
   "gui": 41,
   "kernel": 6,
-  "rust": 234,
-  "wanted": 3
+  "rust": 234
  },
  "debt_by_class": {},
  "by_queue": {
   "corpus": {
-   "sealed": 877,
-   "ajeno": 2,
-   "wanted": 3
+   "sealed": 881,
+   "ajeno": 2
   }
  },
  "by_profile": {
@@ -42,11 +39,11 @@
   },
   "servidor": {
    "raices": 61,
-   "clausura": 97,
-   "sealed": 97
+   "clausura": 101,
+   "sealed": 101
   }
  },
- "sin_perfil": 646,
+ "sin_perfil": 643,
  "orphan_deps": [],
  "topo_ok": true,
  "nodes": {
@@ -1008,7 +1005,7 @@
     "escritorio-sway",
     "servidor"
    ],
-   "dependientes_total": 48
+   "dependientes_total": 52
   },
   "biome": {
    "iname": "biome",
@@ -1285,7 +1282,7 @@
     "escritorio-sway",
     "servidor"
    ],
-   "dependientes_total": 8
+   "dependientes_total": 12
   },
   "bwrap": {
    "iname": "bwrap",
@@ -2444,17 +2441,26 @@
   },
   "chrony": {
    "iname": "chrony",
-   "version": null,
-   "link": null,
-   "compiler": null,
-   "deps": [],
-   "cls": "wanted",
+   "version": "4.9",
+   "link": "static",
+   "compiler": "zig-cc",
+   "deps": [
+    "binutils",
+    "busybox",
+    "libcap",
+    "make",
+    "pkgconf"
+   ],
+   "cls": "c",
    "queue": "corpus",
-   "hash": null,
-   "state": "wanted",
+   "foreign": false,
+   "hash": "b3:b9e26374f000ef5820bcc32bdb761b2b3e7306d1ed400c147271cfdbd77557e9",
+   "state": "sealed",
    "blocked_by": [],
    "unblocks": 0,
-   "perfiles": [],
+   "perfiles": [
+    "servidor"
+   ],
    "dependientes_total": 0
   },
   "cilium-cli": {
@@ -2871,17 +2877,25 @@
   },
   "cronie": {
    "iname": "cronie",
-   "version": null,
-   "link": null,
-   "compiler": null,
-   "deps": [],
-   "cls": "wanted",
+   "version": "1.7.2",
+   "link": "static",
+   "compiler": "zig-cc",
+   "deps": [
+    "binutils",
+    "busybox",
+    "make",
+    "pkgconf"
+   ],
+   "cls": "c",
    "queue": "corpus",
-   "hash": null,
-   "state": "wanted",
+   "foreign": false,
+   "hash": "b3:387347b89b107d97febbb2692ce2d292e74bc7b1fba105676befe396cc3ebf21",
+   "state": "sealed",
    "blocked_by": [],
    "unblocks": 0,
-   "perfiles": [],
+   "perfiles": [
+    "servidor"
+   ],
    "dependientes_total": 0
   },
   "crossplane-cli": {
@@ -9313,7 +9327,7 @@
     "escritorio-sway",
     "servidor"
    ],
-   "dependientes_total": 3
+   "dependientes_total": 4
   },
   "libdisplay-info": {
    "iname": "libdisplay-info",
@@ -10521,17 +10535,26 @@
   },
   "logrotate": {
    "iname": "logrotate",
-   "version": null,
-   "link": null,
-   "compiler": null,
-   "deps": [],
-   "cls": "wanted",
+   "version": "3.22.0",
+   "link": "static",
+   "compiler": "zig-cc",
+   "deps": [
+    "binutils",
+    "busybox",
+    "make",
+    "pkgconf",
+    "popt"
+   ],
+   "cls": "c",
    "queue": "corpus",
-   "hash": null,
-   "state": "wanted",
+   "foreign": false,
+   "hash": "b3:09698cb83519dc2423b8aa1905fa5803955ee1c79598590c30ff5b5b14e5e84d",
+   "state": "sealed",
    "blocked_by": [],
    "unblocks": 0,
-   "perfiles": [],
+   "perfiles": [
+    "servidor"
+   ],
    "dependientes_total": 0
   },
   "lsd": {
@@ -10763,7 +10786,7 @@
     "escritorio-sway",
     "servidor"
    ],
-   "dependientes_total": 124
+   "dependientes_total": 128
   },
   "mako": {
    "iname": "mako",
@@ -13127,7 +13150,7 @@
     "escritorio-sway",
     "servidor"
    ],
-   "dependientes_total": 406
+   "dependientes_total": 410
   },
   "plow": {
    "iname": "plow",
@@ -13308,6 +13331,29 @@
    "perfiles": [],
    "dependientes_total": 0
   },
+  "popt": {
+   "iname": "popt",
+   "version": "1.19",
+   "link": "static",
+   "compiler": "zig-cc",
+   "deps": [
+    "binutils",
+    "busybox",
+    "make",
+    "pkgconf"
+   ],
+   "cls": "c",
+   "queue": "corpus",
+   "foreign": false,
+   "hash": "b3:7202a918f06efe0315267027b071f5380f77aa15ce59eac83b5f078e6edd24f4",
+   "state": "sealed",
+   "blocked_by": [],
+   "unblocks": 0,
+   "perfiles": [
+    "servidor"
+   ],
+   "dependientes_total": 1
+  },
   "prek": {
    "iname": "prek",
    "version": "0.3.11",
diff --git a/recipes/chrony.toml b/recipes/chrony.toml
new file mode 100644
index 00000000..d2846f55
--- /dev/null
+++ b/recipes/chrony.toml
@@ -0,0 +1,70 @@
+# chrony 4.9 — el reloj. `targets.toml` lo declara como raíz del perfil `servidor` con un motivo que
+# no es comodidad: sin NTP un reloj que deriva rompe la validación de certificados TLS y la de firmas
+# con expiración, y falla con errores que NO dicen «la hora», dicen «firma inválida». Era una de las
+# tres `wanted` que el grafo contaba como deuda del perfil.
+#
+# El `configure` de chrony NO ES AUTOTOOLS: es un script a mano que ni siquiera acepta
+# `--build`/`--host` (los imprime como «Unrecognized option» y sigue). Por eso esta receta no lleva
+# la línea `--build=$CBUILD --host=$CHOST` que llevan todas las demás; lo que sí acepta —y es su
+# equivalente— es `--host-system`/`--host-machine`, que se pinean para que la detección no salga de
+# `uname` del lab.
+#
+# ⚠ TODO `--without-*` DE ACÁ ABAJO ESTÁ POR LA MISMA RAZÓN, Y NO ES PROLIJIDAD. El `configure` de
+# chrony PRUEBA el entorno para casi todo (`try_nettle=1`, `try_gnutls=1`, `try_libcap=-1`,
+# `try_seccomp=-1`, `try_editline=1`…): lo que encuentre instalado en el LAB cambia el binario. Y el
+# lab NO entra en `hash_inputs` — dos labs distintos sellarían bytes distintos en la MISMA dirección
+# del store sin que nada lo note. Fijar cada palanca convierte eso en una decisión de la receta, que
+# sí entra en el hash. Enumeradas:
+#
+#   --without-nettle --without-gnutls --without-nss --without-tomcrypt --disable-nts
+#       NTS (NTP sobre TLS) necesita nettle o gnutls, y NINGUNA de las dos está en el corpus. Se
+#       apaga a propósito y queda anotado: subir NTS es su propia unidad de trabajo —entra un árbol
+#       de criptografía entero— no algo que se resuelve de paso. Mientras tanto chronyd hace NTP
+#       clásico, que es lo que la caja necesita para que TLS no falle por la hora.
+#   --disable-readline --without-editline
+#       el editor de línea de `chronyc`. Ni readline ni editline están en el corpus; sin fijarlo, un
+#       lab que las tuviera cambiaría el binario.
+#   --without-seccomp
+#       el filtro de syscalls (`--enable-scfilter`) viene apagado de fábrica, pero el probe de
+#       libseccomp corre igual. Sin scfilter, encontrarla no aporta nada y sí mueve el hash.
+#
+# `libcap` SÍ entra como dep, y es la decisión contraria a las de arriba: chronyd suelta privilegios
+# (`--disable-privdrop` NO se pasa) y para eso necesita `cap_*`. Declararla como dep la vuelve
+# presente-por-contrato en vez de presente-por-suerte-del-lab, que es exactamente el mismo problema
+# visto del otro lado. El `libcap` del corpus publica sólo `.a` (`SHARED=no`), así que enlaza
+# estático y no deja NEEDED colgante.
+#
+# **`--with-user` se deja en `root`.** Lo natural sería `chrony`, pero un chronyd que hace `setuid`
+# a un usuario que no existe en `/etc/passwd` no arranca, y la distro todavía no crea usuarios de
+# servicio. Cambiarlo va junto con esa decisión, no antes.
+#
+# **Consecuencia medida de apagar la criptografía:** el binario sale `-SECHASH`, o sea que para la
+# autenticación simétrica por clave sólo queda MD5. No es un descuido: `sechash` (SHA-2 y CMAC) se
+# implementa contra nettle/gnutls/nss/tomcrypt, las mismas que no están. Lo que el binario SÍ trae,
+# comprobado corriéndolo: `+CMDMON +REFCLOCK +RTC +PRIVDROP +IPV6`, es decir que `libcap` entró de
+# verdad — `cap_set_proc`/`cap_from_text` están en el ELF — y chronyd suelta privilegios.
+#
+# La reproducibilidad ya la trae upstream: el `NTP_ERA_SPLIT` —que por default es «hace 50 años»
+# calculado con `date` en tiempo de configure— respeta `SOURCE_DATE_EPOCH`, que el lab exporta. Sin
+# eso el binario cambiaría cada día sin tocar la receta.
+name    = "chrony"
+version = "4.9"
+license = "GPL-2.0-only"
+
+[source]
+tarball = "https://chrony-project.org/releases/chrony-4.9.tar.gz"
+sha256  = "4924c6f530105bcd5b9e9e33c48a2ae1bfd889222c8480bc41601110efc864d0"
+
+[build]
+compiler = "zig-cc"
+target   = "x86_64-linux-musl"
+link     = "static"
+flags    = []
+
+[build.phases]
+configure = './configure --host-system=Linux --host-machine=x86_64 --prefix=/usr --sysconfdir=/etc --sbindir=/usr/sbin --bindir=/usr/bin --without-nettle --without-gnutls --without-nss --without-tomcrypt --disable-nts --disable-readline --without-editline --without-seccomp'
+compile   = 'make -j"$(nproc)"'
+install   = "make install DESTDIR=/out"
+
+[deps]
+build = ["binutils", "busybox", "make", "pkgconf", "libcap"]
diff --git a/recipes/cronie.toml b/recipes/cronie.toml
new file mode 100644
index 00000000..f7f4b679
--- /dev/null
+++ b/recipes/cronie.toml
@@ -0,0 +1,54 @@
+# cronie 1.7.2 — el disparador periódico. Es la tercera y última de las raíces que `targets.toml`
+# declaraba para el perfil `servidor` sin receta detrás. El motivo escrito allí es directo: en una
+# distro sin systemd no hay timers, y el latido de la granja son tres líneas de crontab.
+#
+# **Y busybox YA trae un `crond` — esto no lo reemplaza por reflejo.** El artefacto sellado de
+# busybox pone `crond` en `/sbin`. Lo que cronie agrega y busybox no tiene: `@reboot`, crontabs por
+# usuario con `crontab -e`, `/etc/cron.d`, `cronnext`, y `anacron` para las tareas que hay que
+# recuperar cuando la máquina estuvo apagada. Cuál de los dos va en cada imagen es una decisión de
+# perfil, no de esta receta; lo que hacía falta era que la opción EXISTIERA en el corpus en vez de
+# descubrirse ausente el día que alguien la necesite.
+#
+# ⚠ EL DEFAULT QUE HABÍA QUE PINEAR SÍ O SÍ: `--with-editor`. Sin él, `configure.ac` hace
+# `AC_PATH_PROG([vi])` — o sea que la ruta del editor que `crontab -e` lanza SALE DE BUSCAR `vi` EN
+# EL LAB, y queda grabada en el binario con `AC_DEFINE_UNQUOTED`. El lab no entra en `hash_inputs`:
+# dos labs con `vi` en rutas distintas sellarían bytes distintos en la MISMA dirección del store y
+# nada lo notaría. Se fija en `/bin/vi`, que además es donde busybox lo deja de verdad (comprobado
+# en su artefacto sellado); el fallback de autoconf era `/usr/bin/vi`, que en esta distro no existe.
+#
+# Lo demás son palancas de cosas que el corpus no tiene, apagadas explícitamente por la misma razón
+# —que el probe no salga del entorno— y no porque estorben:
+#   --without-pam       no hay Linux-PAM en el corpus. La autorización de `crontab` cae entonces a
+#                       `/etc/cron.allow` + `/etc/cron.deny`, que es el mecanismo clásico y no
+#                       necesita nada más.
+#   --without-selinux   la distro no lleva SELinux.
+#   --without-audit     no hay libaudit.
+#   --with-inotify      esto se ENCIENDE: con inotify, crond se entera de un crontab editado sin
+#                       esperar al minuto siguiente. Es una API del kernel, no una dep nueva.
+#   --enable-syscrontab `/etc/crontab` y `/etc/cron.d`, que es por donde entra el latido de la granja.
+#
+# `link = "static"`: binarios monolíticos como el resto de las herramientas de sistema.
+#
+# La licencia es compuesta y no se adivina: el grueso (`src/cron.c` y compañía) es ISC de Vixie/ISC,
+# y `anacron` —que se construye— trae su propio `COPYING.anacron`, GPLv2-or-later.
+name    = "cronie"
+version = "1.7.2"
+license = "ISC AND GPL-2.0-or-later"
+
+[source]
+tarball = "https://github.com/cronie-crond/cronie/releases/download/cronie-1.7.2/cronie-1.7.2.tar.gz"
+sha256  = "f1da374a15ba7605cf378347f96bc8b678d3d7c0765269c8242cfe5b0789c571"
+
+[build]
+compiler = "zig-cc"
+target   = "x86_64-linux-musl"
+link     = "static"
+flags    = []
+
+[build.phases]
+configure = './configure --build=$CBUILD --host=$CHOST --prefix=/usr --sysconfdir=/etc --localstatedir=/var --sbindir=/usr/sbin --with-editor=/bin/vi --with-inotify --without-pam --without-selinux --without-audit --enable-syscrontab'
+compile   = 'make -j"$(nproc)"'
+install   = "make install DESTDIR=/out && find /out -name '*.la' -delete"
+
+[deps]
+build = ["binutils", "busybox", "make", "pkgconf"]
diff --git a/recipes/logrotate.toml b/recipes/logrotate.toml
new file mode 100644
index 00000000..8b7e39c7
--- /dev/null
+++ b/recipes/logrotate.toml
@@ -0,0 +1,52 @@
+# logrotate 3.22.0 — la rotación de logs. Es la última de las tres raíces que `targets.toml` declara
+# para el perfil `servidor` y que no tenían receta: el grafo las contaba como `wanted` y la deuda del
+# perfil era exactamente ésa. El motivo escrito allí sigue valiendo: en una caja que sirve, el log de
+# acceso crece hasta llenar el disco, y Caddy rota el suyo por configuración pero nada más lo hace.
+#
+# DOS DEFAULTS DE `configure` QUE SE PINEAN A PROPÓSITO, y no por prolijidad:
+#
+# 1. **`--with-selinux=no --with-acl=no`.** Los dos vienen con `[default=check]`, o sea que el
+#    contenido del binario depende de si el LAB tiene `libselinux`/`libacl` instaladas. El lab NO
+#    entra en `hash_inputs` (deuda conocida del repo): dos labs distintos sellarían bytes distintos
+#    en la MISMA dirección del store y nada lo notaría. Fijarlo en `no` lo vuelve una decisión de la
+#    receta —que sí entra en el hash— en vez de un accidente del entorno. Y `no` es lo correcto acá:
+#    la distro no lleva SELinux ni ACLs POSIX.
+#
+# 2. **`--with-compress-command` / `--with-uncompress-command`.** `configure.ac` elige la ruta por
+#    sistema operativo y en Linux fija `/usr/bin/gzip`, que en esta distro NO EXISTE: el `gzip` y el
+#    `gunzip` los provee busybox y quedan en `/bin` (comprobado en el artefacto sellado de busybox).
+#    Un logrotate con `compress` en la config fallaría en runtime buscando un binario ausente, y el
+#    error sería «no se pudo comprimir», no «la ruta está mal». Se pinean a `/bin/…`.
+#
+# **Lo que NO se pinea, y por qué se deja anotado:** el `mail command` queda en su default
+# `/bin/mail`, que en esta distro NO existe — busybox provee `sendmail`, no `mail`. No se cambia a
+# `/bin/sendmail` porque no es un reemplazo: logrotate lo invoca como `mail -s <asunto> <destino>` y
+# la interfaz de busybox es otra. Sólo afecta a configuraciones que usen la directiva `mail`, que la
+# distro hoy no tiene; queda dicho para que se descubra leyendo y no en runtime.
+#
+# `--with-state-file-path` se deja en su default (`/var/lib/logrotate.status`): es ruta de ESTADO en
+# la máquina viva, no algo que la imagen deba decidir.
+#
+# `link = "static"`: binario monolítico, como el resto de las herramientas de sistema. popt publica
+# sólo `.a` justo para que acá no aparezca un NEEDED colgante.
+name    = "logrotate"
+version = "3.22.0"
+license = "GPL-2.0-or-later"
+
+[source]
+tarball = "https://github.com/logrotate/logrotate/releases/download/3.22.0/logrotate-3.22.0.tar.xz"
+sha256  = "42b4080ee99c9fb6a7d12d8e787637d057a635194e25971997eebbe8d5e57618"
+
+[build]
+compiler = "zig-cc"
+target   = "x86_64-linux-musl"
+link     = "static"
+flags    = []
+
+[build.phases]
+configure = './configure --build=$CBUILD --host=$CHOST --prefix=/usr --with-selinux=no --with-acl=no --with-compress-command=/bin/gzip --with-uncompress-command=/bin/gunzip'
+compile   = 'make -j"$(nproc)"'
+install   = "make install DESTDIR=/out && find /out -name '*.la' -delete"
+
+[deps]
+build = ["binutils", "busybox", "make", "pkgconf", "popt"]
diff --git a/recipes/popt.toml b/recipes/popt.toml
new file mode 100644
index 00000000..f7f1e8f7
--- /dev/null
+++ b/recipes/popt.toml
@@ -0,0 +1,39 @@
+# popt 1.19 — el parser de opciones de línea de comando de rpm. Entra por UNA razón y sólo una:
+# `logrotate` lo exige. Su `logrotate.c` hace `#include <popt.h>` en la primera pantalla y su
+# `configure` aborta sin él; no hay flag para sacarlo ni fallback interno a getopt.
+#
+# Es la hoja de la cadena `logrotate` del perfil `servidor`, que el grafo pedía como `wanted` desde
+# que se escribió `targets.toml` y que nadie había puesto.
+#
+# Tarball de release y no git: el árbol de git NO trae `configure` (lo genera `autoreconf`) y el
+# corpus no tiene autoconf/automake — el mismo motivo por el que entran libmnl, jq y gmp.
+#
+# `--disable-nls`: popt traduce sus mensajes de error con gettext, y el corpus tiene `gettext-tiny`,
+# cuyo `msgfmt` muere con SIGILL en algunos catálogos (los plurales del árabe). No vale la pena
+# arriesgar eso por los mensajes de error de un parser de flags que sólo ve `logrotate`.
+#
+# `link = "static"` y `--disable-shared`: el consumidor es un binario de sistema que queremos
+# monolítico (la estrategia de hidratación primaria). Publicar sólo `.a` evita además que
+# `logrotate` salga con un NEEDED de `libpopt.so.0` que después nadie provea — la familia de fallos
+# de `needed-colgante`.
+name    = "popt"
+version = "1.19"
+license = "MIT"
+
+[source]
+tarball = "http://ftp.rpm.org/popt/releases/popt-1.x/popt-1.19.tar.gz"
+sha256  = "c25a4838fc8e4c1c8aacb8bd620edb3084a3d63bf8987fdad3ca2758c63240f9"
+
+[build]
+compiler = "zig-cc"
+target   = "x86_64-linux-musl"
+link     = "static"
+flags    = []
+
+[build.phases]
+configure = './configure --build=$CBUILD --host=$CHOST --prefix=/usr --disable-shared --enable-static --disable-nls --disable-rpath'
+compile   = 'make -j"$(nproc)"'
+install   = "make install DESTDIR=/out && find /out -name '*.la' -delete"
+
+[deps]
+build = ["binutils", "busybox", "make", "pkgconf"]