hammer-upgrade: upgrades atómicos con generaciones y rollback (Etapa E4, primer corte)

Crate hammer-upgrade + CLI 'hammer upgrade apply|rollback|status'. Aplica un
árbol Stage1/producto del store sobre el FHS vivo dejando una generación
(manifest + backup de bytes previos), atómico fichero-a-fichero con 'current'
como commit-point. Rollback restaura EXACTO al árbol anterior (o pre-upgrades).
Cada cambio al journal (HammerHydrate, replay-able). Verificación opcional del
of_tree esperado (índice de mirror E3 / release firmada). Maneja ficheros +
symlinks. 7 tests unitarios + scripts/upgrade-e2e-test.sh (apply v1->v2->
rollback->rollback contra el binario real). SDD 13 actualizado.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-20 21:46:14 -04:00
co-authored by Claude Opus 4.8
parent 30daab35e6
commit d23946fa3d
8 changed files with 953 additions and 2 deletions
+19 -2
View File
@@ -63,7 +63,24 @@ kernel en `/boot`, los módulos GRUB en `/boot/grub/i386-pc` y los pasos `grub-m
Endurecimiento pendiente: anclar el `of_tree` a una raíz firmada (log de transparencia `bootstrap.json` /
atestación de Etapa D) para defenderse de un origen plenamente malicioso (hoy el invariante forzado es
"el contenido recibido hashea a lo que el índice anuncia", que ataja corrupción de transporte).
- **E4 — upgrades:** aplicar un nuevo árbol Stage 1 sin reinstalar — atómico vía el modelo overlay +
el journal (D), con rollback al árbol anterior.
- **E4 — upgrades: ✅ primer corte** (crate `hammer-upgrade` + CLI `hammer upgrade apply|rollback|status`).
Aplica un árbol Stage1/producto **nuevo** (artefacto sellado del store, CAS BLAKE3) sobre el FHS vivo
sin reinstalar, dejando una **generación**, y revierte exactamente al árbol anterior con `rollback`.
**Modelo de generaciones** (inspirado en NixOS, in-place al FHS porque el boot lee el root directo, no
un symlink indirecto): cada generación graba bajo `<state>/generations/<N>/` su `manifest.json`
(tree_dir del store, `of_tree`, parent, lista de ficheros, cambios) y un `backup/` con los bytes
**previos** de todo path que pisó o retiró. **Atomicidad:** proyección fichero-a-fichero (escritura a
temporal + `rename` ⇒ cada fichero conmuta atómicamente), con `current` (id de la generación viva) como
commit-point; un corte a mitad deja `current` en la vieja y los backups intactos ⇒ re-apply/rollback
recuperan. **Diff vs el árbol previo:** el apply añade/pisa los ficheros del árbol nuevo y **retira** los
que la generación viva aportaba y el nuevo no tiene. **Rollback:** deshace en orden inverso (borra lo
añadido, restaura desde `backup/` lo pisado/retirado) y retrocede `current` al padre — restauración
*exacta*, incluso al estado pre-upgrades. Cada cambio se registra en el [journal](05-journal.md) como
`HammerHydrate{artifact=tree_dir}` (replay-able). Verificación opcional de integridad: si el llamador
anuncia el `of_tree` esperado (de un índice de **mirror E3** o release firmada), el apply lo exige antes
de tocar el root. Maneja ficheros regulares + symlinks; valida E2E en host con `scripts/upgrade-e2e-test.sh`
(apply v1→v2→rollback→rollback contra el binario real). **Endurecimiento pendiente:** journal de
*intención* + replay para sobrevivir un kernel-panic a media escritura; GC de generaciones viejas
(prune); cableado del init de boot para que el rollback sea seleccionable al arranque.
- **E5 — ISO/medio de arranque:** medio live para correr el instalador (xorriso/grub-mkrescue;
herramental a construir).