X Linux
Menú de documentación

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

RutaContenido
/.snapshots/<id>Snapshot escribible de la raíz viva (restore point booteable)
/var/lib/x/generations/<id>/manifest.jsonProcedencia: motivo, padre, tooling, kernel, cmdline, hashes, root_subvol
/var/lib/x/generations/<id>/packages.tsvCaptura de pacman -Q (o xpm query si xpm es el único gestor)
/var/lib/x/generations/<id>/services.txtUnidades systemd habilitadas
/var/lib/x/generations/<id>/migrations.txtMarcadores 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.uuidUUID btrfs del snapshot
/var/lib/x/generations/<id>/pinnedMarca: nunca podar su entry de boot
/var/lib/x/currentGeneración que bootea por defecto (la seleccionada)
/var/lib/x/pendingLo 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

ComandoDescripción
x gen / x gen listLista 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 bootRegenera 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] / statusHabilita 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ás x.conf espejando la default y una entry x-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/modules congelado.
  • Multi-kernel: una entry por kernel instalado, detectado desde /usr/lib/modules/<release>/pkgbase (linux, linux-lts, ...). El kernel primario (manifiesto kernel.release) conserva el id x-gen-<id>; el resto usa x-gen-<id>-<pkgbase>. Los kernels congelados se archivan por pkgbase en /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_KEEP generaciones más la default, la running y las pinned; podar el ESP nunca borra el snapshot btrfs ni los metadatos, así que cualquier generación se puede volver a seleccionar (rollback recrea su entry y su copia de kernel a demanda).
  • x gen rollback <id> crea una generación de seguridad pre-rollback, marca la objetivo como pinned, actualiza current/pending y reescribe los defaults del menú. /home no se toca.
  • x gen status distingue la generación running (parseada del cmdline rootflags=subvol=...), la default (próximo boot) y un rollback pending; con --json emite 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) y x gen import lo verifica, abortando si no coincide; los bundles sin manifiesto (formato viejo) importan con un warning.
  • Al restaurar un stream btrfs send se 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):

ComandoDescripción
x home / x home listLista 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 update crea una generación de seguridad pre-update, corre pacman -Syu + migraciones y registra una segunda generación (reason: update). Si pacman falla, la de seguridad queda para recuperar.

  • Durante la instalación, x setup corre con X_GEN_SKIP=1; el instalador crea la generación 0001 (reason: install, subvol vivo /@) después del bootloader, con X_GEN_LIVE_SUBVOL=/@ y X_GEN_SUBVOL_PREFIX=/@snapshots.

  • El paquete x-scripts instala dos hooks de pacman:

    HookCuándoEfecto
    /etc/pacman.d/hooks/10-x-gen-pre.hookPreTransactionGeneración de seguridad (reason: pacman-pre)
    /etc/pacman.d/hooks/20-x-gen-post.hookPostTransactionRegistra 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 cuando X_GEN_SKIP=1 — justo lo que x update setea en su propio pacman -Syu para 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] explicit instala los faltantes (pacman -S --needed); los instalados fuera de la declaración se reportan pero se conservan salvo prune = true.
  • [services] enable habilita las units faltantes.
  • [theme] name se aplica vía x theme set como el usuario que invoca.
  • apply soporta --dry-run y respeta X_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_supported es falso: cada hook es un no-op y la CLI reporta que las generaciones no están disponibles.

Variables de entorno

VariableDefaultSignificado
X_GEN_BACKENDautoauto detecta btrfs; btrfs, dir (tests/degradado), off
X_GEN_STATE/var/lib/xRaíz de estado (subvol @xstate en instalaciones)
X_GEN_DIR$X_GEN_STATE/generationsStore de manifiestos
X_GEN_CURRENT$X_GEN_STATE/currentArchivo con la generación default
X_GEN_SNAPSHOTS/.snapshotsStore de snapshots (punto de montaje)
X_GEN_SUBVOL_PREFIXautoRuta 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/cmdlineCmdline registrado en el manifiesto
X_GEN_BOOTautoon/off/auto (auto: activo con btrfs)
X_GEN_BOOT_DIR/bootRuta del ESP con kernels y entries
X_GEN_BOOT_KEEP3Generaciones retenidas en el menú de boot
X_GEN_KEEP5Generaciones retenidas por x gen prune
X_GEN_LIVE_SUBVOL—root_subvol de la generación viva (instalador: /@)
X_GEN_RUNNINGdel cmdlineId de la generación running (tests)
X_GEN_SKIP01 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.

Editar esta página en GitHub