plan freebsd: traza positiva (filemon/META MODE) + taxonomía casper para cierre §3 + libarchive al catálogo
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Semantic delta
No definition changed: this change is text, documentation or data.
1 files · +118 −0
diff --git a/docs/plan-freebsd-aprovechables.md b/docs/plan-freebsd-aprovechables.md
new file mode 100644
index 00000000..ca07d815
--- /dev/null
+++ b/docs/plan-freebsd-aprovechables.md
@@ -0,0 +1,118 @@
+# Plan — qué tomar de FreeBSD
+
+**Fecha:** 2026-07-16 · **Origen:** análisis de META MODE/filemon, Capsicum/casper y
+libarchive contra el estado real de hammer (harkaq Fase 2, cierres §2/§3, catálogo Etapa G).
+
+**Regla del análisis:** de otro kernel se toma *diseño*, no código. La única pieza de código
+importable es libarchive (como receta del catálogo, igual que cualquier otra). Todo lo demás
+es prior art que valida o corrige decisiones ya tomadas.
+
+---
+
+## 1. filemon / META MODE → la traza POSITIVA que a harkaq le falta
+
+**Qué tiene FreeBSD.** `filemon(4)` registra en el kernel cada acceso a fichero durante un
+build; `bmake` en META MODE escribe un `.meta` por target con *todo lo que el build tocó*,
+normalizado. Con eso hacen auto-descubrimiento de dependencias y detección de builds impuros:
+si la traza cambia, el target se reconstruye. Llevan ~12 años iterando qué normalizar (orden,
+tmpdirs, accesos que no importan).
+
+**Qué tiene hammer hoy.** harkaq mide sólo evidencia **negativa**: denegaciones Landlock bajo
+política=clausura (`harkaq-audit.c` emite JSON `{path, motivo}`, `harkaq-verdict.py` clasifica
+esperada/deuda). Lo que NO mide: **qué parte de la clausura concedida se usó de verdad**.
+
+**La brecha que eso deja.** harkaq detecta *sub*-declaración (deuda) pero no
+*sobre*-declaración: una dep declarada y jamás tocada es invisible, engorda la clausura, y esa
+grasa se hereda directo a la política runtime derivada del cierre §3 (política más ancha de lo
+necesario = evidencia más débil). La traza positiva es el dual exacto del veredicto actual.
+
+**Tareas:**
+
+- **T1.1 — sonda de uso positivo.** Un `harkaq-trace` que emita la lista de paths *abiertos*
+ por fase dentro del sandbox. Mecanismo candidato: fanotify — que ya es frente delegado por
+ arje (journal fanotify, ver handoff) ⇒ reutilizar ese trabajo, no abrir mecanismo nuevo.
+ Alternativa si fanotify dentro del userns del sandbox se complica: eBPF sobre `openat`
+ (BPF_SYSCALL ya es explícito en los kernels propios, commit 2b7c701).
+- **T1.2 — normalización estilo `.meta`.** Orden estable, dedupe, paths relativos a la jaula,
+ filtrar tmpdirs / `/out` / `/src`. Las reglas de ruido viven junto a la política — la misma
+ fuente de verdad que los `# expect` (D3: no se calla, se clasifica; el ruido se declara,
+ no se pierde). Aquí es donde la experiencia de META MODE ahorra iteraciones: readdir sin
+ orden garantizado y accesos especulativos del compilador son las dos fuentes de ruido que
+ ellos ya catalogaron.
+- **T1.3 — sellar la traza.** El `b3:` de la traza normalizada viaja junto al `of_tree` del
+ artefacto. Uso inmediato para **why-differs (cierre §2, top-2)**: dos builders que divergen
+ en hash se comparan primero por traza — diff de dos listas de texto ANTES que diff
+ estructural de binarios. El descenso por rangos de `reconcile`/`format` aplica igual.
+- **T1.4 — `harkaq-suggest --poda`.** Con traza positiva, sugerir *remover* deps
+ declaradas-no-usadas: el dual del suggest actual (que sólo sugiere añadir). Cierra el bucle
+ en ambas direcciones: la clausura converge a exactamente-lo-usado.
+
+**Riesgos.** Coste de fanotify en builds grandes (medir primero); falsos "no usados" por
+accesos condicionales entre corridas (mitigación: la poda sugiere, nunca aplica sola — mismo
+contrato que suggest). Piloto: 3 recetas ya instrumentadas (zlib, tar, brotli), en el worker
+(el laptop no compila C).
+
+---
+
+## 2. Capsicum / casper → la taxonomía del cierre §3 (política runtime)
+
+**La lección negativa.** Capsicum es más puro que Landlock (capabilities sobre fds,
+`cap_enter()`), y aun así FreeBSD terminó necesitando `casper`: demonios auxiliares porque
+hay una clase de necesidades — DNS, certificados TLS, syslog, random temprano, locale — que
+son *servicios del mundo*, no paths, y no caben en ninguna jaula por-fichero.
+
+**Dónde muerde en hammer.** En build: en ninguna parte — los builds son offline por
+`--unshare-all`, la clase casper es vacía por construcción. En **runtime** (cierre §3: derivar
+la política Landlock del binario instalado desde la clausura medida en build): la clausura de
+build **no contiene** esa clase ⇒ derivar a ciegas produce binarios que revientan en el primer
+`getaddrinfo`. Saber esto *antes* de arrancar Fase 3 es exactamente el valor del prior art.
+
+**Tareas (papel, cero código hoy):**
+
+- **T2.1 — sección "servicios del mundo" en SDD 16 (Fase 3).** La política runtime = clausura
+ de build **+ clases de servicio declaradas** (`dns`, `tls-certs`, `random`, `locale`,
+ `syslog`). Se declara por clase con nombre, nunca se ensancha en silencio — D3 aplica en
+ runtime igual que en build.
+- **T2.2 — mapa clase→paths para musl.** Ventaja estructural de hammer: musl no tiene
+ nsswitch ni dlopen de módulos NSS — `dns` = `/etc/resolv.conf` + socket UDP; `tls-certs` =
+ `/etc/ssl/certs`; `random` = `getrandom(2)` (ni path necesita). El mapa completo cabe en
+ media página; en glibc esto era intratable y es la mitad de por qué casper existe.
+- **T2.3 — la clase viaja en la ConcesionCapacidad.** `(blake3(binario), permisos)` gana un
+ campo de clases de servicio; `arje-absorb --attest-from` ya integra concesiones (PLAN-ATESTACION
+ §B.3) ⇒ sin formato nuevo.
+
+---
+
+## 3. libarchive / bsdtar → receta + extractor de referencia
+
+El único export de código real de FreeBSD que todo el mundo usa.
+
+- **T3.1 — `recipes/libarchive.toml`.** Receta C ⇒ worker. Nombre nuevo en el catálogo, sin
+ riesgo de colisión homónima (el gotcha de promote no aplica). Deps mínimas: zlib (xz/zstd
+ opcionales según features que se activen).
+- **T3.2 — verificación cruzada de formato.** Donde hammer serializa árboles (pack/export),
+ extraer con bsdtar y comparar el árbol resultante contra el extractor propio = **consenso de
+ implementación aplicado al FORMATO** — mismo espíritu que el consenso de reconstrucción
+ (cierre §1) aplicado a los bytes del contenedor, no al build. Barato: un script de barrido
+ sobre N artefactos sellados.
+- **T3.3 (opcional) — bsdtar como dep de build** para tarballs exóticos donde busybox tar se
+ queda corto (hoy: ninguno conocido; tener la receta lo vuelve una línea en `[deps]`).
+
+---
+
+## Lo que NO se toma (para no re-litigarlo)
+
+- **Ports como corpus de recetas** — patches y Makefiles asumen BSD userland; Alpine+nixpkgs
+ (Etapa G) dominan estrictamente para musl-Linux.
+- **FreeBSD como builder de consenso multi-kernel** — sin namespaces/bwrap/Landlock allá; el
+ consenso multi-kernel barato ya existe (kernel propio vs genérico).
+- **bectl / ZFS boot environments** — `hammer boot menu` + `activate --from-select` ya cubren
+ el concepto; como mucho, robar UX de nombres/rollback más adelante.
+- **bmake** — el mundo Linux necesita gmake; no reduce la deuda declarable de `/usr/bin/make`.
+
+## Orden propuesto
+
+1. **T2.1–T2.2** — papel, una tarde, desbloquea el diseño de Fase 3 antes de escribir código.
+2. **T1.1–T1.3** — piloto en 3 recetas en el worker; decide si fanotify o eBPF con datos.
+3. **T3.1** — receta a la próxima tanda del worker.
+4. **T1.4 y T3.2** — sólo después de validar los pilotos (la poda sin traza confiable es ruido).