Files
hammer/docs/04-overlay.md
T
SergioandClaude Opus 4.8 fe9a2d50cf Fase 2: anidación real de overlays con guard LIFO
Un `try` sobre un target ya cubierto apila un overlay nuevo: el kernel toma el
merged view de la capa inferior como lowerdir. Para que esto sea seguro y no un
footgun:

- fresh_id añade un `seq` atómico por proceso (`<ts>-<pid>-<seq>` zero-padded),
  eliminando la colisión de ids cuando un orquestador apila varios overlays en
  el mismo segundo — el caso real de la anidación.
- stack_key define un orden de apilamiento total y determinista (created_at,
  desempatado por id).
- blocking_overlays detecta capas más jóvenes que solapan targets (igualdad o
  ancestro de path). commit/discard fallan con Error::Shadowed si existen,
  exigiendo resolver LIFO de arriba hacia abajo — antes se desmontaba la capa
  equivocada del target compartido en silencio.

Tests: 6 unit del guard (disjuntos / solape / ancestro / desempate / commit y
discard rechazados) sin privilegios; e2e overlay_nested_stack_inside_userns
prueba el stack real (capa 2 ve la 1, rechazo LIFO, fusión arriba→abajo),
gated en HAMMER_OVERLAY_TESTS. Docs 04 y roadmap actualizados.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 20:39:32 +00:00

4.2 KiB

SDD 04 — Overlay de experimentación

El overlay es la red de seguridad que convierte la mutabilidad total en algo que también es seguro: la capacidad de cometer errores sin perder el control imperativo. Es donde la IA prueba sus cambios antes de tocar el sistema real, y donde tú experimentas a mano sin miedo.

1. El mecanismo

Usa overlayfs nativo del kernel, gestionado dinámicamente desde la terminal:

                 montaje overlay sobre /bin (y /etc, /lib…)
  upperdir  →  /var/lib/hammer/overlays/<id>/upper   (tmpfs o dir oculto, MUTABLE)
  lowerdir  →  el /bin real                          (read-only de facto bajo el overlay)
  merged    →  /bin                                  (lo que ve el sistema)

Mientras el overlay está montado, el sistema y tú veis /bin como el sistema real modificado en caliente. Pero toda escritura cae en el upperdir; el /bin real (lowerdir) nunca se toca.

2. El ciclo de vida

hammer try            # monta un overlay sobre los directorios del sistema
   ... experimentas / la IA inyecta binarios y edita configs ...
   ... pruebas en caliente: ejecutas, rompes, observas ...
hammer commit         # fusiona upperdir → FHS real (rsync inteligente) y registra en el diario
   — o —
hammer discard        # desmonta y borra el upperdir: el sistema vuelve al estado base, instantáneo
  • try → estado base limpio + capa mutable encima. Riesgo cero.
  • discard → desmonta el overlay; el sistema vuelve a su base instantáneamente. No hay que deshacer cambios uno por uno.
  • commit → fusiona la capa al FHS real y anota cada archivo tocado en el diario (SDD 05). A partir de aquí el cambio es permanente y rastreable.

3. Por qué overlay y no contenedor

Un contenedor (docker/chroot) te aísla de tus dispositivos y procesos reales — pierdes el acceso al sistema vivo. El overlay hace lo contrario: modificas la "realidad" del sistema operativo (mismos PIDs, mismos /dev, misma red) pero con una red de seguridad. Es la diferencia entre "probar en una maqueta" y "probar en la casa real con un seguro de vida".

4. Composición con el resto del sistema

  • La IA (ver SDD 08) opera siempre dentro de un overlay: clona el repo, compila en el lab, hidrata en el upperdir, edita configs en el upper. Tú validas en caliente. La IA nunca escribe en el FHS real hasta que tú haces commit.
  • Múltiples overlays pueden coexistir (distintos <id>), p. ej. un experimento por hipótesis. Cada uno es independiente.
  • El diario sólo registra en commit — el ruido del experimento no contamina la historia.

5. Estados y reglas

Estado Significado Transiciones
none sin overlay activo tryactive
active overlay montado, capa mutable encima commitnone (+diario), discardnone

Reglas:

  • commit/discard sin overlay activo: no-op con aviso.
  • Un try anidado sobre un target ya cubierto crea un overlay nuevo apilado: el kernel toma el merged view de la capa de abajo como lowerdir de la nueva. El orden de apilamiento es total y determinista (created_at, desempatado por id, que ahora lleva un seq por proceso para que ráfagas en el mismo segundo no colisionen). commit/discard exigen LIFO: si una capa más joven aún sombrea alguno de tus targets, la operación falla con Error::Shadowed listándolas — resuelve primero las de arriba. (La unicidad de orden entre procesos concurrentes apilando sobre el mismo target en el mismo segundo no la garantiza el seq; queda para el track posterior junto al init propio.)
  • Si el sistema se apaga con overlays activos, al arranque hammerd los reporta y deja que el humano decida (no auto-commit, no auto-discard — eso sería declarativo).

6. Interfaz

// hammer-cli (con helpers de hammer-core)
pub fn overlay_try(targets: &[PathBuf]) -> Result<OverlayId>;
pub fn overlay_commit(id: OverlayId, journal: &Journal) -> Result<CommitReport>;
pub fn overlay_discard(id: OverlayId) -> Result<()>;
pub fn overlay_status() -> Result<Vec<OverlayState>>;

CLI: hammer try · hammer commit · hammer discard · hammer status.