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>
This commit is contained in:
@@ -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.)*
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user