qorpa: nombre adoptado y el preflight que mide si una máquina puede alojar

ADR 0015 pasa de "propuesta de nombre" a `qorpa` adoptado: la frontera es
`hammer qorpa {…}` y el espacio de nombres se unifica en
/var/lib/hammer/qorpa/{imagenes,instancias}/ — un solo árbol, para que la poda
de §NO-resuelve 5 tenga un único sitio que barrer. La clase de nodo del grafo
sigue siendo `ajeno`: describe la procedencia, no el subsistema.

Y arranca el §Orden de trabajo 1 (subuid) como GUARDIÁN en vez de a mano:
scripts/qorpa/qorpa-preflight.sh mide las cinco capacidades de entorno que un
huésped necesita y que no están en ningún grafo — userns sin privilegios (+
anidado), mapeo multi-id, overlayfs sin root, los nodos del borde y disco.
Tres niveles (BLOQUEA/LIMITA/NOTA) y salida 0/1/2, porque "arranca pero sin
dnf" es una respuesta legítima, no un error.

Medido en `momento` (exit 2, 2 limitaciones):
- userns ANIDADO funciona ⇒ el "verificar, no asumir" de D6 (pressure-vessel
  creando su userns dentro del nuestro) queda verificado a nivel de primitiva.
- subuid es papel mojado acá: el rango está declarado en /etc/subuid y las
  herramientas están, pero newuidmap/newgidmap vienen sin setuid y sin
  capability ⇒ no pueden escribir el uid_map. Es exactamente la "primera cosa
  que va a fallar" del ADR, y resulta ser de PROVISIÓN, no de kernel.

