# 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/arje-zero`.                |
| `GRUB_DEFAULT`        | NO se cambia. Sigue arrancando systemd por default.      |
| `/ente/seed.card.json`| nueva — define qué arranca arje.                         |
| `/usr/sbin/arje-*`    | 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/arje-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/arje-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 `arje-machined-compat`.  |
| `systemd-logind.service`             | reemplazado por `arje-logind-compat`.    |
| `systemd-resolved.service`           | reemplazado por `arje-resolved-compat`.  |
| `systemd-hostnamed.service`          | reemplazado por `arje-hostnamed-compat`. |
| `systemd-timedated.service`          | reemplazado por `arje-timedated-compat`. |
| `systemd-localed.service`            | reemplazado por `arje-localed-compat`.   |
| `systemd-journald.service`           | reemplazado por `arje-journald-compat`.  |
| `systemd-tmpfiles-*.service`         | reemplazado por `arje-tmpfiles-compat`.  |
| `systemd-binfmt.service`             | reemplazado por `arje-binfmt-compat`.    |
| `polkitd.service`                    | reemplazado por `arje-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/arje-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/arje-*-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`                  | `arje-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): `arje-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/arje-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 `arje-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.
