645 líneas. Los ADR entran porque en este repo SON documentos vivos, no registros inmutables: el 0013 tiene 5 commits, el 0009 dos. Eso se comprobó antes de decidir, no se asumió por convención general. EXCLUIDOS por ser REGISTRO o generado: docs/evidencia/ (6), el HANDOFF de la noche de KDE (1) y docs/state/ (24, se regenera solo). Reescribir un comando dentro de una evidencia la falsifica. Y el ADR 0016 se excluye de todo barrido, con un aviso adentro para el próximo que barra: habla SOBRE el renombre, así que necesita seguir diciendo 'hammer'. El barrido se lo llevó puesto y lo dejó titulado 'Renombre del sistema: takana → takana'; revertido. Congelados, verificados uno por uno con controles: /opt/hammer, /var/lib/hammer, /usr/bin/hammer, /mnt/vvv/hammer, la URL de gitea, hammer-farm.service, hammer-live-install.sh, BRIEFING-hammer.md, hammerd, hammer-recover y HAMMER_LIVE.
160 lines
10 KiB
Markdown
160 lines
10 KiB
Markdown
# 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 takana (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 takana 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).
|
||
|
||
**PILOTO MEDIDO (2026-07-17, worker, builds reales de zlib-ng y dwarves):**
|
||
|
||
1. **Ceguera-overlay — el hallazgo que corrige el diseño.** `FAN_MARK_FILESYSTEM` sobre el
|
||
fs del host (donde vive el store) NO ve los reads que el build hace a través del
|
||
`--tmp-overlay` del sandbox: el superbloque del overlay es OTRO, y overlayfs abre los
|
||
lower por dentro sin fsnotify en el sb de abajo. Medido: build completo de zlib-ng con
|
||
traza sobre el store = **0 eventos**; los binds sí aparecen (206 eventos de `/src` vía
|
||
`work/sources`, mismo sb) e incluso los reads host-side de takana (`recipes/*.toml` al
|
||
resolver deps — la traza ya sirve para auditar el hub).
|
||
2. **La salida, validada**: marcar `--fs /proc/<pid-bwrap>/root` — el magic-link cruza al
|
||
mount-ns y la marca cae sobre el SB DEL OVERLAY. Test sintético en el worker (overlay en
|
||
mntns aparte, lector externo): la lectura del lower a través del overlay **aparece** en
|
||
la traza. Bonus: los paths salen EN EL NAMESPACE DEL SANDBOX (`/usr/include/zlib.h`) —
|
||
el mismo idioma de la política harkaq, sin traducción host↔jaula. Cero código nuevo:
|
||
`harkaq-trace --fs /proc/$PID/root --prefix /usr`.
|
||
3. ~~Pendiente de orquestación~~ **HECHO en forma piloto**: `harkaq-trace-build.sh` envuelve
|
||
un `takana build`, engancha un tracer a cada bwrap que aparece (un bwrap por fase = una
|
||
traza por fase, la granularidad del veredicto) y emite canónica + sello + tabla
|
||
declaradas-vs-usadas. La integración sin carrera (lanzar el tracer ANTES del exec, no
|
||
por polling) sigue siendo del harness — coordinar con Opus.
|
||
|
||
**PILOTO END-TO-END (2026-07-17, worker, `takana build` real de zlib en store desechable
|
||
con deps hardlinkeadas — cero contaminación):**
|
||
|
||
- **El build trazado SELLÓ y el hash REPRODUJO bit a bit un artefacto ya sellado del store
|
||
principal** (`b3:2623a403…`): la traza no perturba el build y un store fresco reproduce —
|
||
mini-consenso de regalo.
|
||
- **La señal de poda (T1.4) existe y es filosa**: de la clausura concedida, el build usó
|
||
`make` **1 de 8** ficheros y `busybox` **1 de 2**. Por-fichero, no por-paquete — la misma
|
||
granularidad que la política.
|
||
- **Taxonomía del ruido medida** (3520 paths únicos crudos): 84 de `/cache` (cache zig
|
||
interna = estado derivado del build, regla nueva en el normalizador), ~3.4k del stdlib de
|
||
zig (`/lib/compiler_rt/*.zig` — toolchain, no clausura de receta) y los productos del
|
||
propio build (`example.o`, tmpfiles borrados del upper). Tras las reglas, la clausura
|
||
real de zlib son un puñado de paths exactos. META MODE otra vez: **las reglas de
|
||
normalización SON el producto**.
|
||
- Caveats anotados: ventana ciega del polling (~decenas de ms por fase), helpers de bwrap
|
||
duplican marcas (la dedupe lo absorbe), y el sello del piloto es sha256 PROVISIONAL (el
|
||
real debe ser b3, espacio de nombres del store). Gotcha operativo: `--store` ajeno a la
|
||
raíz del repo rompe la resolución del rootfs (`<padre-del-store>/.dev-fs`).
|
||
|
||
---
|
||
|
||
## 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 takana.** 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 takana: 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 takana 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** — `takana 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).
|