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 [--overlay-src …] --tmp-overlay /` — base y deps de solo + lectura, escrituras a una capa tmpfs descartable. +- `--bind /src`, `--bind /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`. 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 │ + │ 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 `**. 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)