645 líneas. Los ADR entran porque en este repo SON documentos vivos, no registros inmutables: el 0013 tiene 5 commits, el 0009 dos. Eso se comprobó antes de decidir, no se asumió por convención general. EXCLUIDOS por ser REGISTRO o generado: docs/evidencia/ (6), el HANDOFF de la noche de KDE (1) y docs/state/ (24, se regenera solo). Reescribir un comando dentro de una evidencia la falsifica. Y el ADR 0016 se excluye de todo barrido, con un aviso adentro para el próximo que barra: habla SOBRE el renombre, así que necesita seguir diciendo 'hammer'. El barrido se lo llevó puesto y lo dejó titulado 'Renombre del sistema: takana → takana'; revertido. Congelados, verificados uno por uno con controles: /opt/hammer, /var/lib/hammer, /usr/bin/hammer, /mnt/vvv/hammer, la URL de gitea, hammer-farm.service, hammer-live-install.sh, BRIEFING-hammer.md, hammerd, hammer-recover y HAMMER_LIVE.
87 lines
4.1 KiB
Markdown
87 lines
4.1 KiB
Markdown
# SDD 05 — Diario de mutaciones
|
|
|
|
Este es el insight central de `takana`: **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 `takana`, 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
|
|
`takana`. 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`
|
|
|
|
`takana 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. `takana` 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 / takana-core
|
|
pub struct Journal { path: PathBuf }
|
|
impl Journal {
|
|
pub fn record(&self, ev: MutationEvent) -> Result<()>;
|
|
pub fn since(&self, base: &BaseRef) -> Result<Vec<MutationEvent>>;
|
|
pub fn export_swm(&self, base: &BaseRef) -> Result<(Swm, Vec<OpaqueWarning>)>;
|
|
}
|
|
```
|
|
|
|
CLI: `takana journal` (ver/seguir el diario) · `takana export [--base <ref>] > my.swm`.
|