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 1572Shell 203Rust 109Markdown 993728 Definiciones

opensmtpd: la familia «correo» estaba vacía — y la colisión de símbolos se CONTÓ antes de arreglarla

Tercera de las cuatro familias que la tabla de `planear.py` daba SIN NADA en el catálogo. Quedan dos:
contenedores y base vectorial (qdrant está moliendo en el worker).

Cuál de los tres servidores es una decisión, y va con el motivo para poder discutirla: **postfix**
asume glibc en varios sitios (NIS, nsswitch, su `dict_nis`) y son ~250 kLOC — portarlo a musl es un
frente, no una receta; **exim** tiene una configuración que es literalmente un lenguaje de
programación, y esa superficie ha sido su fuente histórica de CVEs; **OpenSMTPD** viene de OpenBSD
con separación de privilegios POR DISEÑO, ~40 kLOC, ISC, y su rama «portable» existe justo para
construirse fuera de OpenBSD ⇒ musl es un objetivo previsto. Si alguien necesita las tablas
MySQL/LDAP de postfix esto no sirve — pero se discute con la razón delante en vez de descubrir la
ausencia durante una mudanza.

Verificado: los 10 binarios estáticos, CERO NEEDED, y `smtpd -n -f` sobre una `smtpd.conf` real
contesta `configuration OK`. REPRODUCE bit a bit.

**El muro fue una colisión de símbolos, y lo que importa es que se MIDIÓ antes de elegir el arreglo.**
OpenSMTPD-portable trae su propia **libtls** (la envoltura de LibreSSL) porque la necesita cuando se
construye contra OpenSSL; y OpenSSL 3 define en su capa de récords una función INTERNA con el mismo
nombre. Estático, las dos caen en el mismo binario:

    ld.lld: error: duplicate symbol: tls_free
      defined at ../../openbsd-compat/libtls/tls.c:708
      defined at ssl/record/methods/tls_common.c:1473  in archive /usr/lib/libssl.a

