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).