# SDD 05 — Diario de mutaciones Este es el insight central de `hammer`: **el diario es tu configuración del sistema — sin ser declarativa.** No declaras tu sistema en un archivo de texto antes de vivirlo; vives el sistema imperativamente, y el propio sistema deriva, *a posteriori*, un mapa de tu desviación respecto a la base limpia construida por el laboratorio. Esto rompe la falsa dicotomía declarativo vs imperativo, y resuelve dos problemas a la vez: - El de Arch/Gentoo: con los meses se pudren en archivos huérfanos porque el gestor pierde el rastro de lo que el usuario hizo a mano. - El de Nix: para evitar lo anterior, te ata las manos (todo debe declararse antes). ## 1. El mecanismo Un daemon ultraligero (`hammerd`) usa **`fanotify`** a nivel de kernel para registrar **exclusivamente las mutaciones** sobre los directorios del sistema (`/bin`, `/sbin`, `/lib`, `/etc`). No bloquea ninguna acción. Te deja destruir, mover y crear archivos a tu antojo. Pero anota, en silencio, un diario de modificaciones. > `fanotify` sobre `inotify`: cobertura a nivel de superbloque/mount, eventos de modificación > con info de PID/UID, y menor coste que vigilar recursivamente cada subdirectorio. ## 2. Qué registra (y qué no) **Registra** mutaciones del FHS gestionado: - creación / reemplazo / borrado de archivos en `/bin`, `/sbin`, `/lib`, `/etc`, - qué los originó (PID, UID, y si fue una hidratación de `hammer`, el `artifact_hash`), - ediciones de archivos de config en `/etc`. **No registra:** - actividad efímera en `/tmp`, `/run`, `/proc`, `/sys`, `/dev`, - lecturas/ejecuciones (sólo mutaciones), - nada dentro de un overlay activo (el ruido del experimento no entra; sólo el `commit`). ## 3. Formato del diario Log **append-only de texto plano** (JSON-líneas), legible con `cat`/`grep`/`jq`. Cada línea es un evento atómico. (Los timestamps los provee el daemon; el formato no impone reloj de construcción.) ```jsonl {"ts":"2026-06-06T18:40:01Z","op":"replace","path":"/bin/grep","by":{"uid":1000,"pid":4821},"artifact":"b3:9f2a…","note":"hydrate grep@perl-default"} {"ts":"2026-06-06T18:41:12Z","op":"edit","path":"/etc/network.conf","by":{"uid":1000,"pid":4990},"diff_hash":"b3:7c1d…"} {"ts":"2026-06-06T18:42:03Z","op":"manual-replace","path":"/bin/ls","by":{"uid":1000,"pid":5102},"artifact":null,"note":"usuario pisó ls a mano"} ``` `artifact: null` + `op: manual-replace` ⇒ el usuario reemplazó algo a mano fuera del flujo de `hammer`. Eso no es un error; es información. El diario distingue "mutación trazable" (vino de una receta/artefacto conocido) de "mutación opaca" (pisada manual), sin prohibir ninguna. ## 4. Exportar: del diario al `.swm` `hammer export` lee el diario, lo recorta al delta relevante respecto a una **base** conocida, y produce un manifiesto `.swm` ([SDD 06](06-swm-format.md)): - **base** = el conjunto de commits fijados + versión de distro de los que partiste. - **mutations** = las mutaciones trazables convertidas a recetas (`source_patch`) y las ediciones de config (`config_edit`), más reglas de init si aplican. - Las mutaciones **opacas** (pisadas manuales sin artefacto) se reportan como advertencia: no se pueden compartir como receta reproducible. `hammer` te dice exactamente cuáles y por qué. Así, **exportar el diario = exportar tu distro**, pero sólo la parte que es reproducible y verificable. Lo que pisaste a ciegas, lo sabes y decides qué hacer con ello. ## 5. Replicar en otra máquina No copias una config abstracta: exportas el diario de tus acciones humanas sobre el metal. La otra máquina, partiendo de la misma base, **reproduce** las recetas y aplica las ediciones. Tu sistema se replica desde su historia, no desde una declaración. ## 6. Interfaz ```rust // hammerd / hammer-core pub struct Journal { path: PathBuf } impl Journal { pub fn record(&self, ev: MutationEvent) -> Result<()>; pub fn since(&self, base: &BaseRef) -> Result>; pub fn export_swm(&self, base: &BaseRef) -> Result<(Swm, Vec)>; } ``` CLI: `hammer journal` (ver/seguir el diario) · `hammer export [--base ] > my.swm`.