En vez de suponer el tamaño del problema, se contó:

    nm -g --defined-only openbsd-compat/libtls/*.o | sort -u   →  158 símbolos
    comm -12 <esos> <los globales de libssl.a>                 →  **1**:  tls_free

Uno solo ⇒ el arreglo es renombrar ése y nada más, con `--with-cppflags=-Dtls_free=…` (la perilla que
upstream ya expone). El `-D` alcanza toda la compilación de OpenSMTPD, así que renombra a la vez la
definición, la declaración de `tls.h` y los usos — consistente por construcción; la de OpenSSL vive
en un `.a` ya compilado y no se toca. Es seguro porque esa libtls es interna a este build.

Si hubieran sido docenas de símbolos, el arreglo correcto era otro —traer LibreSSL al corpus, que es
contra lo que upstream construye— y por eso se midió primero. Queda escrito en la receta para que el
día que OpenSSL sume otro choque se vuelva a contar en vez de apilar `-D`s.

Dos cosas más anotadas y no tapadas: `--with-libfts=/usr` es obligatorio porque `fts(3)` está en
glibc y NO en musl (el corpus tiene `musl-fts` justo para eso) y sin pasarlo `configure` lo buscaría
en el LAB, que no entra en `hash_inputs`; y los usuarios `_smtpd`/`_smtpq` todavía no existen en la
distro — se dejan en su nombre canónico a propósito, porque cambiarlos por `root` tiraría la
separación de privilegios, que es la razón principal para elegir este servidor.

f52205af313da2d9671cb74cf0e6fa5fb5b7934b · por Sergio · (hace 1 mes) · padre 294959c1d · ver el árbol en este commit

Delta semántico

Ninguna definición cambió: el cambio es de texto, documentación o datos.

3 archivos · +122 −14

diff --git a/docs/state/build-state.json b/docs/state/build-state.json
index 6761da29..09ee7fd0 100644
--- a/docs/state/build-state.json
+++ b/docs/state/build-state.json
@@ -1,14 +1,14 @@
 {
  "schema": "hammer-build-state/1",
  "totals": {
-  "recipes": 885,
-  "nodes": 887,
-  "sealed": 885,
+  "recipes": 886,
+  "nodes": 888,
+  "sealed": 886,
   "ajeno": 2
  },
  "by_class": {
   "ajeno": 2,
-  "c": 241,
+  "c": 242,
   "go": 363,
   "gui": 41,
   "kernel": 6,
@@ -17,7 +17,7 @@
  "debt_by_class": {},
  "by_queue": {
   "corpus": {
-   "sealed": 885,
+   "sealed": 886,
    "ajeno": 2
   }
  },
@@ -43,7 +43,7 @@
    "sealed": 101
   }
  },
- "sin_perfil": 647,
+ "sin_perfil": 648,
  "orphan_deps": [],
  "topo_ok": true,
  "nodes": {
@@ -1026,7 +1026,7 @@
     "escritorio-sway",
     "servidor"
    ],
-   "dependientes_total": 55
+   "dependientes_total": 56
   },
   "biome": {
    "iname": "biome",
@@ -1303,7 +1303,7 @@
     "escritorio-sway",
     "servidor"
    ],
-   "dependientes_total": 15
+   "dependientes_total": 16
   },
   "bwrap": {
    "iname": "bwrap",
@@ -9511,7 +9511,7 @@
    "perfiles": [
     "servidor"
    ],
-   "dependientes_total": 1
+   "dependientes_total": 2
   },
   "libffi": {
    "iname": "libffi",
@@ -10825,7 +10825,7 @@
     "escritorio-sway",
     "servidor"
    ],
-   "dependientes_total": 131
+   "dependientes_total": 132
   },
   "mako": {
    "iname": "mako",
@@ -11739,7 +11739,7 @@
    "blocked_by": [],
    "unblocks": 0,
    "perfiles": [],
-   "dependientes_total": 1
+   "dependientes_total": 2
   },
   "musl-obstack": {
    "iname": "musl-obstack",
@@ -12446,6 +12446,31 @@
    "perfiles": [],
    "dependientes_total": 0
   },
+  "opensmtpd": {
+   "iname": "opensmtpd",
+   "version": "7.8.0p1",
+   "link": "static",
+   "compiler": "zig-cc",
+   "deps": [
+    "binutils",
+    "busybox",
+    "libevent",
+    "make",
+    "musl-fts",
+    "openssl",
+    "pkgconf",
+    "zlib"
+   ],
+   "cls": "c",
+   "queue": "corpus",
+   "foreign": false,
+   "hash": "b3:51b185ef7e032a4ee10a6ac31799a7baf2b9ea0b95a1fd8f7fcc5239fd287a5b",
+   "state": "sealed",
+   "blocked_by": [],
+   "unblocks": 0,
+   "perfiles": [],
+   "dependientes_total": 0
+  },
   "openssh": {
    "iname": "openssh",
    "version": "10.3p1",
@@ -12492,7 +12517,7 @@
     "escritorio-sway",
     "servidor"
    ],
-   "dependientes_total": 29
+   "dependientes_total": 30
   },
   "openssl-threads": {
    "iname": "openssl-threads",
@@ -13189,7 +13214,7 @@
     "escritorio-sway",
     "servidor"
    ],
-   "dependientes_total": 411
+   "dependientes_total": 412
   },
   "plow": {
    "iname": "plow",
@@ -17930,7 +17955,7 @@
     "escritorio-sway",
     "servidor"
    ],
-   "dependientes_total": 118
+   "dependientes_total": 119
   },
   "zlib-ng": {
    "iname": "zlib-ng",
diff --git a/docs/state/repro-verificado.tsv b/docs/state/repro-verificado.tsv
index 0887218c..3c004169 100644
--- a/docs/state/repro-verificado.tsv
+++ b/docs/state/repro-verificado.tsv
@@ -79,3 +79,4 @@ valkey	9571c1a691ff8dd2e91611a341d2ddab11a9cc9a030ed8054006a4626542a06f	2026-09-
 abseil-cpp	a0d2ad6f771ecb64471a7e25db365cfcfb785574d75d4161ee2423beb7f35b5f	2026-09-11	reproduce
 protobuf	c01f39d4a566e48f09d077ac20a5c6249c6a36fe3c53196c1c6e5e4ce0bc5991	2026-09-11	reproduce
 kubectl	ca95c5292688c838715727f9b909a0722d9fc98a778edefb2a4864e01678056e	2026-09-11	reproduce
+opensmtpd	51b185ef7e032a4ee10a6ac31799a7baf2b9ea0b95a1fd8f7fcc5239fd287a5b	2026-09-11	reproduce
diff --git a/recipes/opensmtpd.toml b/recipes/opensmtpd.toml
new file mode 100644
index 00000000..4b4a4f18
--- /dev/null
+++ b/recipes/opensmtpd.toml
@@ -0,0 +1,82 @@
+# OpenSMTPD 7.8.0p1 — el servidor de correo. Cierra la tercera de las cuatro familias que la tabla
+# de `scripts/mudanza/planear.py` daba VACÍAS en el catálogo («correo»: ni postfix, ni exim, ni
+# opensmtpd). Una familia vacía no se descubre leyendo: se descubre el día que la mudanza llega a
+# ese servicio y se para.
+#
+# ⚠ **CUÁL DE LOS TRES ES UNA DECISIÓN, Y ACÁ ESTÁ EL MOTIVO PARA PODER DISCUTIRLA.** No es gusto:
+#
+#   · **postfix** asume glibc en varios sitios (NIS, `nsswitch`, su propio `dict_nis`), su árbol son
+#     ~250 kLOC y su configuración son dos ficheros con cientos de parámetros. Portarlo a musl es un
+#     frente, no una receta.
+#   · **exim** tiene una configuración que es literalmente un lenguaje de programación con listas de
+#     ACL; su superficie de configuración ha sido su fuente histórica de CVEs.
+#   · **OpenSMTPD** viene de OpenBSD: separación de privilegios POR DISEÑO (procesos `smtpd`/`queue`
+#     con usuarios distintos, que es justo lo que el `--with-user-*` de abajo configura), ~40 kLOC,
+#     licencia ISC, y una `smtpd.conf` útil entra en cinco líneas. Y lo más importante para acá: su
+#     rama «portable» existe precisamente para construirse fuera de OpenBSD, así que musl es un
+#     objetivo previsto y no una aventura.
+#
+# Si alguien necesita las tablas MySQL/LDAP de postfix, ésta no sirve y hay que discutirlo — pero se
+# discute con la razón delante en vez de descubrir la ausencia en la mudanza.
+#
+# ⚠ **`--with-libfts` NO ES OPCIONAL Y CASI NO SE VE.** `fts(3)` —recorrer un árbol de directorios—
+# está en glibc y **NO en musl**. El corpus tiene `musl-fts` sellada exactamente para este hueco. Si
+# no se pasa, `configure` la BUSCA en el entorno: otra decisión que saldría del LAB, que no entra en
+# `hash_inputs`. Se declara como dep y se apunta la ruta a mano.
+#
+# `--without-libbsd`: no hay libbsd en el corpus, y `configure` la usaría si la encontrara.
+# **PAM queda apagado** (no se pasa `--with-auth-pam`): no hay Linux-PAM en el corpus, así que la
+# autenticación SMTP sale de `smtpd.conf` con una tabla de credenciales, que es el camino nativo de
+# OpenSMTPD. `--with-table-db` queda en su default `no`: pide Berkeley DB, que tampoco está.
+#
+# **Los usuarios `_smtpd`/`_smtpq` NO EXISTEN todavía en la distro**, igual que el `chrony` de
+# `recipes/chrony.toml`. Se dejan en su nombre canónico a propósito: son lo que la documentación de
+# OpenSMTPD y cualquier runbook nombran, y cambiarlos por `root` sería peor —tiraría la separación de
+# privilegios, que es la razón principal para elegir este servidor—. Crearlos es parte de armar la
+# imagen, no de esta receta; hasta entonces `smtpd` no arranca, y eso es ruidoso y correcto.
+name    = "opensmtpd"
+version = "7.8.0p1"
+license = "ISC"
+
+[source]
+tarball = "https://github.com/OpenSMTPD/OpenSMTPD/releases/download/7.8.0p1/opensmtpd-7.8.0p1.tar.gz"
+sha256  = "f473b1b32e3440f28669419466d1e6ff037cf6b228769979135a4fb39060e3c3"
+
+[build]
+compiler = "zig-cc"
+target   = "x86_64-linux-musl"
+link     = "static"
+flags    = []
+
+[build.phases]
+# ⚠ **`-Dtls_free=…` NO ES UN APAÑO A CIEGAS: ES UNA COLISIÓN MEDIDA, Y ES EXACTAMENTE UNA.**
+#
+# OpenSMTPD-portable trae su propia **libtls** (la envoltura TLS de LibreSSL) en
+# `openbsd-compat/libtls/`, porque la necesita cuando se construye contra OpenSSL en vez de LibreSSL.
+# Y OpenSSL 3 define, en su capa de récords (`ssl/record/methods/tls_common.c`), una función INTERNA
+# que se llama igual. Enlazando estático las dos caen en el mismo binario:
+#
+#     ld.lld: error: duplicate symbol: tls_free
+#       defined at ../../openbsd-compat/libtls/tls.c:708
+#       defined at ssl/record/methods/tls_common.c:1473  in archive /usr/lib/libssl.a
+#
+# Antes de elegir el arreglo se CONTÓ el solapamiento en vez de suponerlo — comparando los símbolos
+# globales definidos por los `.o` de la libtls bundleada contra los de `libssl.a`:
+#
+#     nm -g --defined-only openbsd-compat/libtls/*.o | … | sort -u  →  158 símbolos
+#     comm -12 <esos> <los de libssl.a>                             →  **1**:  tls_free
+#
+# Uno. Por eso el arreglo es renombrar ése y nada más. El `-D` se aplica a TODA la compilación de
+# OpenSMTPD, así que renombra a la vez la definición, la declaración de `tls.h` y todos los usos —
+# consistente por construcción; la de OpenSSL vive en un `.a` ya compilado y no se toca. Es seguro
+# porque esa libtls es INTERNA a este build: nadie enlaza contra ella desde fuera.
+#
+# Si el solapamiento hubiera sido de docenas de símbolos, el arreglo correcto era otro —traer
+# LibreSSL al corpus, que es contra lo que upstream construye—, y por eso se midió primero. Queda
+# dicho para que el día que OpenSSL sume otro choque se vuelva a contar en vez de apilar `-D`s.
+configure = './configure --build=$CBUILD --host=$CHOST --prefix=/usr --sysconfdir=/etc/smtpd --without-libbsd --without-rpath --with-libfts=/usr --with-mantype=man --with-cppflags=-Dtls_free=opensmtpd_tls_free'
+compile   = 'make -j"$(nproc)"'
+install   = "make install DESTDIR=/out && find /out -name '*.la' -delete"
+
+[deps]
+build = ["binutils", "busybox", "make", "pkgconf", "libevent", "openssl", "zlib", "musl-fts"]