harkaq: HITO DE FASE 1 — wiring en Rust; veredicto por fase sobre una receta real

crates/hammer-build/src/harkaq.rs + Sandbox::bwrap_args_harkaq: harkaq sale de
`hammer build`, no de un script. Deriva la política de la clausura declarada
(D1), lanza el lector, mete harkaq-exec como último eslabón antes del `sh -c`, y
recoge el Verdict por FASE (configure/compile/install son bwraps distintos ⇒
dominios distintos ⇒ un veredicto cada uno).

EL HITO, sobre recipes/zlib.toml (receta real del catálogo):
  configure →  Hermetico   esperadas (14): /usr/bin/gcc   deuda: ninguna ✓
  compile   →  Impuro      DEUDA (1): /usr/bin/make       → rc=126, la jaula lo frenó

El diagnóstico completo de zlib cabe en una frase: lo único que toma de Alpine
sin declararlo es `make`.

INERTE sin HARKAQ=1: mismos args de bwrap, mismo entorno, ningún proceso extra.
Requisito duro, no cortesía — 700+ artefactos sellados no pueden cambiar de hash
por encender un diagnóstico. Los 57 tests previos del crate siguen verdes sin
tocar, que es la prueba.

harkaq-policy es PURO y se testea sin kernel (4 tests nuevos, como pide §4):
traduce store→sandbox, excluye la metadata `.hammer/` del artefacto, y deja
listables los ancestros de cada fichero de la clausura.

La integración se delató sola en su PRIMERA corrida real: faltaba /cache como
superficie de contrato (ZIG_GLOBAL_CACHE_DIR=/cache/zig — zig CREA dirs ahí) y
la fase moría con `fs.make_dir /cache/zig/tmp`. De ahí que las superficies de
contrato las decida el Sandbox (que sabe qué montó) y no harkaq: /cache sólo
existe si hay cache_dir, y una política que nombre un path inexistente aborta el
build a propósito.

libc en hammer-build: Child::kill() manda SIGKILL y el lector moriría MUDO, sin
emitir el Verdict — justo lo que no queremos de un componente cuyo producto ES
el veredicto. Hace falta SIGTERM.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 15:55:29 -04:00
co-authored by Claude Opus 4.8
parent ed614e6898
commit 47982d7c6a
5 changed files with 421 additions and 2 deletions
+20 -1
View File
@@ -671,7 +671,26 @@ Después: tomar **una** receta real de hammer. Derivar su política de la clausu
Landlock+seccomp dentro del bwrap actual. Emitir el `Verdict` con `denials = []` **y el canario
visto**.
**Estado (2026-07-15): la cadena corre de punta a punta.** `harkaq-audit` (lector, host,
**✅ HITO DE FASE 1 CUMPLIDO (2026-07-15).** `hammer build` sobre una receta **real del catálogo**
(`recipes/zlib.toml`), con la política derivada de la clausura declarada y el `Verdict` emitido
**por fase**:
```
configure → Hermetico esperadas (14): /usr/bin/gcc deuda: ninguna ✓
compile → Impuro DEUDA (1): /usr/bin/make → build rc=126 (la jaula lo frenó)
```
El diagnóstico completo de zlib cabe en una frase: **lo único que toma de Alpine sin declararlo
es `make`**. Wiring en `crates/hammer-build/src/harkaq.rs`, detrás de `HARKAQ=1` e **inerte** sin
él (requisito duro: 700+ artefactos sellados no pueden cambiar de hash por encender un
diagnóstico). `harkaq-policy` es puro y se testea sin kernel (4 tests), como pide §4.
La integración se delató sola en su primera corrida: faltaba `/cache` (superficie de contrato —
`ZIG_GLOBAL_CACHE_DIR=/cache/zig`, zig **crea** directorios ahí) y la fase moría con
`fs.make_dir /cache/zig/tmp`. De ahí que las superficies de contrato las decida el `Sandbox`, que
es quien sabe qué montó, y no harkaq: `/cache` sólo existe si hay `cache_dir`.
**Estado previo — la cadena corre de punta a punta.** `harkaq-audit` (lector, host,
`CAP_AUDIT_READ`) + `harkaq-exec` (política, estático, dentro de bwrap) + `harkaq-run.sh`
(orquesta y pone el canario). Los dos actos, con comandos sintéticos: