Resumen/Scripts y CLI
x — generaciones
Las generaciones son la capa de versionado del sistema X Linux: cada cambio
relevante produce un snapshot booteable del árbol raíz más un manifiesto
con el estado del sistema. El comportamiento es estilo NixOS (generaciones
numeradas, rollback, restore granular) sin store content-addressed: los
snapshots son subvolúmenes btrfs, el menú de arranque los lista y
x gen rollback cambia el default. No hay git involucrado.
Los sistemas instalados por el instalador de texto usan el layout btrfs
(@, @home, @snapshots, @xstate) y la primera generación (0001) se
crea al final de la instalación (ver layout.md y
provisioning.md).
Qué contiene una generación
| Ruta | Contenido |
|---|---|
/.snapshots/<id> | Snapshot escribible de la raíz viva (restore point booteable) |
/var/lib/x/generations/<id>/manifest.json | Procedencia: motivo, padre, tooling, kernel, cmdline, hashes, root_subvol |
/var/lib/x/generations/<id>/packages.tsv | Captura de pacman -Q (o xpm query si xpm es el único gestor) |
/var/lib/x/generations/<id>/services.txt | Unidades systemd habilitadas |
/var/lib/x/generations/<id>/migrations.txt | Marcadores de migración aplicados (usuario<TAB>marcador) |
/var/lib/x/generations/<id>/boot/ | Kernel/initramfs archivados y kernels.tsv (pkgbase/release/vmlinuz/initramfs) |
/var/lib/x/generations/<id>/snapshot.uuid | UUID btrfs del snapshot |
/var/lib/x/generations/<id>/pinned | Marca: nunca podar su entry de boot |
/var/lib/x/current | Generación que bootea por defecto (la seleccionada) |
/var/lib/x/pending | Lo escribe el rollback: objetivo a bootear en el próximo reinicio |
El manifiesto (manifest.json, schema 2) registra la procedencia y el estado
capturado: reason, generación padre, versión del tooling, kernel primario,
cmdline, hashes y root_subvol. Las capturas (packages.tsv, services.txt,
migrations.txt, boot/) son archivos aparte; el manifiesto hashea /etc
(configs.etc_sha256) y x gen status reporta el drift.
Layout y estado
@ -> / (raíz viva; la primera generación, subvol vivo /@)
@home -> /home (datos de usuario; el rollback no los toca)
@snapshots -> /.snapshots (snapshots de generación, modo 0700)
@xstate -> /var/lib/x (metadatos compartidos, modo 0700)
/var/lib/x es su propio subvolumen: los metadatos los ven todas las
generaciones (los snapshots no los capturan). /tmp va en tmpfs para que los
snapshots no capturen archivos transitorios.
/var/lib/x y /.snapshots son 0700 root: los comandos de lectura
(list, status, diff, ...) imprimen un error claro de "re-ejecutá con
sudo" en vez de una lista vacía cuando se corren sin privilegios.
Comandos
| Comando | Descripción |
|---|---|
x gen / x gen list | Lista las generaciones; * marca la default (actual) |
x gen new [--reason R] [--label L] | Registra una generación (snapshot + manifiesto + entry de boot) |
x gen status [--json] | Backend, running vs default, rollback pendiente y drift de /etc |
x gen rollback <id> [--no-safety] | Cambia el default boot a una generación (aplica al reiniciar) |
x gen boot | Regenera las entries de boot por generación |
x gen diff <a> <b> | Diferencias de paquetes/servicios/migraciones/kernel//etc entre dos generaciones |
x gen verify [id] | Compara el sistema vivo contra una generación (exit 1 con drift) |
x gen pin <id> [--unpin] | Protege una generación de x gen prune |
x gen prune [--keep N] [--older-than DAYS] [--dry-run] | Elimina generaciones viejas (pinned, running y default siempre quedan) |
x gen restore <path> [--from ID] [--dest PATH] | Restaura un archivo o directorio desde un snapshot |
x gen restore --pkg <name> [--from ID] [--dest ROOT] | Restaura todos los archivos de un paquete (db pacman/xpm del snapshot) |
x gen export <id> [--out FILE] [--with-data] | Empaqueta una generación como bundle portable |
x gen import <file> [--force] | Importa un bundle a $X_GEN_STATE (--force reemplaza) |
x gen plan <system.toml> | Imprime las acciones para cumplir una declaración declarativa |
x gen apply <system.toml> [--dry-run] | Aplica la declaración y registra una generación |
x gen quota init [--limit SIZE] / status | Habilita quotas btrfs y muestra uso / tabla de qgroups |
sudo x gen new --reason manual --label "antes de tocar"
x gen list
x gen status
x gen diff 0001 0003
sudo x gen rollback 0002 # bootea la generación 0002 al reiniciar
sudo x gen restore /etc/sddm.conf --from 0002
sudo x gen restore --pkg kitty --from 0002
sudo x gen pin 0002 # nunca podar
sudo x gen prune --keep 5 --dry-run
x gen new registra estado; no cambia el default boot (para eso está
x gen rollback). x gen diff compara packages.tsv (versiones
agregadas/eliminadas/actualizadas), services.txt, migrations.txt, el
kernel y el hash de /etc; x gen verify corre la misma comparación contra
el sistema vivo. x gen prune borra metadatos, snapshot y entry de boot
de las generaciones fuera de la ventana, pero siempre conserva pinned,
running y default; si elimina el objetivo pending limpia el marcador.
--older-than DAYS protege además las generaciones recientes.
Rollback y entries de arranque
- Una entry por generación retenida en el menú (GRUB:
custom.cfg; systemd-boot:loader/entries/x-gen-<id>.conf), másx.confespejando la default y una entryx-rescue(mismo kernel,systemd.unit=rescue.target). - La generación running bootea el kernel vivo del ESP
(
/vmlinuz-linux) porque su raíz muta in-place; las generaciones congeladas bootean su copia archivada (/boot/x/gen-<id>/...), que coincide con su/usr/lib/modulescongelado. - Multi-kernel: una entry por kernel instalado, detectado desde
/usr/lib/modules/<release>/pkgbase(linux,linux-lts, ...). El kernel primario (manifiestokernel.release) conserva el idx-gen-<id>; el resto usax-gen-<id>-<pkgbase>. Los kernels congelados se archivan porpkgbaseen/boot/x/gen-<id>/<pkgbase>/; las generaciones legacy de un solo kernel mantienen el layout viejo y emparejan vmlinuz/initramfs por sufijo. - El ESP conserva las últimas
X_GEN_BOOT_KEEPgeneraciones más la default, la running y laspinned; podar el ESP nunca borra el snapshot btrfs ni los metadatos, así que cualquier generación se puede volver a seleccionar (rollbackrecrea su entry y su copia de kernel a demanda). x gen rollback <id>crea una generación de seguridad pre-rollback, marca la objetivo comopinned, actualizacurrent/pendingy reescribe los defaults del menú./homeno se toca.x gen statusdistingue la generación running (parseada del cmdlinerootflags=subvol=...), la default (próximo boot) y un rollback pending; con--jsonemite un objeto con backend, ids, snapshot, entries y drift.
Restore granular
x gen restore recupera una ruta o un directorio desde el snapshot de una
generación (--from, por defecto la actual) y restore --pkg restaura todos
los archivos de un paquete usando la base de datos de pacman (o xpm) que
viaja en el snapshot. El restore nunca pisa en silencio: un archivo que
difiere se mueve a <archivo>.bak.<ts> antes de copiar la versión del
snapshot (mismo contrato que x_sync_config en install/helpers/sync.sh).
Las rutas se validan: se rechazan componentes .. y las listas de archivos
de paquete con rutas inseguras se saltean con un warning.
Export e import
x gen export <id> empaqueta los metadatos de la generación (manifiesto,
capturas, migraciones, kernel archivado) como tar.zst (o tar.gz sin
zstd). --with-data agrega el snapshot: btrfs send en btrfs (root) o una
copia del árbol con backend dir.
- Los bundles llevan
BUNDLE.sha256(hash de cada archivo) yx gen importlo verifica, abortando si no coincide; los bundles sin manifiesto (formato viejo) importan con un warning. - Al restaurar un stream
btrfs sendse bifurca el subvolumen recibido para que la generación quede escribible. - La generación importada no se selecciona automáticamente: usá
x gen rollback <id>después. Los duplicados fallan salvo--force.
Es la vía de portabilidad: mover una generación entre máquinas o respaldarla
sin saber de btrfs send/receive.
Generaciones de home
Las generaciones de sistema cubren el subvolumen raíz; /home queda afuera a
propósito. Una segunda capa, propiedad del usuario, versiona los dotfiles con
copias de archivos (sin root, sin btrfs, funciona en WSL):
| Comando | Descripción |
|---|---|
x home / x home list | Lista las generaciones de home (* marca la actual) |
x home new [--label L] | Registra una copia de los dotfiles incluidos |
x home status [--json] | Generación actual y drift |
x home diff <a> <b> | Diferencias por archivo (agregado/eliminado/cambiado) |
x home restore <path> [--from ID] [--dest PATH] | Restaura un dotfile (backup .bak.<ts>) |
x home prune [--keep N] [--dry-run] | Elimina generaciones viejas (actual y pinned quedan) |
Store: ~/.local/share/x/home-gens/<id>/ con manifest.json, files/
(copia) y files.sha256 (listado por archivo). Incluye por defecto
.bashrc, .bash_profile, .profile, .zshrc, .zshenv, .gitconfig y
.config; se saltean los directorios Cache, CachedData, GPUCache y
logs en cualquier parte del árbol. Las rutas se validan para que un restore
nunca escape del home.
Las generaciones de home también se registran automáticamente (best effort,
nunca bloquean el aprovisionamiento): x setup --user registra pre-setup
antes de sembrar/sincronizar dotfiles, y x update registra pre-update
antes de las migraciones. X_HGEN_SKIP=1 desactiva las capturas automáticas.
Integración con el aprovisionamiento
-
x setup(fases de sistema) termina con una generación (reason: setup). -
x updatecrea una generación de seguridad pre-update, correpacman -Syu+ migraciones y registra una segunda generación (reason: update). Si pacman falla, la de seguridad queda para recuperar. -
Durante la instalación,
x setupcorre conX_GEN_SKIP=1; el instalador crea la generación0001(reason: install, subvol vivo/@) después del bootloader, conX_GEN_LIVE_SUBVOL=/@yX_GEN_SUBVOL_PREFIX=/@snapshots. -
El paquete
x-scriptsinstala dos hooks de pacman:Hook Cuándo Efecto /etc/pacman.d/hooks/10-x-gen-pre.hookPreTransaction Generación de seguridad ( reason: pacman-pre)/etc/pacman.d/hooks/20-x-gen-post.hookPostTransaction Registra el resultado ( reason: pacman)Ambos llaman a
/usr/share/x/hooks/pacman-gen.sh, que es no-op cuando todavía no hay generación actual (instalador/pacstrap), en sistemas no-btrfs o cuandoX_GEN_SKIP=1— justo lo quex updatesetea en su propiopacman -Syupara manejar él mismo sus generaciones pre/post. Toda transacción manual de pacman queda así capturada.
Sistema declarativo (system.toml)
x gen plan imprime las acciones para reconciliar el sistema con un
system.toml; x gen apply las ejecuta y registra una generación
(reason: apply):
[system]
hostname = "x"
timezone = "UTC"
locale = "en_US.UTF-8"
[packages]
explicit = ["kitty", "neovim"]
# prune = true # elimina los paquetes instalados fuera de la lista
[services]
enable = ["NetworkManager"]
[theme]
name = "x-dark"
[system]hostname/timezone/locale se setean solo si difieren.[packages] explicitinstala los faltantes (pacman -S --needed); los instalados fuera de la declaración se reportan pero se conservan salvoprune = true.[services] enablehabilita las units faltantes.[theme] namese aplica víax theme setcomo el usuario que invoca.applysoporta--dry-runy respetaX_DRY_RUN=1; las acciones de sistema necesitan root/sudo.
Límite de espacio (qgroups de btrfs)
x gen quota init [--limit SIZE] habilita quotas btrfs en el subvolumen de
snapshots y fija un límite exclusivo (btrfs qgroup limit -e), así el
presupuesto solo cuenta los datos propios de los snapshots (X_GEN_QGROUP
define el default). x gen quota status muestra el uso y la tabla de
qgroups. El límite lo aplica btrfs, así que conviene combinarlo con retención
(x gen prune --keep N) y dejar margen; los scripts nunca fijan un límite
solos.
Límites y estado
- Elegir generación desde el menú de arranque (el rollback es un comando,
como
nixos-rebuild --rollback), UKIs con Secure Boot y snapshots de home por btrfs siguen fuera de alcance. - En un sistema no-btrfs (o WSL)
xgen_supportedes falso: cada hook es un no-op y la CLI reporta que las generaciones no están disponibles.
Variables de entorno
| Variable | Default | Significado |
|---|---|---|
X_GEN_BACKEND | auto | auto detecta btrfs; btrfs, dir (tests/degradado), off |
X_GEN_STATE | /var/lib/x | Raíz de estado (subvol @xstate en instalaciones) |
X_GEN_DIR | $X_GEN_STATE/generations | Store de manifiestos |
X_GEN_CURRENT | $X_GEN_STATE/current | Archivo con la generación default |
X_GEN_SNAPSHOTS | /.snapshots | Store de snapshots (punto de montaje) |
X_GEN_SUBVOL_PREFIX | auto | Ruta in-fs del store (opción subvol=); se deriva del montaje (/@snapshots) |
X_GEN_ROOT | / | Árbol a snapshotear (los tests usan una raíz falsa) |
X_GEN_CMDLINE | /proc/cmdline | Cmdline registrado en el manifiesto |
X_GEN_BOOT | auto | on/off/auto (auto: activo con btrfs) |
X_GEN_BOOT_DIR | /boot | Ruta del ESP con kernels y entries |
X_GEN_BOOT_KEEP | 3 | Generaciones retenidas en el menú de boot |
X_GEN_KEEP | 5 | Generaciones retenidas por x gen prune |
X_GEN_LIVE_SUBVOL | — | root_subvol de la generación viva (instalador: /@) |
X_GEN_RUNNING | del cmdline | Id de la generación running (tests) |
X_GEN_SKIP | 0 | 1 desactiva las generaciones automáticas en los hooks |
Las variables de home (X_HGEN_STATE, X_HGEN_HOME, X_HGEN_INCLUDE,
X_HGEN_EXCLUDE, X_HGEN_KEEP, X_HGEN_SKIP) están en la tabla de
cli.md. El detalle de empaquetado del motor y de la aserción de
payload está en packaging.md.