feat(arje): reemplaza systemd en máquina real con coexistencia GRUB
Flujo seguro de adopción: arje se instala como entrada GRUB
alternativa, no toca systemd ni /sbin/init. Booteás arje cuando
querés, volvés a systemd si rompe (rollback instantáneo desde el
menú).
Artefactos nuevos:
- scripts/install-arje-as-init.sh: instala binarios musl-static a
/usr/sbin/ y /usr/bin/, copia seed a /ente/seed.card.json, agrega
menuentry "arje" a /etc/grub.d/40_custom usando init=/sbin/ente-zero
con kernel + initrd nativos. NO cambia GRUB_DEFAULT. Idempotente
(regenera el bloque ARJE-MENUENTRY si existe).
- scripts/uninstall-arje.sh: revierte binarios + menuentry. Conserva
/ente/seed.card.json por si la editaste.
- seeds/arje-host.card.json: seed para máquina real con 15 cards:
tmpfiles + mount-fstab + swap-on + dbus-system + 11 compat shims +
dhcpcd + sshd + agetty. Validada.
- docs/arje-replace-systemd.md: filosofía vs systemd ("no acapara
porque no genera, sólo arranca lo declarado"), lista exhaustiva de
servicios systemd que NO deben migrarse (ModemManager, snapd, cups,
unattended-upgrades, etc.), tabla diferencial de UX vs systemd
(systemctl restart → kill PID, systemctl enable → editar seed),
checklist pre-primer-boot, instrucciones de rollback y cómo hacer
arje default sólo cuando estés seguro.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Delta semántico
Ninguna definición cambió: el cambio es de texto, documentación o datos.
4 archivos · +693 −0
- docs/arje-replace-systemd.md +221 −0
- scripts/install-arje-as-init.sh +178 −0
- scripts/uninstall-arje.sh +45 −0
- seeds/arje-host.card.json +249 −0
diff --git a/docs/arje-replace-systemd.md b/docs/arje-replace-systemd.md
new file mode 100644
index 0000000..0fa22c1
--- /dev/null
+++ b/docs/arje-replace-systemd.md
@@ -0,0 +1,221 @@
+# Reemplazar systemd con arje en una máquina real
+
+> **Importante**: este flujo deja **systemd intacto** y agrega arje como
+> entrada GRUB alternativa. No es "destruir y reemplazar"; es "convivir
+> hasta confiar". Booteás arje cuando querés, volvés a systemd si rompe.
+
+## 1. Estrategia
+
+| componente | qué hacemos |
+| --------------------- | -------------------------------------------------------- |
+| `/sbin/init` | NO se toca (sigue siendo systemd). |
+| `/lib/systemd/*` | NO se toca. |
+| `kernel + initramfs` | reusamos los del host (firmware, módulos, root FS). |
+| `init=` en GRUB | nueva entrada apunta a `/sbin/ente-zero`. |
+| `GRUB_DEFAULT` | NO se cambia. Sigue arrancando systemd por default. |
+| `/ente/seed.card.json`| nueva — define qué arranca arje. |
+| `/usr/sbin/ente-*` | binarios musl-static del fractal. |
+
+Booteo del kernel → initramfs nativo hace su trabajo (mountea root,
+carga módulos) → cuando llega el `switch_root`, en lugar de exec
+`/lib/systemd/systemd`, exec `/sbin/ente-zero`. Esto deja firmware y
+udev fuera del scope de arje (todavía).
+
+## 2. Instalación
+
+```bash
+# 1. (opcional) revisá la seed default que se va a copiar a /ente/
+$EDITOR seeds/arje-host.card.json
+
+# 2. instalar
+sudo scripts/install-arje-as-init.sh
+
+# 3. (opcional) bootear arje SÓLO en el próximo reboot (no cambia default)
+sudo grub-reboot "arje (init=/sbin/ente-zero) — kernel $(uname -r)"
+sudo reboot
+```
+
+Si algo rompe en arje: reset/poweroff → en el menú GRUB elegir la
+entrada default (systemd) → volvés a un sistema funcional. `arje` no
+toca configuración de systemd, así que el rollback es instantáneo.
+
+Para **desinstalar**:
+
+```bash
+sudo scripts/uninstall-arje.sh
+```
+
+## 3. Filosofía vs systemd — y por qué arje no acapara
+
+systemd arranca, por default, **todo lo que tiene un `.service` con
+`WantedBy=multi-user.target`**, recursivamente vía dependencias. Eso es
+opaco y crece con cada paquete instalado.
+
+arje no tiene generadores, ni `WantedBy`, ni `Wants`, ni target trees.
+**Arranca exactamente lo declarado en `genesis: [...]` de la
+`seed.card.json`**, en el orden declarativo, con la `supervision` que
+declarás vos. Si no lo declaraste, no corre. Punto.
+
+### 3a. Servicios que systemd arranca y arje NO debería migrar
+
+Estos son los sospechosos típicos en Debian/Ubuntu/Arch. **No agregues
+ninguno a tu seed** salvo que sepas que lo usás:
+
+| systemd unit | por qué saltearlo |
+| ------------------------------------ | ---------------------------------------- |
+| `ModemManager.service` | no tenés módem 3G/4G. |
+| `bluetooth.service` | si no usás bluetooth. |
+| `cups.service`, `cups-browsed` | si no imprimís. |
+| `avahi-daemon.service` | mDNS local. Ruido si no lo necesitás. |
+| `snapd.service` | si no usás snaps. |
+| `apt-daily.service`, `apt-daily-upgrade` | autoupdates — no son init. |
+| `unattended-upgrades.service` | corre tras boot, no init. |
+| `accounts-daemon.service` | UI helpers GNOME; no init. |
+| `colord.service` | calibración de color. No init. |
+| `geoclue.service` | localización. No init. |
+| `gdm.service`, `lightdm.service` | DM gráfico — agregalo SÓLO si querés GUI.|
+| `NetworkManager-wait-online.service` | espera red. arje no necesita "esperar". |
+| `systemd-timesyncd.service` | reemplaza con cron-like + ntpd one-shot. |
+| `systemd-machined.service` | reemplazado por `ente-machined-compat`. |
+| `systemd-logind.service` | reemplazado por `ente-logind-compat`. |
+| `systemd-resolved.service` | reemplazado por `ente-resolved-compat`. |
+| `systemd-hostnamed.service` | reemplazado por `ente-hostnamed-compat`. |
+| `systemd-timedated.service` | reemplazado por `ente-timedated-compat`. |
+| `systemd-localed.service` | reemplazado por `ente-localed-compat`. |
+| `systemd-journald.service` | reemplazado por `ente-journald-compat`. |
+| `systemd-tmpfiles-*.service` | reemplazado por `ente-tmpfiles-compat`. |
+| `systemd-binfmt.service` | reemplazado por `ente-binfmt-compat`. |
+| `polkitd.service` | reemplazado por `ente-polkit-compat`. |
+
+Las últimas 12 ya están como Cards en `seeds/arje-host.card.json`. Las
+de la mitad de arriba **no** — no las agregues.
+
+### 3b. Servicios que SÍ necesitás declarar (y que ya están en `arje-host.card.json`)
+
+| Card label | reemplaza | binario |
+| ------------------ | -------------------------- | -------------------------------------------- |
+| `tmpfiles-boot` | `systemd-tmpfiles-setup` | `/usr/sbin/ente-tmpfiles-compat --boot` |
+| `mount-fstab` | `local-fs.target` | `/bin/mount -a` (one-shot) |
+| `swap-on` | `swap.target` | `/sbin/swapon -a` (one-shot) |
+| `dbus-system` | `dbus.service` | `/usr/bin/dbus-daemon --system --nofork` |
+| `compat-*` (×11) | shims D-Bus de systemd | `/usr/sbin/ente-*-compat` |
+| `network-dhcpcd` | `NetworkManager` / `networkd` | `/usr/sbin/dhcpcd -B` |
+| `sshd` | `ssh.service` | `/usr/sbin/sshd -D` |
+| `getty-tty1` | `getty@tty1.service` | `/sbin/agetty --noclear tty1 linux` |
+
+15 entes. Compará: una instalación Debian fresh con GNOME desktop tiene
+**>180 unidades activas** tras `systemctl list-units --state=running`.
+
+### 3c. Cómo agregar un servicio que falta
+
+1. Encontrá el binario y los argumentos. Mirá el `.service` original:
+ ```
+ systemctl cat <unit>
+ ```
+ Mirá `ExecStart=` y reproducí.
+
+2. Agregá una Card al `genesis` de `/ente/seed.card.json`:
+ ```json
+ {
+ "schema_version": 1,
+ "id": "<ULID nuevo, Crockford base32, no I L O U>",
+ "label": "mi-servicio",
+ "provides": [], "requires": [],
+ "permissions": { "networking": "outbound", "filesystem": "read-write", "ipc": { "allow": [] }, "processes": true },
+ "soma": { ... idéntico al patrón de los otros ... },
+ "payload": { "Native": { "exec": "/usr/sbin/mi-bin", "argv": ["--foreground"], "envp": [] } },
+ "supervision": { "Restart": { "initial": 500, "max": 30000 } },
+ "lifecycle": "daemon",
+ "priority": "normal",
+ "flow": { "input": [], "output": [] },
+ "genesis": []
+ }
+ ```
+ **Importante**: `argv` tiene que tener el equivalente a `--foreground` o
+ `-D` o `--no-fork`. Si el binario se daemoniza solo (fork+exit), arje
+ no lo puede supervisar y caerá en loop de restart.
+
+3. Validar:
+ ```bash
+ seeds/validate.sh /ente/seed.card.json
+ ```
+
+4. Reboot a arje. Verificar:
+ ```sh
+ arje# brahman-status
+ ```
+
+## 4. Diferencias de UX con systemd
+
+| acción | systemd | arje |
+| ---------------------------- | ------------------------------------ | ------------------------------------------------------- |
+| Ver servicios activos | `systemctl list-units --state=running` | `brahman-status` + `busctl list-entes` |
+| Ver logs | `journalctl -u foo` | `ente-journalctl` (TODO en CLI; por ahora `dmesg`) |
+| Restart un servicio | `systemctl restart foo` | matar el PID (`brain` lo reanuda por `Supervision`) |
+| Habilitar/Deshabilitar | `systemctl enable/disable` | editar `/ente/seed.card.json` + reboot |
+| Ver dependencias | `systemctl list-dependencies foo` | `brahman-status` muestra references y flow matches |
+| Detectar fallas reincidentes | `systemctl status foo` → "failed" | `brainctl crystals` muestra patrones `EnteDied → …` |
+| socket activation | `.socket` unit | (no implementado todavía; ver flow + broker matching) |
+| user services | `--user` | (no implementado; arje es system-wide por ahora) |
+
+## 5. Cosas que arje todavía NO hace que sí hace systemd
+
+- **udev / hotplug**: arje captura uevents (vía `spawn_uevent_stream`),
+ pero no hay reglas declarativas todavía. Para boot inicial, el
+ initramfs nativo ya hizo el cold-plug, así que no es bloqueante.
+- **Timers** (cron-like): `ente-timer-compat` existe pero no está en la
+ seed host por default. Agregalo si querés `*.timer` units migradas.
+- **Service generators**: no hay equivalente a `systemd-fstab-generator`
+ ni `systemd-getty-generator`. Tu seed los declara explícitamente.
+- **Socket activation**: parcialmente — el broker matchea flow
+ consumer↔producer, pero no hay aún listen-on-fd-handoff como
+ `sd_listen_fds()`.
+- **User instances**: no hay todavía `arje --user`. Los procesos de
+ usuario los lanza un getty/login estándar.
+
+## 6. Checklist pre-primer-boot
+
+- [ ] `seeds/arje-host.card.json` ajustada a tu hardware/red/users.
+- [ ] `seeds/validate.sh` → OK.
+- [ ] `/usr/bin/dbus-daemon` existe (de lo contrario los shims no se
+ anuncian — `apt install dbus`).
+- [ ] `/usr/sbin/dhcpcd` o tu network stack preferido está instalado.
+- [ ] `/usr/sbin/sshd` instalado y `/etc/ssh/sshd_config` ok si querés
+ poder entrar remoto si la consola falla.
+- [ ] `/sbin/agetty` instalado (`util-linux`) para tty1.
+- [ ] `sudo scripts/install-arje-as-init.sh` ejecutado.
+- [ ] `grub-reboot` apuntado a la entrada arje **sólo para el próximo
+ boot** — no `grub-set-default`.
+- [ ] Listo para revertir vía menú GRUB si algo falla.
+
+## 7. Cuando arje sea estable: hacerlo default
+
+Cuando ya hayas booteado arje N veces sin problemas y querés que sea
+default:
+
+```bash
+# Listar entradas
+sudo grep -E "^\s*menuentry " /boot/grub/grub.cfg | nl
+
+# Ponerlo como default (usa el índice del menú o el title exacto)
+sudo grub-set-default "arje (init=/sbin/ente-zero) — kernel $(uname -r)"
+```
+
+Mantené la entrada systemd como rescue. Si querés acortar la lista de
+units que systemd arrancaría si bootea de nuevo: `systemctl disable
+ModemManager bluetooth cups …` — los nombres de §3a son una guía.
+
+## Apéndice — depurar el primer boot fallido
+
+Si arje no levanta el primer boot:
+
+1. Volver a GRUB → entrada systemd → bootea normal.
+2. `journalctl --boot=-1` (logs del intento previo).
+3. Buscar `ente-zero` o `Tarjeta Semilla`. Si nada → el ejecutable no
+ se lanzó (kernel panic temprano, falta `init=` correcto, etc.).
+4. Si arrancó pero falló alguna Card: la próxima vez, antes de reboot:
+ ```bash
+ sudo sed -i 's/console=tty1/console=tty1 ente.log=trace/' /etc/grub.d/40_custom
+ sudo update-grub
+ ```
+ y revisá `dmesg` desde systemd tras el reintento.
diff --git a/scripts/install-arje-as-init.sh b/scripts/install-arje-as-init.sh
new file mode 100755
index 0000000..b446634
--- /dev/null
+++ b/scripts/install-arje-as-init.sh
@@ -0,0 +1,178 @@
+#!/usr/bin/env bash
+# install-arje-as-init.sh — instala arje como init alternativo en una
+# máquina real con systemd ya instalado, en coexistencia.
+#
+# ESTRATEGIA: arje NO reemplaza al systemd default. Se instala en
+# paralelo con su propia entrada GRUB. Booteás arje cuando querés,
+# volvés a systemd si algo no anda.
+#
+# QUÉ HACE:
+# 1. Compila musl-static todos los binarios del fractal.
+# 2. Copia binarios a /usr/sbin/ (init/shims) y /usr/bin/ (CLIs admin)
+# del rootfs vivo.
+# 3. Copia la seed a /ente/seed.card.json (default: arje-host.card.json).
+# 4. Crea menuentry "arje" en /etc/grub.d/40_custom usando init=/sbin/ente-zero
+# (conserva initramfs nativo → firmware, módulos kernel, root FS,
+# siguen funcionando exactamente igual).
+# 5. Ejecuta update-grub o grub-mkconfig según distro.
+#
+# QUÉ NO HACE:
+# - Tocar /sbin/init, /usr/lib/systemd/* (systemd queda intacto y default).
+# - Cambiar GRUB_DEFAULT (seguís booteando systemd por default).
+# - Cargar arje ahora. Lo elegís manualmente en el próximo boot.
+# - Habilitar servicios systemd que arje no necesita (eso es responsabilidad
+# tuya, ver docs/arje-replace-systemd.md §4).
+#
+# REVERTIR: sudo scripts/uninstall-arje.sh
+#
+# Uso:
+# sudo scripts/install-arje-as-init.sh [seed.card.json]
+#
+# seed default: seeds/arje-host.card.json
+
+set -euo pipefail
+
+SEED="${1:-seeds/arje-host.card.json}"
+TARGET="${ARJE_TARGET:-x86_64-unknown-linux-musl}"
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
+cd "$REPO_DIR"
+
+if [ "$(id -u)" -ne 0 ]; then
+ echo "[install-arje] hace falta root (modifica /usr/sbin, /etc/grub.d, /ente)" >&2
+ exit 1
+fi
+
+if [ ! -f "$SEED" ]; then
+ echo "[install-arje] seed no encontrada: $SEED" >&2
+ exit 2
+fi
+
+# Verificar que el systemd default sigue siendo el init — querés saber
+# de qué te volvés capaz de regresar.
+if [ ! -e /sbin/init ] && [ ! -e /lib/systemd/systemd ] && [ ! -e /usr/lib/systemd/systemd ]; then
+ echo "[install-arje] WARN: no detecto systemd en esta máquina." >&2
+ echo " Si esta máquina ya corre arje, este script no es lo que querés." >&2
+ echo " Continuá sólo si sabés lo que hacés." >&2
+ read -r -p " ¿continuar? [y/N] " ans
+ [ "$ans" = "y" ] || exit 3
+fi
+
+echo "[install-arje] paso 1/5: compilar binarios musl-static"
+"$SCRIPT_DIR/build-arje-initrd.sh" "$SEED" /tmp/arje-noop.cpio.gz >/dev/null
+rm -f /tmp/arje-noop.cpio.gz
+BIN_DIR="$REPO_DIR/target/$TARGET/release"
+EX_DIR="$BIN_DIR/examples"
+
+echo "[install-arje] paso 2/5: instalar binarios en /usr/sbin/ y /usr/bin/"
+install -m 0755 "$BIN_DIR/ente-zero" /usr/sbin/ente-zero
+for b in ente-echo ente-policy-provider \
+ ente-logind-compat ente-hostnamed-compat ente-timedated-compat \
+ ente-localed-compat ente-journald-compat ente-resolved-compat \
+ ente-polkit-compat ente-machined-compat ente-systemd1-compat \
+ ente-notify-compat ente-timer-compat ente-tmpfiles-compat \
+ ente-binfmt-compat; do
+ install -m 0755 "$BIN_DIR/$b" "/usr/sbin/$b"
+done
+for e in brahman-status busctl brainctl; do
+ install -m 0755 "$EX_DIR/$e" "/usr/bin/$e"
+done
+
+echo "[install-arje] paso 3/5: instalar seed en /ente/seed.card.json"
+mkdir -p /ente
+# Backup del seed previo (si lo hay) — nunca sobrescribimos sin guardar.
+if [ -f /ente/seed.card.json ]; then
+ cp /ente/seed.card.json "/ente/seed.card.json.bak-$(date +%s)"
+fi
+install -m 0644 "$SEED" /ente/seed.card.json
+
+echo "[install-arje] paso 4/5: detectar kernel para menuentry GRUB"
+# Reusamos el kernel del host (mismo vmlinuz que usa systemd). Detectamos
+# el current; si tenés varios, se elige el del uname.
+KVER="$(uname -r)"
+VMLINUZ=""
+INITRD=""
+for cand in "/boot/vmlinuz-$KVER" "/boot/vmlinuz" /boot/vmlinuz-*; do
+ if [ -f "$cand" ]; then VMLINUZ="$cand"; break; fi
+done
+for cand in "/boot/initrd.img-$KVER" "/boot/initramfs-$KVER.img" \
+ "/boot/initrd.img" /boot/initrd.img-* /boot/initramfs-*.img; do
+ if [ -f "$cand" ]; then INITRD="$cand"; break; fi
+done
+
+if [ -z "$VMLINUZ" ] || [ -z "$INITRD" ]; then
+ echo "[install-arje] ERROR: no encontré vmlinuz o initrd nativo en /boot." >&2
+ echo " Sin esos no se puede armar la entrada GRUB." >&2
+ exit 4
+fi
+echo " kernel: $VMLINUZ"
+echo " initrd: $INITRD"
+
+# El root device — leemos /proc/cmdline para reusarlo exactamente como
+# está en la entrada systemd actual.
+ROOT_OPT="$(awk -v RS=' ' '/^root=/' /proc/cmdline | tr -d '\n')"
+if [ -z "$ROOT_OPT" ]; then
+ echo "[install-arje] WARN: no extraje root= de /proc/cmdline." >&2
+ echo " Configurá manualmente la entrada GRUB con tu root device." >&2
+ ROOT_OPT="root=/dev/sda1 # AJUSTAR"
+fi
+
+echo "[install-arje] paso 5/5: agregar menuentry GRUB"
+CUSTOM=/etc/grub.d/40_custom
+# Si ya existe nuestro menuentry, lo reemplazamos (idempotente).
+if grep -q "BEGIN ARJE-MENUENTRY" "$CUSTOM"; then
+ # Borrar el bloque previo.
+ awk '/BEGIN ARJE-MENUENTRY/{f=1} /END ARJE-MENUENTRY/{f=0; next} !f' "$CUSTOM" \
+ > "$CUSTOM.tmp" && mv "$CUSTOM.tmp" "$CUSTOM"
+fi
+
+cat >> "$CUSTOM" <<EOF
+# BEGIN ARJE-MENUENTRY (generated by install-arje-as-init.sh)
+menuentry "arje (init=/sbin/ente-zero) — kernel $KVER" {
+ insmod gzio
+ insmod part_msdos
+ insmod ext2
+ linux $VMLINUZ $ROOT_OPT ro init=/sbin/ente-zero console=tty1 console=ttyS0,115200 panic=10
+ initrd $INITRD
+}
+# END ARJE-MENUENTRY
+EOF
+chmod 0755 "$CUSTOM"
+
+# Regenerar grub.cfg.
+if command -v update-grub >/dev/null 2>&1; then
+ echo " ejecutando update-grub"
+ update-grub
+elif command -v grub-mkconfig >/dev/null 2>&1; then
+ echo " ejecutando grub-mkconfig"
+ grub-mkconfig -o /boot/grub/grub.cfg
+else
+ echo "[install-arje] WARN: no encontré update-grub ni grub-mkconfig." >&2
+ echo " Regenerá grub.cfg manualmente." >&2
+fi
+
+cat <<EOF
+
+[install-arje] HECHO.
+
+systemd sigue siendo tu init default — esta máquina arranca systemd
+salvo que elijas explícitamente "arje" en GRUB.
+
+ - binarios: /usr/sbin/ente-* y /usr/bin/{brahman-status,busctl,brainctl}
+ - seed activa: /ente/seed.card.json (origen: $SEED)
+ - menuentry: /etc/grub.d/40_custom (regenerá grub.cfg si lo editás)
+
+Para arrancar arje al próximo boot UNA SOLA VEZ (sin cambiar default):
+
+ sudo grub-reboot "arje (init=/sbin/ente-zero) — kernel $KVER"
+ sudo reboot
+
+Para revertir (quita binarios + menuentry):
+
+ sudo scripts/uninstall-arje.sh
+
+Antes del primer boot real, leé docs/arje-replace-systemd.md §3
+("Servicios systemd que arje NO arranca") para que NO te quedes sin
+red, sin sshd o sin filesystems montados.
+EOF
diff --git a/scripts/uninstall-arje.sh b/scripts/uninstall-arje.sh
new file mode 100755
index 0000000..5afe645
--- /dev/null
+++ b/scripts/uninstall-arje.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+# uninstall-arje.sh — revierte lo que install-arje-as-init.sh hizo.
+#
+# - Borra /usr/sbin/ente-* y /usr/bin/{brahman-status,busctl,brainctl}.
+# - Borra el bloque ARJE-MENUENTRY de /etc/grub.d/40_custom.
+# - Regenera grub.cfg.
+# - NO toca /ente/seed.card.json (querés conservar customización).
+# - NO toca /sbin/init ni systemd.
+
+set -euo pipefail
+
+if [ "$(id -u)" -ne 0 ]; then
+ echo "[uninstall-arje] hace falta root" >&2
+ exit 1
+fi
+
+echo "[uninstall-arje] removiendo binarios"
+rm -f /usr/sbin/ente-zero
+for b in ente-echo ente-policy-provider \
+ ente-logind-compat ente-hostnamed-compat ente-timedated-compat \
+ ente-localed-compat ente-journald-compat ente-resolved-compat \
+ ente-polkit-compat ente-machined-compat ente-systemd1-compat \
+ ente-notify-compat ente-timer-compat ente-tmpfiles-compat \
+ ente-binfmt-compat; do
+ rm -f "/usr/sbin/$b"
+done
+for e in brahman-status busctl brainctl; do
+ rm -f "/usr/bin/$e"
+done
+
+echo "[uninstall-arje] limpiando menuentry de /etc/grub.d/40_custom"
+CUSTOM=/etc/grub.d/40_custom
+if [ -f "$CUSTOM" ] && grep -q "BEGIN ARJE-MENUENTRY" "$CUSTOM"; then
+ awk '/BEGIN ARJE-MENUENTRY/{f=1} /END ARJE-MENUENTRY/{f=0; next} !f' "$CUSTOM" \
+ > "$CUSTOM.tmp" && mv "$CUSTOM.tmp" "$CUSTOM"
+ chmod 0755 "$CUSTOM"
+fi
+
+if command -v update-grub >/dev/null 2>&1; then
+ update-grub
+elif command -v grub-mkconfig >/dev/null 2>&1; then
+ grub-mkconfig -o /boot/grub/grub.cfg
+fi
+
+echo "[uninstall-arje] hecho. /ente/seed.card.json conservado (borralo a mano si querés)."
diff --git a/seeds/arje-host.card.json b/seeds/arje-host.card.json
new file mode 100644
index 0000000..ffa001d
--- /dev/null
+++ b/seeds/arje-host.card.json
@@ -0,0 +1,249 @@
+{
+ "schema_version": 1,
+ "id": "01J8YVKZH0M0M0M0M0M0M0M0M0",
+ "lineage": null,
+ "label": "arje.seed.host",
+ "provides": ["Spawn", "Journal"],
+ "requires": [],
+ "permissions": {
+ "networking": "full",
+ "filesystem": "read-write",
+ "ipc": { "allow": ["wit-v1", "dbus-v1"] },
+ "processes": true
+ },
+ "soma": {
+ "namespaces": { "mount": false, "pid": false, "net": false, "uts": false, "ipc": false, "user": false, "cgroup": false },
+ "rlimits": { "mem_bytes": null, "nproc": null, "nofile": null },
+ "cgroup": { "path": "ente.slice/zero", "cpu_weight": null, "io_weight": null },
+ "cpu_affinity": null
+ },
+ "payload": "Virtual",
+ "supervision": "OneShot",
+ "lifecycle": "daemon",
+ "priority": "critical",
+ "flow": { "input": [], "output": [] },
+ "genesis": [
+ {
+ "schema_version": 1,
+ "id": "01J8YVKZH0M0M0M0M0M0M0M0T1",
+ "label": "tmpfiles-boot",
+ "provides": [], "requires": [],
+ "permissions": { "networking": "none", "filesystem": "read-write", "ipc": { "allow": [] }, "processes": false },
+ "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false},
+ "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null},
+ "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null},
+ "cpu_affinity": null },
+ "payload": { "Native": { "exec": "/usr/sbin/ente-tmpfiles-compat", "argv": ["--boot"], "envp": [] } },
+ "supervision": "OneShot", "lifecycle": "oneshot", "priority": "critical",
+ "flow": { "input": [], "output": [] }, "genesis": []
+ },
+ {
+ "schema_version": 1,
+ "id": "01J8YVKZH0M0M0M0M0M0M0M0F1",
+ "label": "mount-fstab",
+ "provides": [], "requires": [],
+ "permissions": { "networking": "none", "filesystem": "read-write", "ipc": { "allow": [] }, "processes": true },
+ "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false},
+ "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null},
+ "cgroup": {"path":"ente.slice/boot","cpu_weight":null,"io_weight":null},
+ "cpu_affinity": null },
+ "payload": { "Native": { "exec": "/bin/mount", "argv": ["-a"], "envp": [] } },
+ "supervision": "OneShot", "lifecycle": "oneshot", "priority": "critical",
+ "flow": { "input": [], "output": [] }, "genesis": []
+ },
+ {
+ "schema_version": 1,
+ "id": "01J8YVKZH0M0M0M0M0M0M0M0S1",
+ "label": "swap-on",
+ "provides": [], "requires": [],
+ "permissions": { "networking": "none", "filesystem": "read-write", "ipc": { "allow": [] }, "processes": true },
+ "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false},
+ "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null},
+ "cgroup": {"path":"ente.slice/boot","cpu_weight":null,"io_weight":null},
+ "cpu_affinity": null },
+ "payload": { "Native": { "exec": "/sbin/swapon", "argv": ["-a"], "envp": [] } },
+ "supervision": "OneShot", "lifecycle": "oneshot", "priority": "normal",
+ "flow": { "input": [], "output": [] }, "genesis": []
+ },
+ {
+ "schema_version": 1,
+ "id": "01J8YVKZH0M0M0M0M0M0M0M0DB",
+ "label": "dbus-system",
+ "provides": [], "requires": [],
+ "permissions": { "networking": "loopback", "filesystem": "read-write", "ipc": { "allow": ["dbus-v1"] }, "processes": true },
+ "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false},
+ "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null},
+ "cgroup": {"path":"ente.slice/bus","cpu_weight":null,"io_weight":null},
+ "cpu_affinity": null },
+ "payload": { "Native": { "exec": "/usr/bin/dbus-daemon", "argv": ["--system", "--nofork"], "envp": [] } },
+ "supervision": { "Restart": { "initial": 200, "max": 30000 } },
+ "lifecycle": "daemon", "priority": "critical",
+ "flow": { "input": [], "output": [] }, "genesis": []
+ },
+ {
+ "schema_version": 1,
+ "id": "01J8YVKZH0M0M0M0M0M0M0M0H1",
+ "label": "compat-hostnamed",
+ "provides": [], "requires": [],
+ "permissions": { "networking": "loopback", "filesystem": "read-only", "ipc": { "allow": ["dbus-v1"] }, "processes": false },
+ "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false},
+ "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null},
+ "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null},
+ "cpu_affinity": null },
+ "payload": { "Native": { "exec": "/usr/sbin/ente-hostnamed-compat", "argv": [], "envp": [] } },
+ "supervision": { "Restart": { "initial": 100, "max": 30000 } },
+ "lifecycle": "daemon", "priority": "normal",
+ "flow": { "input": [], "output": [] }, "genesis": []
+ },
+ {
+ "schema_version": 1,
+ "id": "01J8YVKZH0M0M0M0M0M0M0M0T2",
+ "label": "compat-timedated",
+ "provides": [], "requires": [],
+ "permissions": { "networking": "loopback", "filesystem": "read-only", "ipc": { "allow": ["dbus-v1"] }, "processes": false },
+ "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false},
+ "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null},
+ "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null},
+ "cpu_affinity": null },
+ "payload": { "Native": { "exec": "/usr/sbin/ente-timedated-compat", "argv": [], "envp": [] } },
+ "supervision": { "Restart": { "initial": 100, "max": 30000 } },
+ "lifecycle": "daemon", "priority": "normal",
+ "flow": { "input": [], "output": [] }, "genesis": []
+ },
+ {
+ "schema_version": 1,
+ "id": "01J8YVKZH0M0M0M0M0M0M0M0J1",
+ "label": "compat-journald",
+ "provides": ["Journal"], "requires": [],
+ "permissions": { "networking": "none", "filesystem": "read-write", "ipc": { "allow": ["dbus-v1"] }, "processes": false },
+ "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false},
+ "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null},
+ "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null},
+ "cpu_affinity": null },
+ "payload": { "Native": { "exec": "/usr/sbin/ente-journald-compat", "argv": [], "envp": [] } },
+ "supervision": { "Restart": { "initial": 100, "max": 30000 } },
+ "lifecycle": "daemon", "priority": "high",
+ "flow": { "input": [], "output": [] }, "genesis": []
+ },
+ {
+ "schema_version": 1,
+ "id": "01J8YVKZH0M0M0M0M0M0M0M0Y1",
+ "label": "compat-logind",
+ "provides": ["LegacyLogind"], "requires": [],
+ "permissions": { "networking": "loopback", "filesystem": "read-write", "ipc": { "allow": ["dbus-v1"] }, "processes": false },
+ "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false},
+ "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null},
+ "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null},
+ "cpu_affinity": null },
+ "payload": { "Native": { "exec": "/usr/sbin/ente-logind-compat", "argv": [], "envp": [] } },
+ "supervision": { "Restart": { "initial": 100, "max": 30000 } },
+ "lifecycle": "daemon", "priority": "high",
+ "flow": { "input": [], "output": [] }, "genesis": []
+ },
+ {
+ "schema_version": 1,
+ "id": "01J8YVKZH0M0M0M0M0M0M0M0R1",
+ "label": "compat-resolved",
+ "provides": [], "requires": [],
+ "permissions": { "networking": "outbound", "filesystem": "read-write", "ipc": { "allow": ["dbus-v1"] }, "processes": false },
+ "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false},
+ "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null},
+ "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null},
+ "cpu_affinity": null },
+ "payload": { "Native": { "exec": "/usr/sbin/ente-resolved-compat", "argv": [], "envp": [] } },
+ "supervision": { "Restart": { "initial": 100, "max": 30000 } },
+ "lifecycle": "daemon", "priority": "normal",
+ "flow": { "input": [], "output": [] }, "genesis": []
+ },
+ {
+ "schema_version": 1,
+ "id": "01J8YVKZH0M0M0M0M0M0M0M0P1",
+ "label": "compat-polkit",
+ "provides": [], "requires": [],
+ "permissions": { "networking": "loopback", "filesystem": "read-only", "ipc": { "allow": ["dbus-v1"] }, "processes": false },
+ "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false},
+ "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null},
+ "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null},
+ "cpu_affinity": null },
+ "payload": { "Native": { "exec": "/usr/sbin/ente-polkit-compat", "argv": [], "envp": [] } },
+ "supervision": { "Restart": { "initial": 100, "max": 30000 } },
+ "lifecycle": "daemon", "priority": "normal",
+ "flow": { "input": [], "output": [] }, "genesis": []
+ },
+ {
+ "schema_version": 1,
+ "id": "01J8YVKZH0M0M0M0M0M0M0M0PP",
+ "label": "policy-provider",
+ "provides": [], "requires": [],
+ "permissions": { "networking": "none", "filesystem": "read-only", "ipc": { "allow": ["wit-v1"] }, "processes": false },
+ "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false},
+ "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null},
+ "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null},
+ "cpu_affinity": null },
+ "payload": { "Native": { "exec": "/usr/sbin/ente-policy-provider", "argv": [], "envp": [] } },
+ "supervision": { "Restart": { "initial": 100, "max": 30000 } },
+ "lifecycle": "daemon", "priority": "normal",
+ "flow": { "input": [], "output": [] }, "genesis": []
+ },
+ {
+ "schema_version": 1,
+ "id": "01J8YVKZH0M0M0M0M0M0M0M0SY",
+ "label": "compat-systemd1",
+ "provides": [], "requires": [],
+ "permissions": { "networking": "loopback", "filesystem": "read-write", "ipc": { "allow": ["dbus-v1"] }, "processes": false },
+ "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false},
+ "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null},
+ "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null},
+ "cpu_affinity": null },
+ "payload": { "Native": { "exec": "/usr/sbin/ente-systemd1-compat", "argv": [], "envp": [] } },
+ "supervision": { "Restart": { "initial": 100, "max": 30000 } },
+ "lifecycle": "daemon", "priority": "high",
+ "flow": { "input": [], "output": [] }, "genesis": []
+ },
+ {
+ "schema_version": 1,
+ "id": "01J8YVKZH0M0M0M0M0M0M0M0NW",
+ "label": "network-dhcpcd",
+ "provides": [], "requires": [],
+ "permissions": { "networking": "full", "filesystem": "read-write", "ipc": { "allow": [] }, "processes": true },
+ "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false},
+ "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null},
+ "cgroup": {"path":"ente.slice/net","cpu_weight":null,"io_weight":null},
+ "cpu_affinity": null },
+ "payload": { "Native": { "exec": "/usr/sbin/dhcpcd", "argv": ["-B"], "envp": [] } },
+ "supervision": { "Restart": { "initial": 500, "max": 60000 } },
+ "lifecycle": "daemon", "priority": "high",
+ "flow": { "input": [], "output": [] }, "genesis": []
+ },
+ {
+ "schema_version": 1,
+ "id": "01J8YVKZH0M0M0M0M0M0M0M0SS",
+ "label": "sshd",
+ "provides": [], "requires": [],
+ "permissions": { "networking": "full", "filesystem": "read-write", "ipc": { "allow": [] }, "processes": true },
+ "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false},
+ "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null},
+ "cgroup": {"path":"ente.slice/ssh","cpu_weight":null,"io_weight":null},
+ "cpu_affinity": null },
+ "payload": { "Native": { "exec": "/usr/sbin/sshd", "argv": ["-D"], "envp": [] } },
+ "supervision": { "Restart": { "initial": 200, "max": 30000 } },
+ "lifecycle": "daemon", "priority": "high",
+ "flow": { "input": [], "output": [] }, "genesis": []
+ },
+ {
+ "schema_version": 1,
+ "id": "01J8YVKZH0M0M0M0M0M0M0M0G1",
+ "label": "getty-tty1",
+ "provides": [], "requires": [],
+ "permissions": { "networking": "none", "filesystem": "read-write", "ipc": { "allow": [] }, "processes": true },
+ "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false},
+ "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null},
+ "cgroup": {"path":"ente.slice/getty","cpu_weight":null,"io_weight":null},
+ "cpu_affinity": null },
+ "payload": { "Native": { "exec": "/sbin/agetty", "argv": ["--noclear", "tty1", "linux"], "envp": [] } },
+ "supervision": { "Restart": { "initial": 100, "max": 30000 } },
+ "lifecycle": "daemon", "priority": "high",
+ "flow": { "input": [], "output": [] }, "genesis": []
+ }
+ ]
+}