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 1184Shell 70Rust 62Markdown 501503 Definiciones

docs: SDD 16 harkaq — Draft 2 reescrito contra la realidad del builder (bwrap, no nix)

El Draft 1 estaba escrito contra el sandbox de nix. hammer no construye con
nix: construye con bubblewrap (ADR 0004, hammer-build/src/sandbox.rs). Eso
invalidaba por irrelevancia el argumento anti-nix (§0), las FOD y su red
abierta (§6 + D8), Q5, y la Fase 3 del fetcher mediado — que el propio doc
marcaba como el mayor riesgo de cronograma, de "meses".

Fase 0 ejecutada (5 minutos, no días):
  - ABI Landlock = 10 en el laptop (techo: audit/7, TSYNC/8, quiet/10).
  - Kernel de hammer 6.16.12-metal: CONFIG_SECURITY_LANDLOCK ausente, pero
    CONFIG_LSM ya lista landlock ⇒ falta un flag, no una versión. Q2 cerrada.
  - Cero FODs: download.rs baja con curl en el HOST y --unshare-all incluye
    --unshare-net ⇒ el fetcher mediado de D8 ya está construido. Fase 3 se
    elimina.

La brecha real es otra y paga mejor: el sandbox monta un rootfs Alpine ENTERO
como capa base ⇒ política ⊋ clausura ⇒ una receta usa Alpine sin declararlo y
pasa en verde. Es el bug que la campaña de de-Alpinización caza a mano. D1 +
audit de ABI 7 lo vuelve mecánico (path/dev/ino de lo no declarado).

Encaje en el modelo de confianza (recipe.rs:229): H1 garantiza que lo
declarado PASA; harkaq que lo declarado BASTA. Complementos simétricos.

Sobreviven intactos D1 (política derivada), D2 (evidencia negativa) y D3
(cero quieting). Nuevo Q1 = el riesgo técnico real: cómo llegan los registros
AUDIT_LANDLOCK_* al lector (CAP_AUDIT_READ, auditd, userns, flags de exec).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

9478f4c065d9c31dd8c586b6f00281f568fdfa87 · por sergio · (hace 3 meses) · padre e263b66ec · ver el árbol en este commit

Delta semántico

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

2 archivos · +400 −0