El guardián ya se corrigió a sí mismo una vez: marcaba LIMITA por
CONFIG_OVERLAY_FS=m mientras tres secciones más abajo el overlay montaba de
verdad. Manda la prueba funcional, no la declarada.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5cQtQrYNjWJVXVE6aEpQ2
This commit is contained in:
Sergio
2026-09-03 03:05:12 +00:00
co-authored by Claude Opus 5
parent 2fc30c4df2
commit aa5bb67fd7
2 changed files with 236 additions and 16 deletions
+21 -16
View File
@@ -1,9 +1,10 @@
# ADR 0015 — Imágenes ajenas: el mundo glibc entra enjaulado, y no entra al store
# ADR 0015 — qorpa: el mundo glibc entra enjaulado, y no entra al store
- **Estado:** PROPUESTO — sin implementar. Este documento decide la frontera; el código viene después.
- **Estado:** PROPUESTO — nombre ADOPTADO (`qorpa`, 2026-09-03); implementación EN CURSO por el
§Orden de trabajo. Este documento decide la frontera; el código viene después.
- **Fecha:** 2026-09-03
- **Frontera (a crear):** `hammer ajenas {traer,crear,correr,exportar,listar,podar}`,
`/var/lib/hammer/ajenas/<sha256>/`, `/var/lib/hammer/instancias/<id>/`,
- **Frontera (a crear):** `hammer qorpa {traer,crear,correr,exportar,listar,podar}`,
`/var/lib/hammer/qorpa/imagenes/<sha256>/`, `/var/lib/hammer/qorpa/instancias/<id>/`,
`instancia.toml` (manifiesto), clase de nodo `ajeno` en `build-state.py`.
- **Continúa:** [SDD 04](../04-overlay.md) (overlay), [SDD 16](../16-harkaq-jaula.md) (harkaq),
[ADR 0004](0004-no-custom-nix.md) (hammer no usa nix), [SDD 20](../20-catalogo-publicable-y-completa.md)
@@ -88,8 +89,8 @@ todo fue bien»).
Espacio de nombres paralelo, direccionado por digest, **fuera de `hash_inputs` de todo**:
```
/var/lib/hammer/ajenas/<sha256>/ imagen — INMUTABLE, verificada por digest
/var/lib/hammer/instancias/<id>/
/var/lib/hammer/qorpa/imagenes/<sha256>/ imagen — INMUTABLE, verificada por digest
/var/lib/hammer/qorpa/instancias/<id>/
├── instancia.toml manifiesto — LA VERDAD
├── upper/ capa mutable — CACHÉ
└── work/ overlayfs
@@ -127,7 +128,7 @@ Si el `upper` con 200 paquetes instalados a mano fuese el activo, tendríamos un
justo lo que hammer existe para no tener. La relación correcta es la misma que receta↔artefacto:
```toml
# /var/lib/hammer/instancias/juegos/instancia.toml
# /var/lib/hammer/qorpa/instancias/juegos/instancia.toml
base = "sha256:…" # digest de la imagen, inmutable
distro = "fedora-43" # informativo
paquetes = ["steam", "mesa-dri-drivers", "mesa-vulkan-drivers"]
@@ -144,11 +145,11 @@ apps = ["steam.desktop"]
```
Consecuencias que se caen solas:
- **`hammer ajenas recrear <id>`** reconstruye la instancia desde el manifiesto. El `upper` es
- **`hammer qorpa recrear <id>`** reconstruye la instancia desde el manifiesto. El `upper` es
descartable.
- **Actualizar la base** (Fedora 43 → 44) no es un rebase riesgoso: se cambia el digest y se recrea.
- **El respaldo** es el manifiesto (KB), no el `upper` (GB). `respaldo-storagebox.sh` no toca
`/var/lib/hammer/instancias/*/upper` y eso es correcto, no un olvido.
`/var/lib/hammer/qorpa/instancias/*/upper` y eso es correcto, no un olvido.
- La poda tiene una regla trivial: **un `upper` siempre se puede borrar.**
### D4 — Granularidad: cuatro tipos, no uno
@@ -306,17 +307,21 @@ Se escriben acá para que no se descubran en producción.
1. **Provisionar subuid** y probar `dnf install` en una instancia mínima. Es lo primero que falla
(§NO-resuelve 2) y define si el resto es fácil o difícil.
2. `hammer ajenas traer <url> --sha256` + verificación de digest. Reusa el patrón de `lab-image.sh`.
2. `hammer qorpa traer <url> --sha256` + verificación de digest. Reusa el patrón de `lab-image.sh`.
3. Instancia = overlay sobre la imagen + `instancia.toml`; `crear` / `recrear` / `correr`.
4. Concesiones → `PolicySpec` de harkaq. Empezar por **nada** y abrir sólo lo declarado.
5. Shims + `.desktop` generados (`exportar`), y clase `ajeno` en `build-state.py`.
6. **Steam de punta a punta**, con verificación explícita del bwrap anidado.
7. Poda (`hammer ajenas podar`) y renglón en SDD 20 sobre licencias.
7. Poda (`hammer qorpa podar`) y renglón en SDD 20 sobre licencias.
8. Proxy filtrante de Wayland — ticket propio, el más valioso de la lista.
## Nombre
## Nombre — ADOPTADO 2026-09-03
El subsistema pide un nombre de la familia del repo (harkaq, yupana, arje, wawafs, kikin, churay,
mirada, minga, khipu). Propuesta: **`qorpa`** — *huésped alojado*: alguien que entra a la casa, se
le dan habitaciones concretas y no las demás. Encaja con lo que hace y con cómo se comporta. Queda a
criterio del autor del repo; el resto del ADR no depende del nombre.
El subsistema se llama **`qorpa`** — *huésped alojado*: alguien que entra a la casa, se le dan
habitaciones concretas y no las demás. Elegido por el autor del repo; entra en la familia (harkaq,
yupana, arje, wawafs, kikin, churay, mirada, minga).
Consecuencias de nombre, ya aplicadas arriba: la frontera es `hammer qorpa {…}`, el espacio de
nombres es `/var/lib/hammer/qorpa/{imagenes,instancias}/` —uno solo, para que la poda de
§NO-resuelve 5 tenga un único árbol que barrer— y el adjetivo del grafo sigue siendo `ajeno`
(clase de nodo), porque describe la **procedencia** del nodo, no el subsistema que lo aloja.