1# Plan — qué tomar de FreeBSD
2
3**Fecha:** 2026-07-16 · **Origen:** análisis de META MODE/filemon, Capsicum/casper y
4libarchive contra el estado real de takana (harkaq Fase 2, cierres §2/§3, catálogo Etapa G).
5
6**Regla del análisis:** de otro kernel se toma *diseño*, no código. La única pieza de código
7importable es libarchive (como receta del catálogo, igual que cualquier otra). Todo lo demás
8es prior art que valida o corrige decisiones ya tomadas.
9
10---
11
12## 1. filemon / META MODE → la traza POSITIVA que a harkaq le falta
13
14**Qué tiene FreeBSD.** `filemon(4)` registra en el kernel cada acceso a fichero durante un
15build; `bmake` en META MODE escribe un `.meta` por target con *todo lo que el build tocó*,
16normalizado. Con eso hacen auto-descubrimiento de dependencias y detección de builds impuros:
17si la traza cambia, el target se reconstruye. Llevan ~12 años iterando qué normalizar (orden,
18tmpdirs, accesos que no importan).
19
20**Qué tiene takana hoy.** harkaq mide sólo evidencia **negativa**: denegaciones Landlock bajo
21política=clausura (`harkaq-audit.c` emite JSON `{path, motivo}`, `harkaq-verdict.py` clasifica
22esperada/deuda). Lo que NO mide: **qué parte de la clausura concedida se usó de verdad**.
23
24**La brecha que eso deja.** harkaq detecta *sub*-declaración (deuda) pero no
25*sobre*-declaración: una dep declarada y jamás tocada es invisible, engorda la clausura, y esa
26grasa se hereda directo a la política runtime derivada del cierre §3 (política más ancha de lo
27necesario = evidencia más débil). La traza positiva es el dual exacto del veredicto actual.
28
29**Tareas:**
30
31- **T1.1 — sonda de uso positivo.** Un `harkaq-trace` que emita la lista de paths *abiertos*
32 por fase dentro del sandbox. Mecanismo candidato: fanotify — que ya es frente delegado por
33 arje (journal fanotify, ver handoff) ⇒ reutilizar ese trabajo, no abrir mecanismo nuevo.
34 Alternativa si fanotify dentro del userns del sandbox se complica: eBPF sobre `openat`
35 (BPF_SYSCALL ya es explícito en los kernels propios, commit 2b7c701).
36- **T1.2 — normalización estilo `.meta`.** Orden estable, dedupe, paths relativos a la jaula,
37 filtrar tmpdirs / `/out` / `/src`. Las reglas de ruido viven junto a la política — la misma
38 fuente de verdad que los `# expect` (D3: no se calla, se clasifica; el ruido se declara,
39 no se pierde). Aquí es donde la experiencia de META MODE ahorra iteraciones: readdir sin
40 orden garantizado y accesos especulativos del compilador son las dos fuentes de ruido que
41 ellos ya catalogaron.
42- **T1.3 — sellar la traza.** El `b3:` de la traza normalizada viaja junto al `of_tree` del
43 artefacto. Uso inmediato para **why-differs (cierre §2, top-2)**: dos builders que divergen
44 en hash se comparan primero por traza — diff de dos listas de texto ANTES que diff
45 estructural de binarios. El descenso por rangos de `reconcile`/`format` aplica igual.
46- **T1.4 — `harkaq-suggest --poda`.** Con traza positiva, sugerir *remover* deps
47 declaradas-no-usadas: el dual del suggest actual (que sólo sugiere añadir). Cierra el bucle
48 en ambas direcciones: la clausura converge a exactamente-lo-usado.
49
50**Riesgos.** Coste de fanotify en builds grandes (medir primero); falsos "no usados" por
51accesos condicionales entre corridas (mitigación: la poda sugiere, nunca aplica sola — mismo
52contrato que suggest). Piloto: 3 recetas ya instrumentadas (zlib, tar, brotli), en el worker
53(el laptop no compila C).
54
55**PILOTO MEDIDO (2026-07-17, worker, builds reales de zlib-ng y dwarves):**
56
571. **Ceguera-overlay — el hallazgo que corrige el diseño.** `FAN_MARK_FILESYSTEM` sobre el
58 fs del host (donde vive el store) NO ve los reads que el build hace a través del
59 `--tmp-overlay` del sandbox: el superbloque del overlay es OTRO, y overlayfs abre los
60 lower por dentro sin fsnotify en el sb de abajo. Medido: build completo de zlib-ng con
61 traza sobre el store = **0 eventos**; los binds sí aparecen (206 eventos de `/src` vía
62 `work/sources`, mismo sb) e incluso los reads host-side de takana (`recipes/*.toml` al
63 resolver deps — la traza ya sirve para auditar el hub).
642. **La salida, validada**: marcar `--fs /proc/<pid-bwrap>/root` — el magic-link cruza al
65 mount-ns y la marca cae sobre el SB DEL OVERLAY. Test sintético en el worker (overlay en
66 mntns aparte, lector externo): la lectura del lower a través del overlay **aparece** en
67 la traza. Bonus: los paths salen EN EL NAMESPACE DEL SANDBOX (`/usr/include/zlib.h`) —
68 el mismo idioma de la política harkaq, sin traducción host↔jaula. Cero código nuevo:
69 `harkaq-trace --fs /proc/$PID/root --prefix /usr`.
703. ~~Pendiente de orquestación~~ **HECHO en forma piloto**: `harkaq-trace-build.sh` envuelve
71 un `takana build`, engancha un tracer a cada bwrap que aparece (un bwrap por fase = una
72 traza por fase, la granularidad del veredicto) y emite canónica + sello + tabla
73 declaradas-vs-usadas. La integración sin carrera (lanzar el tracer ANTES del exec, no
74 por polling) sigue siendo del harness — coordinar con Opus.
75
76**PILOTO END-TO-END (2026-07-17, worker, `takana build` real de zlib en store desechable
77con deps hardlinkeadas — cero contaminación):**
78
79- **El build trazado SELLÓ y el hash REPRODUJO bit a bit un artefacto ya sellado del store
80 principal** (`b3:2623a403…`): la traza no perturba el build y un store fresco reproduce —
81 mini-consenso de regalo.
82- **La señal de poda (T1.4) existe y es filosa**: de la clausura concedida, el build usó
83 `make` **1 de 8** ficheros y `busybox` **1 de 2**. Por-fichero, no por-paquete — la misma
84 granularidad que la política.
85- **Taxonomía del ruido medida** (3520 paths únicos crudos): 84 de `/cache` (cache zig
86 interna = estado derivado del build, regla nueva en el normalizador), ~3.4k del stdlib de
87 zig (`/lib/compiler_rt/*.zig` — toolchain, no clausura de receta) y los productos del
88 propio build (`example.o`, tmpfiles borrados del upper). Tras las reglas, la clausura
89 real de zlib son un puñado de paths exactos. META MODE otra vez: **las reglas de
90 normalización SON el producto**.
91- Caveats anotados: ventana ciega del polling (~decenas de ms por fase), helpers de bwrap
92 duplican marcas (la dedupe lo absorbe), y el sello del piloto es sha256 PROVISIONAL (el
93 real debe ser b3, espacio de nombres del store). Gotcha operativo: `--store` ajeno a la
94 raíz del repo rompe la resolución del rootfs (`<padre-del-store>/.dev-fs`).
95
96---
97
98## 2. Capsicum / casper → la taxonomía del cierre §3 (política runtime)
99
100**La lección negativa.** Capsicum es más puro que Landlock (capabilities sobre fds,
101`cap_enter()`), y aun así FreeBSD terminó necesitando `casper`: demonios auxiliares porque
102hay una clase de necesidades — DNS, certificados TLS, syslog, random temprano, locale — que
103son *servicios del mundo*, no paths, y no caben en ninguna jaula por-fichero.
104
105**Dónde muerde en takana.** En build: en ninguna parte — los builds son offline por
106`--unshare-all`, la clase casper es vacía por construcción. En **runtime** (cierre §3: derivar
107la política Landlock del binario instalado desde la clausura medida en build): la clausura de
108build **no contiene** esa clase ⇒ derivar a ciegas produce binarios que revientan en el primer
109`getaddrinfo`. Saber esto *antes* de arrancar Fase 3 es exactamente el valor del prior art.
110
111**Tareas (papel, cero código hoy):**
112
113- **T2.1 — sección "servicios del mundo" en SDD 16 (Fase 3).** La política runtime = clausura
114 de build **+ clases de servicio declaradas** (`dns`, `tls-certs`, `random`, `locale`,
115 `syslog`). Se declara por clase con nombre, nunca se ensancha en silencio — D3 aplica en
116 runtime igual que en build.
117- **T2.2 — mapa clase→paths para musl.** Ventaja estructural de takana: musl no tiene
118 nsswitch ni dlopen de módulos NSS — `dns` = `/etc/resolv.conf` + socket UDP; `tls-certs` =
119 `/etc/ssl/certs`; `random` = `getrandom(2)` (ni path necesita). El mapa completo cabe en
120 media página; en glibc esto era intratable y es la mitad de por qué casper existe.
121- **T2.3 — la clase viaja en la ConcesionCapacidad.** `(blake3(binario), permisos)` gana un
122 campo de clases de servicio; `arje-absorb --attest-from` ya integra concesiones (PLAN-ATESTACION
123 §B.3) ⇒ sin formato nuevo.
124
125---
126
127## 3. libarchive / bsdtar → receta + extractor de referencia
128
129El único export de código real de FreeBSD que todo el mundo usa.
130
131- **T3.1 — `recipes/libarchive.toml`.** Receta C ⇒ worker. Nombre nuevo en el catálogo, sin
132 riesgo de colisión homónima (el gotcha de promote no aplica). Deps mínimas: zlib (xz/zstd
133 opcionales según features que se activen).
134- **T3.2 — verificación cruzada de formato.** Donde takana serializa árboles (pack/export),
135 extraer con bsdtar y comparar el árbol resultante contra el extractor propio = **consenso de
136 implementación aplicado al FORMATO** — mismo espíritu que el consenso de reconstrucción
137 (cierre §1) aplicado a los bytes del contenedor, no al build. Barato: un script de barrido
138 sobre N artefactos sellados.
139- **T3.3 (opcional) — bsdtar como dep de build** para tarballs exóticos donde busybox tar se
140 queda corto (hoy: ninguno conocido; tener la receta lo vuelve una línea en `[deps]`).
141
142---
143
144## Lo que NO se toma (para no re-litigarlo)
145
146- **Ports como corpus de recetas** — patches y Makefiles asumen BSD userland; Alpine+nixpkgs
147 (Etapa G) dominan estrictamente para musl-Linux.
148- **FreeBSD como builder de consenso multi-kernel** — sin namespaces/bwrap/Landlock allá; el
149 consenso multi-kernel barato ya existe (kernel propio vs genérico).
150- **bectl / ZFS boot environments** — `takana boot menu` + `activate --from-select` ya cubren
151 el concepto; como mucho, robar UX de nombres/rollback más adelante.
152- **bmake** — el mundo Linux necesita gmake; no reduce la deuda declarable de `/usr/bin/make`.
153
154## Orden propuesto
155
1561. **T2.1–T2.2** — papel, una tarde, desbloquea el diseño de Fase 3 antes de escribir código.
1572. **T1.1–T1.3** — piloto en 3 recetas en el worker; decide si fanotify o eBPF con datos.
1583. **T3.1** — receta a la próxima tanda del worker.
1594. **T1.4 y T3.2** — sólo después de validar los pilotos (la poda sin traza confiable es ruido).
plan-freebsd-aprovechables.md
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):
- 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).
- 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.
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
- T2.1–T2.2 — papel, una tarde, desbloquea el diseño de Fase 3 antes de escribir código.
- T1.1–T1.3 — piloto en 3 recetas en el worker; decide si fanotify o eBPF con datos.
- T3.1 — receta a la próxima tanda del worker.
- T1.4 y T3.2 — sólo después de validar los pilotos (la poda sin traza confiable es ruido).