diff --git a/docs/16-harkaq-jaula.md b/docs/16-harkaq-jaula.md
new file mode 100644
index 00000000..b057ae26
--- /dev/null
+++ b/docs/16-harkaq-jaula.md
@@ -0,0 +1,399 @@
+# SDD 16 — harkaq: la jaula de hammer
+
+**Estado:** Draft 2 · **Fecha:** 2026-07-15 · **Nombre provisional:** *harkaq* ("el que detiene"; renombrable, ningún identificador depende del nombre)
+
+> **Qué cambió del Draft 1.** El Draft 1 se escribió contra el sandbox de nix. hammer no usa nix
+> para construir: usa bubblewrap (ADR 0004, `crates/hammer-build/src/sandbox.rs`). Eso invalidó
+> por irrelevancia ~40% del documento — el argumento anti-nix, las FOD y su red abierta, y la fase
+> del *fetcher mediado* que era el mayor riesgo de cronograma. Fase 0 (reconocimiento) se ejecutó
+> y sus tres mediciones están en §3. El resultado neto es un proyecto **más chico, más barato y
+> con un beneficio que cobra antes**. Lo que sobrevive intacto es el corazón: D1 (política
+> derivada), D2 (evidencia negativa) y D3 (cero quieting).
+
+**Tesis:** hammer es una distro cuyo autor es un modelo. Su sandbox (bwrap) fue diseñado para dar
+*hermeticidad de facto* — aislar el build del host — no para *probar* hermeticidad ni para acotar
+lo que el build usa **de adentro de la jaula**. harkaq cierra esa brecha sin pedirle política a
+nadie: **la clausura de entradas declaradas de la receta ya es la política**. El subproducto es
+más valioso que el producto — la ausencia de denegaciones bajo una política igual a la clausura
+es *evidencia mecánica* de que lo declarado **basta**, encadenable en willay y consumible por iniy.
+
+---
+
+## 0. Por qué esto no lo resuelve bwrap
+
+El sandbox actual (`sandbox.rs:224`) hace bien lo que se propuso hacer:
+
+- `--unshare-all` — netns, pidns, mountns, ipcns, utsns, userns. **Sin red, ya hoy.**
+- `--overlay-src <rootfs Alpine> [--overlay-src <dep>…] --tmp-overlay /` — base y deps de solo
+  lectura, escrituras a una capa tmpfs descartable.
+- `--bind <src> /src`, `--bind <out> /out` — las únicas dos superficies RW reales.
+- Entorno mínimo y fijado (`SOURCE_DATE_EPOCH`, `CC`, `-mcpu=baseline`, `CARGO_BUILD_JOBS=1`).
+
+Eso da hermeticidad **respecto del host**. La brecha no está ahí. Está adentro:
+
+> **El sandbox monta un rootfs Alpine entero como capa base del overlay.**
+
+O sea `política ⊋ clausura`, por un margen enorme. Una receta puede usar cualquier header,
+cualquier `.so`, cualquier binario que Alpine traiga — **sin declararlo en `deps`** — y el build
+pasa en verde. El artefacto queda sellado y reproducible, y aun así arrastra una dependencia
+fantasma que nadie escribió.
+
+Ese no es un bug hipotético: es exactamente el bug que la campaña de **de-Alpinización** viene
+cazando *a mano, receta por receta*, leyendo logs y adivinando. El patrón documentado (strip
+`makedepends`, fase configure explícita, `.pc Requires` → `[deps].build`) es el parche manual de
+un problema que el kernel puede diagnosticar solo.
+
+**La inversión que hace hammer:** el sandbox de bwrap asume que quien escribe la receta sabe lo
+que declaró. En hammer, quien escribe la receta es un modelo, la receta se importa
+automáticamente desde nixpkgs/Alpine, y "lo que declaró" es precisamente la parte que falla.
+Nadie más tiene ese problema porque nadie más tiene una distro cuyo autor sea un modelo.
+
+**No-tesis:** esto no reemplaza a bwrap. harkaq **compone** con él (§5, D4): la jaula de mount y
+namespaces sigue siendo de bwrap; harkaq agrega la política de grano fino y —lo que importa— el
+canal de evidencia.
+
+### 0.1 El lugar exacto en el modelo de confianza
+
+De `hammer-core/src/recipe.rs:229`, sobre la evidencia H1 (proof-carrying recipes, SDD 15):
+
+> *"Frontera honesta: garantiza que **lo declarado pasa**, no que **lo declarado basta**."*
+
+harkaq es el complemento simétrico de esa frase, y por eso encaja sin forzar nada:
+
+| | Pregunta | Mecanismo | Evidencia |
+|---|---|---|---|
+| **H1** | ¿lo declarado **pasa**? | checks corridos en el sandbox | positiva (los checks pasan) |
+| **harkaq** | ¿lo declarado **basta**? | política = clausura, en el kernel | negativa (nadie denegó nada) |
+
+Juntas cierran el cuadro: H1 dice que el artefacto *hace lo que dice*; harkaq dice que *no
+necesitó nada que no dijo*. Ninguna de las dos pide confiar más en el modelo. Las dos piden
+verificar más — que es el principio declarado de SDD 15 §0.
+
+---
+
+## 1. Metas
+
+1. Ejecutar el builder bajo una política **derivada, no escrita**: `política = clausura(deps declaradas)`.
+2. Producir, por cada build, un veredicto de hermeticidad con evidencia:
+   `hermético ⟺ política = clausura ∧ denegaciones = ∅`.
+3. Emitir esa evidencia hacia willay/iniy, direccionada por blake3, como un `kind` nuevo junto a
+   la evidencia H1.
+4. **Convertir la de-Alpinización en un diagnóstico mecánico**: cada denegación nombra el path,
+   el dev y el ino de lo que el build usó sin declarar.
+5. Degradación explícita y declarada: en kernel sin Landlock, harkaq marca el build **sin
+   evidencia** — nunca finge.
+
+## 2. No-metas
+
+- No corre en wawa. Ver D6.
+- No reemplaza el sandbox de bwrap; se compone encima. Defensa en capas.
+- No es un contenedor de propósito general ni compite con bubblewrap/landrun.
+- v1 no cubre macOS (hammer es musl/Linux).
+- No promete contener un exploit de kernel. Ver §8.
+- **No reabre ADR 0004.** hammer no adopta nix como backend de build. Esa opción sigue diferida
+  y harkaq no la necesita.
+
+---
+
+## 3. Fase 0 — Reconocimiento: EJECUTADA (2026-07-15)
+
+El Draft 1 estimaba "días". Fueron cinco minutos, porque las tres preguntas tenían respuesta
+mecánica. Las tres respuestas están medidas, no supuestas.
+
+### 3.1 ABI de Landlock
+
+Medido con el syscall (`landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION)`), que
+es la vía correcta: los kernels de otras fuentes traen features backporteadas y **el número de
+versión del kernel miente** (RHEL 9.6 backporteó hasta ABI 5 sobre un 5.14).
+
+| Dónde | Kernel | ABI | Veredicto |
+|---|---|---|---|
+| Laptop de desarrollo | 7.2.0-rc3-1-cachyos-rc | **10** | techo; todo disponible |
+| Kernel de hammer | 6.16.12-metal (`recipes/linux.toml`) | **ausente** | `# CONFIG_SECURITY_LANDLOCK is not set` |
+
+Lo que hace falta de cada ABI, y desde dónde:
+
+| ABI | Kernel | Qué trae | Para harkaq |
+|---|---|---|---|
+| 2 | 5.19 | `FS_REFER` | **necesario** — sin refer no compila nada real |
+| 3 | 6.2 | `FS_TRUNCATE` | necesario |
+| 4 | 6.7 | `NET_BIND_TCP`, `NET_CONNECT_TCP` | **inerte** — no hay red que filtrar (§6) |
+| 5 | 6.10 | `FS_IOCTL_DEV` | cierra ioctls a dispositivos |
+| 6 | 6.12 | `SCOPE_ABSTRACT_UNIX_SOCKET`, `SCOPE_SIGNAL` | corta fuga lateral |
+| 7 | 6.15 | **audit de denegaciones** | **este es el proyecto entero** |
+| 8 | 7.0 | `RESTRICT_SELF_TSYNC` | robustez (todos los hilos) |
+| 10 | — | `ADD_RULE_QUIET`, `quiet_access_*` | **deliberadamente NO usar** (D3) |
+
+El hallazgo que ordena el diseño sigue siendo ABI 7: Linux 6.15 registra las peticiones denegadas
+vía audit con el motivo — `domain`, `blockers` (p.ej. `fs.make_reg,fs.refer`) y la identificación
+del objeto (`path`, `dev`, `ino` para filesystem; `opid`, `ocomm` para señales).
+
+Traducción: **el mecanismo de traza que el Draft 1 iba a construir ya lo hace el kernel.** No hay
+que instrumentar syscalls ni interponer un tracer. La evidencia sale del audit log del propio LSM,
+que es un productor mucho más confiable que cualquier cosa en espacio de usuario.
+
+### 3.2 El kernel de hammer: falta un flag, no una versión
+
+`store/060b34a6…-linux-metal/boot/config-6.16.12-metal` dice:
+
+```
+# CONFIG_SECURITY_LANDLOCK is not set
+CONFIG_LSM="landlock,lockdown,yama,loadpin,safesetid,selinux,smack,tomoyo,apparmor,ipe,bpf"
+CONFIG_SECCOMP_FILTER=y
+```
+
+Respuesta a la Q2 del Draft 1 ("¿vale la pena forzar ≥6.15 en la distro?"): **no hace falta
+forzar nada.** 6.16.12 ya trae hasta ABI 8, y `CONFIG_LSM` ya lista `landlock` de primero. Falta
+sólo compilarlo: un `scripts/config -e SECURITY_LANDLOCK` junto a los otros ajustes de la fase
+`configure` de `recipes/linux.toml` (`SECURITY_PATH` entra por `select`). Cuesta un rebuild
+(~35min) y cambia el hash del kernel. Es el único trabajo de infraestructura del proyecto.
+
+seccomp ya está `=y` en ambos kernels. No hay trabajo ahí.
+
+### 3.3 Las FODs: no existen. El fetcher mediado: ya está construido
+
+El Draft 1 dedicaba §6 entero + D8 al problema de las derivaciones de salida fija y su red
+abierta, y ponía el *fetcher mediado* en Fase 3 llamándolo *"el mayor riesgo de alcance del
+proyecto"*, de *"meses"*.
+
+Ese trabajo está hecho desde siempre, por construcción:
+
+- `crates/hammer-build/src/download.rs` corre `curl` **en el host**, fuera del sandbox.
+- El builder recibe **bytes** vía `--bind <src> /src`. Nunca un socket.
+- `--unshare-all` incluye `--unshare-net`: netns vacío para **todo** build, sin excepciones,
+  porque no existe la categoría "derivación que necesita red".
+
+Eso *es* la jaula con ventanilla que D8 proponía construir. hammer nunca tuvo el agujero de las
+FOD porque nunca tuvo FODs: separar el fetch del build fue una decisión temprana y silenciosa que
+resultó ser la correcta por razones de seguridad que nadie había escrito todavía. Queda escrita acá.
+
+**Fase 3 se elimina del plan.** El riesgo de cronograma desaparece.
+
+---
+
+## 4. Arquitectura
+
+```
+ receta (escrita/importada por el agente)
+        │
+        ├─ deps declaradas (paths del store, por hash)
+        │
+        ▼
+ ┌──────────────────────────────────────────────────────────┐
+ │ harkaq-policy   (puro, testeable sin kernel)             │
+ │   clausura(deps) → PolicySpec {                          │
+ │      fs_ro:   [store paths de la clausura + base runtime]│
+ │      fs_rw:   [/src, /out, /tmp, /cache]                 │
+ │      scoped:  UNIX_ABSTRACT | SIGNAL                     │
+ │   }                                                      │
+ │   PolicyId = blake3(postcard(PolicySpec))                │
+ └───────────────────────────┬──────────────────────────────┘
+                             ▼
+ ┌──────────────────────────────────────────────────────────┐
+ │ harkaq-exec     (musl, Rust) — DENTRO de bwrap           │
+ │   bwrap ya dio: ns, overlay, /src, /out, sin red         │
+ │   1. landlock: PolicySpec → ruleset (best-effort/ABI)    │
+ │   2. seccomp-bpf: allowlist (D4), vía --seccomp <fd>     │
+ │   3. no_new_privs, drop caps, exec builder               │
+ └───────────────────────────┬──────────────────────────────┘
+                             ▼
+ ┌──────────────────────────────────────────────────────────┐
+ │ harkaq-audit    (lector de AUDIT_LANDLOCK_*)             │
+ │   denegaciones → Denial { blockers, path, dev, ino, ... }│
+ └───────────────────────────┬──────────────────────────────┘
+                             ▼
+        Verdict { PolicyId, recipe_hash, artifact_hash, denials[] }
+                             │
+              blake3 ──► CAS ──► willay ──► iniy
+```
+
+Ley de dependencia: `harkaq-policy` no conoce el kernel (función pura de `Recipe` → `PolicySpec`,
+testeable con `cargo test` en cualquier máquina, incluso sin Landlock). `harkaq-exec` no conoce
+la receta. El acoplamiento vive sólo en `hammer-build`.
+
+Nota de encaje: `Sandbox::bwrap_args` ya construye la lista de deps (`self.deps`) — es
+literalmente el input de `clausura()`. Y `merge_deps_layer()` ya las funde en una capa. El
+`PolicySpec` se deriva de datos que la struct `Sandbox` ya tiene en la mano.
+
+---
+
+## 5. Decisiones cerradas
+
+### D1 — La política se deriva, no se escribe
+
+Es el corazón, y sobrevive al Draft 1 sin un rasguño. Todo sandbox del mundo muere en la
+ergonomía: alguien mantiene a mano un perfil de permisos, nadie lo revisa, todos terminan en
+permisivo. hammer no tiene ese problema porque **la receta ya declara sus deps por hash**.
+`clausura(deps)` es el conjunto exacto de rutas de solo lectura. Cero configuración humana,
+precisión igual a la del direccionamiento por contenido.
+
+Corolario: no existe "modo permisivo". Una receta que necesita más de lo que declara está mal
+escrita, y ese es exactamente el diagnóstico que queremos que el agente reciba — hoy lo
+producimos leyendo logs a mano y llamándolo de-Alpinización.
+
+### D2 — La evidencia es negativa, y por eso es barata
+
+**No se traza lo permitido.** La tentación es loguear todo acceso para "probar" qué tocó el build;
+eso exige fanotify o eBPF, cuesta rendimiento y produce un río de datos que nadie audita.
+
+La formulación correcta es la contrapositiva:
+
+> `hermético(build) ⟺ política = clausura(deps) ∧ denegaciones = ∅`
+
+Si la política concede *exactamente* la clausura declarada y el kernel no denegó nada, entonces el
+build no usó nada fuera de lo declarado. **La política es la afirmación; la ausencia de
+denegaciones es la prueba.** Coste de la traza: un lector de audit y una lista casi siempre vacía.
+
+Esto convierte la reproducibilidad de hammer, que hoy es una promesa arquitectónica verificable
+sólo rebuildeando, en un certificado por build.
+
+### D3 — Cero quieting. Las denegaciones son el producto
+
+ABI 10 permite suprimir selectivamente logs de accesos denegados por objeto (`LANDLOCK_ADD_RULE_QUIET`,
+campos `quiet_access_*`). Es una feature excelente para sandboxear apps de escritorio ruidosas —
+y **veneno para harkaq**. Silenciar una denegación es destruir la única evidencia que produce el
+sistema. En harkaq `quiet_*` está prohibido por diseño; una denegación esperada no se calla, se
+*clasifica* en el `Verdict`.
+
+(De la propia doc del kernel: el objeto queda marcado como quiet si al menos una llamada lo pidió,
+y llamadas posteriores sin el flag no lo limpian — es pegajoso. Razón extra para no tocarlo.)
+
+Este laptop reporta ABI 10, así que la feature está a un flag de distancia. La disciplina es no
+usarla.
+
+### D4 — Landlock no basta; seccomp es obligatorio, no opcional
+
+Landlock cubre fs, puertos TCP e IPC scoping. No cubre `ptrace`, `bpf`, `userfaultfd`, `keyctl`,
+`io_uring`, montajes, ni la mitad de la superficie interesante. seccomp-bpf con allowlist por
+syscall es la otra mitad de la jaula.
+
+Buena noticia de composición: **bwrap acepta `--seccomp <fd>`**. No hay que reemplazar el
+launcher; se le pasa el filtro al que ya tenemos. `CONFIG_SECCOMP_FILTER=y` en ambos kernels.
+
+`io_uring` va explícitamente denegado en v1 (superficie de kernel enorme, y bypassea buena parte
+del análisis por-syscall). Si un builder lo necesita, que lo justifique.
+
+### D5 — Herencia y TSYNC
+
+Las restricciones de Landlock se heredan automáticamente por `fork()` y `exec()`, así que todo el
+árbol de procesos del build queda contenido sin trabajo extra — que es exactamente lo que hace
+falta cuando `Sandbox::run` lanza `/bin/sh -c` que lanza make que lanza `zig cc`. Con ABI 8
+disponible (6.16.12 lo tiene), usar `RESTRICT_SELF_TSYNC` para cubrir todos los hilos.
+
+### D6 — harkaq es órgano de hammer, no de la suite. Rompe la paridad wawa, y está bien
+
+Landlock, seccomp y cgroups son Linux puro; en `x86_64-unknown-none` no existen. Esto viola el
+principio de paridad Linux/wawa que gobierna el resto del ecosistema, y hay que decirlo en voz
+alta antes de que alguien lo cobre.
+
+El costo es aceptable porque **el bucle de build del agente es una actividad Linux por
+definición**. wawa nunca compila nada: consume artefactos atestados del CAS. La frontera de
+confianza de wawa es la atestación; la de hammer es la jaula. Son mecanismos distintos para
+posiciones distintas del pipeline, y eso es coherente, no inconsistente.
+
+### D7 — Fallo cerrado, y honesto sobre el ABI
+
+- `abi_minima = V3` para intentar el build (sin `REFER` y `TRUNCATE` no compila software real).
+- `abi_evidencia = V7` para emitir un `Verdict` con evidencia.
+- Entre V3 y V7: el build corre, el `Verdict` sale `SinEvidencia`, iniy lo trata con
+  `uncertainty` alta en vez de creerle.
+- Sin Landlock (el kernel de hammer **hoy**): `SinEvidencia`, y el build corre igual bajo el bwrap
+  de siempre. harkaq es inerte, no un muro.
+
+Nunca se emite evidencia que el kernel no respaldó. Se consulta el ABI con el syscall, jamás
+`uname`.
+
+### D8 — La red ya está cerrada; harkaq no la toca
+
+(Reemplaza al D8 del Draft 1, que trataba de las FOD.)
+
+`--unshare-all` ⇒ netns vacío ⇒ los derechos de red de ABI 4 son **inertes**. No se implementan.
+Si algún día un build necesitara red, la respuesta no es abrir el netns: es extender el fetcher
+del host (§3.3), que es el embudo con hash donde ancla el CAS y donde willay puede auditar.
+
+`SCOPE_ABSTRACT_UNIX_SOCKET` (ABI 6) sí se usa: cierra la fuga simétrica hacia sockets abstractos.
+Ojo con la semántica documentada — un socket datagram ya conectado antes del scoping puede seguir
+enviando; y el scoping no admite reglas: si un dominio está scoped, no hay forma de permitir un
+recurso puntual fuera. Es todo-o-nada, lo cual para nosotros está bien.
+
+---
+
+## 6. Fases
+
+**Fase 0 — Reconocimiento.** ✅ **CERRADA (2026-07-15)**. Ver §3. Salidas: ABI 10 en el laptop,
+Landlock ausente-pero-a-un-flag en el kernel de hammer, cero FODs y fetcher ya construido.
+
+**Fase 1 — El hito demostrable (una tarde).**
+Encender `SECURITY_LANDLOCK` en `recipes/linux.toml`. Tomar **una** receta real de hammer.
+Derivar su política de la clausura. Correrla bajo Landlock+seccomp dentro del bwrap actual. Emitir
+el `Verdict` con `denials = []`.
+Luego correr **una receta que sepamos no de-Alpinizada** y mostrar que la jaula nombra exactamente
+qué usó sin declarar, con path/dev/ino.
+**Ese par de corridas es el argumento entero del proyecto**, y se enseña en dos minutos.
+
+> Cambio respecto del Draft 1: el segundo acto era una derivación de juguete que leía
+> `/etc/passwd`. Una receta real no de-Alpinizada es *mejor demo* (es el problema que de verdad
+> tenemos) y de paso le entrega valor inmediato a una campaña en curso.
+
+**Fase 2 — `harkaq-policy` completo + medir la brecha.** Clausura real, `/src`, `/out`, `/tmp`,
+`/cache`, `/proc` y `/dev` mínimos, y el **runtime base** (sin `/bin/sh` ni la libc del rootfs no
+arranca nada — ver Q3). Correr contra N recetas del catálogo y medir la tasa de falsos positivos.
+*Métrica de éxito: <5% de recetas necesitan excepción.*
+El delta entre `política = clausura` y `política = clausura + base Alpine` **es** la medida
+cuantitativa de la deuda de de-Alpinización del catálogo. Ese número no existe hoy.
+
+**Fase 3 — El bucle epistémico.** `Verdict → willay → iniy`, como `kind` nuevo junto a la
+evidencia H1 (§0.1). Aquí el agente empieza a recibir su propio reflejo: una receta cuyo build
+denegó cosas es una receta de la que el sistema desconfía, y esa desconfianza es un número, no una
+vibra.
+
+*(La Fase 3 del Draft 1 —el fetcher mediado— se eliminó: ya existe. Ver §3.3.)*
+
+---
+
+## 7. Riesgos y honestidades
+
+- **El kernel es la TCB.** harkaq no contiene un exploit de kernel. Landlock+seccomp reduce
+  superficie; no la elimina. Si el modelo de amenaza incluye "la IA encuentra un 0-day de kernel",
+  la respuesta correcta es microVM, no LSM. Decir esto en el README, no en letra chica.
+- **La entrega de la evidencia es el único riesgo técnico serio que queda.** Ver Q1. Todo el
+  proyecto cuelga de poder leer los registros `AUDIT_LANDLOCK_*` de forma confiable desde el
+  proceso que orquesta el build. Si eso no se resuelve limpio, harkaq queda sin producto.
+- **Falsos positivos matan proyectos de sandboxing.** Si el 30% de los builds necesita excepción
+  manual, harkaq se desactiva y queda como decoración. La Fase 2 existe para descubrir esto
+  temprano y barato. Atenuante: acá un "falso positivo" suele ser un **verdadero positivo de
+  de-Alpinización**, y el catálogo tiene ~750 recetas para probarlo.
+- **Domesticar el vocabulario.** harkaq no prueba que un build fue hermético. Prueba que *bajo el
+  supuesto de que el kernel y la derivación de política son correctos*, el build no accedió fuera
+  de su clausura declarada. Es una afirmación fuerte y defendible. La versión sin asteriscos es la
+  que te van a cobrar. (Mismo criterio que SDD 15 §0: "verificado bajo supuestos nombrados".)
+- **Competencia.** Como proyecto OSS genérico, "sandbox para agentes" es un espacio poblado (ya
+  existe landrun) y la ventana no es infinita. Como el ejecutor de hammer, no compite con nadie: el
+  diferenciador no es la jaula, es que **la política sale del hash y la evidencia entra al grafo de
+  confianza**. Nadie más tiene CAS + log monótono + grafo para cerrar ese bucle.
+- **El rebuild del kernel cambia su hash.** Encender `SECURITY_LANDLOCK` invalida
+  `060b34a6…-linux-metal` y lo que dependa de él. Es esperado y barato, pero hay que agendarlo, no
+  descubrirlo.
+
+---
+
+## 8. Preguntas abiertas
+
+- **Q1 (Fase 1, la que importa):** ¿cómo llegan los registros `AUDIT_LANDLOCK_*` a `harkaq-audit`?
+  El subsistema de audit del kernel pide `CAP_AUDIT_READ` para leer por netlink, y suele tener un
+  único consumidor (`auditd`) con el que habría que competir o coordinar. Además el build corre
+  bajo `--unshare-all` (userns) y `exec`ea varias veces, lo cual interactúa con los flags de
+  logging de `restrict_self` (`LOG_NEW_EXEC_ON` y compañía). **Esto se prueba antes que nada**: si
+  la evidencia no sale del kernel de forma confiable, no hay proyecto. Es el primer experimento,
+  no el último.
+- **Q2 (Fase 2):** ¿qué es exactamente el "runtime base" que toda receta necesita y ninguna
+  declara (`/bin/sh`, la libc del rootfs, `/opt/zig`)? ¿Va en la clausura implícitamente, o se
+  declara y se vuelve parte del hash de la receta? *Esta es la decisión de diseño de verdad*, y
+  determina si la métrica de la Fase 2 mide deuda real o ruido.
+- **Q3 (Fase 2):** ¿`/proc` y `/dev` mínimos rompen builds reales? (`/proc/self` suele hacer falta;
+  `/proc/sys` casi nunca.) bwrap ya monta `--proc /proc --dev /dev`.
+- **Q4:** ¿el `Verdict` entra en `Recipe::hash_inputs`? Por simetría con `Evidence` (H1), **no**:
+  hermeticidad es comportamiento, no identidad. Confirmar con el mismo criterio de `recipe.rs:228`.
+
+*(Las Q1/Q4/Q5 del Draft 1 —el vector page-cache de `copy.fail`, la fracción de FODs, y si harkaq
+envuelve o reemplaza a nix— se eliminan: eran todas sobre nix.)*
diff --git a/docs/README.md b/docs/README.md
index 63f70b74..947f9b68 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -20,6 +20,7 @@ discrepancia, o se corrige el código o se actualiza el SDD con un commit que ex
 | 10 | [Roadmap](10-roadmap.md) | Fases, MVP, primer entregable |
 | 11 | [Bootstrap from-scratch](11-bootstrap.md) | Track posterior: Stage 0/1/2, auto-alojamiento, semilla pinned |
 | 12 | [`arje` como init real del Stage 1](12-init-real.md) | Contrato de runtime: seed card, hammerd supervisado, `CRASHED` real |
+| 16 | [harkaq: la jaula de hammer](16-harkaq-jaula.md) | Landlock+seccomp sobre el bwrap actual; política = clausura; evidencia negativa de hermeticidad |
 
 ### Runbooks (operativos)