1# Reemplazar systemd con arje en una máquina real
2
3> **Importante**: este flujo deja **systemd intacto** y agrega arje como
4> entrada GRUB alternativa. No es "destruir y reemplazar"; es "convivir
5> hasta confiar". Booteás arje cuando querés, volvés a systemd si rompe.
6
7## 1. Estrategia
8
9| componente | qué hacemos |
10| --------------------- | -------------------------------------------------------- |
11| `/sbin/init` | NO se toca (sigue siendo systemd). |
12| `/lib/systemd/*` | NO se toca. |
13| `kernel + initramfs` | reusamos los del host (firmware, módulos, root FS). |
14| `init=` en GRUB | nueva entrada apunta a `/sbin/arje-zero`. |
15| `GRUB_DEFAULT` | NO se cambia. Sigue arrancando systemd por default. |
16| `/ente/seed.card.json`| nueva — define qué arranca arje. |
17| `/usr/sbin/arje-*` | binarios musl-static del fractal. |
18
19Booteo del kernel → initramfs nativo hace su trabajo (mountea root,
20carga módulos) → cuando llega el `switch_root`, en lugar de exec
21`/lib/systemd/systemd`, exec `/sbin/arje-zero`. Esto deja firmware y
22udev fuera del scope de arje (todavía).
23
24## 2. Instalación
25
26```bash
27# 1. (opcional) revisá la seed default que se va a copiar a /ente/
28$EDITOR seeds/arje-host.card.json
29
30# 2. instalar
31sudo scripts/install-arje-as-init.sh
32
33# 3. (opcional) bootear arje SÓLO en el próximo reboot (no cambia default)
34sudo grub-reboot "arje (init=/sbin/arje-zero) — kernel $(uname -r)"
35sudo reboot
36```
37
38Si algo rompe en arje: reset/poweroff → en el menú GRUB elegir la
39entrada default (systemd) → volvés a un sistema funcional. `arje` no
40toca configuración de systemd, así que el rollback es instantáneo.
41
42Para **desinstalar**:
43
44```bash
45sudo scripts/uninstall-arje.sh
46```
47
48## 3. Filosofía vs systemd — y por qué arje no acapara
49
50systemd arranca, por default, **todo lo que tiene un `.service` con
51`WantedBy=multi-user.target`**, recursivamente vía dependencias. Eso es
52opaco y crece con cada paquete instalado.
53
54arje no tiene generadores, ni `WantedBy`, ni `Wants`, ni target trees.
55**Arranca exactamente lo declarado en `genesis: [...]` de la
56`seed.card.json`**, en el orden declarativo, con la `supervision` que
57declarás vos. Si no lo declaraste, no corre. Punto.
58
59### 3a. Servicios que systemd arranca y arje NO debería migrar
60
61Estos son los sospechosos típicos en Debian/Ubuntu/Arch. **No agregues
62ninguno a tu seed** salvo que sepas que lo usás:
63
64| systemd unit | por qué saltearlo |
65| ------------------------------------ | ---------------------------------------- |
66| `ModemManager.service` | no tenés módem 3G/4G. |
67| `bluetooth.service` | si no usás bluetooth. |
68| `cups.service`, `cups-browsed` | si no imprimís. |
69| `avahi-daemon.service` | mDNS local. Ruido si no lo necesitás. |
70| `snapd.service` | si no usás snaps. |
71| `apt-daily.service`, `apt-daily-upgrade` | autoupdates — no son init. |
72| `unattended-upgrades.service` | corre tras boot, no init. |
73| `accounts-daemon.service` | UI helpers GNOME; no init. |
74| `colord.service` | calibración de color. No init. |
75| `geoclue.service` | localización. No init. |
76| `gdm.service`, `lightdm.service` | DM gráfico — agregalo SÓLO si querés GUI.|
77| `NetworkManager-wait-online.service` | espera red. arje no necesita "esperar". |
78| `systemd-timesyncd.service` | reemplaza con cron-like + ntpd one-shot. |
79| `systemd-machined.service` | reemplazado por `arje-machined-compat`. |
80| `systemd-logind.service` | reemplazado por `arje-logind-compat`. |
81| `systemd-resolved.service` | reemplazado por `arje-resolved-compat`. |
82| `systemd-hostnamed.service` | reemplazado por `arje-hostnamed-compat`. |
83| `systemd-timedated.service` | reemplazado por `arje-timedated-compat`. |
84| `systemd-localed.service` | reemplazado por `arje-localed-compat`. |
85| `systemd-journald.service` | reemplazado por `arje-journald-compat`. |
86| `systemd-tmpfiles-*.service` | reemplazado por `arje-tmpfiles-compat`. |
87| `systemd-binfmt.service` | reemplazado por `arje-binfmt-compat`. |
88| `polkitd.service` | reemplazado por `arje-polkit-compat`. |
89
90Las últimas 12 ya están como Cards en `seeds/arje-host.card.json`. Las
91de la mitad de arriba **no** — no las agregues.
92
93### 3b. Servicios que SÍ necesitás declarar (y que ya están en `arje-host.card.json`)
94
95| Card label | reemplaza | binario |
96| ------------------ | -------------------------- | -------------------------------------------- |
97| `tmpfiles-boot` | `systemd-tmpfiles-setup` | `/usr/sbin/arje-tmpfiles-compat --boot` |
98| `mount-fstab` | `local-fs.target` | `/bin/mount -a` (one-shot) |
99| `swap-on` | `swap.target` | `/sbin/swapon -a` (one-shot) |
100| `dbus-system` | `dbus.service` | `/usr/bin/dbus-daemon --system --nofork` |
101| `compat-*` (×11) | shims D-Bus de systemd | `/usr/sbin/arje-*-compat` |
102| `network-dhcpcd` | `NetworkManager` / `networkd` | `/usr/sbin/dhcpcd -B` |
103| `sshd` | `ssh.service` | `/usr/sbin/sshd -D` |
104| `getty-tty1` | `getty@tty1.service` | `/sbin/agetty --noclear tty1 linux` |
105
10615 entes. Compará: una instalación Debian fresh con GNOME desktop tiene
107**>180 unidades activas** tras `systemctl list-units --state=running`.
108
109### 3c. Cómo agregar un servicio que falta
110
1111. Encontrá el binario y los argumentos. Mirá el `.service` original:
112 ```
113 systemctl cat <unit>
114 ```
115 Mirá `ExecStart=` y reproducí.
116
1172. Agregá una Card al `genesis` de `/ente/seed.card.json`:
118 ```json
119 {
120 "schema_version": 1,
121 "id": "<ULID nuevo, Crockford base32, no I L O U>",
122 "label": "mi-servicio",
123 "provides": [], "requires": [],
124 "permissions": { "networking": "outbound", "filesystem": "read-write", "ipc": { "allow": [] }, "processes": true },
125 "soma": { ... idéntico al patrón de los otros ... },
126 "payload": { "Native": { "exec": "/usr/sbin/mi-bin", "argv": ["--foreground"], "envp": [] } },
127 "supervision": { "Restart": { "initial": 500, "max": 30000 } },
128 "lifecycle": "daemon",
129 "priority": "normal",
130 "flow": { "input": [], "output": [] },
131 "genesis": []
132 }
133 ```
134 **Importante**: `argv` tiene que tener el equivalente a `--foreground` o
135 `-D` o `--no-fork`. Si el binario se daemoniza solo (fork+exit), arje
136 no lo puede supervisar y caerá en loop de restart.
137
1383. Validar:
139 ```bash
140 seeds/validate.sh /ente/seed.card.json
141 ```
142
1434. Reboot a arje. Verificar:
144 ```sh
145 arje# brahman-status
146 ```
147
148## 4. Diferencias de UX con systemd
149
150| acción | systemd | arje |
151| ---------------------------- | ------------------------------------ | ------------------------------------------------------- |
152| Ver servicios activos | `systemctl list-units --state=running` | `brahman-status` + `busctl list-entes` |
153| Ver logs | `journalctl -u foo` | `arje-journalctl` (TODO en CLI; por ahora `dmesg`) |
154| Restart un servicio | `systemctl restart foo` | matar el PID (`brain` lo reanuda por `Supervision`) |
155| Habilitar/Deshabilitar | `systemctl enable/disable` | editar `/ente/seed.card.json` + reboot |
156| Ver dependencias | `systemctl list-dependencies foo` | `brahman-status` muestra references y flow matches |
157| Detectar fallas reincidentes | `systemctl status foo` → "failed" | `brainctl crystals` muestra patrones `EnteDied → …` |
158| socket activation | `.socket` unit | (no implementado todavía; ver flow + broker matching) |
159| user services | `--user` | (no implementado; arje es system-wide por ahora) |
160
161## 5. Cosas que arje todavía NO hace que sí hace systemd
162
163- **udev / hotplug**: arje captura uevents (vía `spawn_uevent_stream`),
164 pero no hay reglas declarativas todavía. Para boot inicial, el
165 initramfs nativo ya hizo el cold-plug, así que no es bloqueante.
166- **Timers** (cron-like): `arje-timer-compat` existe pero no está en la
167 seed host por default. Agregalo si querés `*.timer` units migradas.
168- **Service generators**: no hay equivalente a `systemd-fstab-generator`
169 ni `systemd-getty-generator`. Tu seed los declara explícitamente.
170- **Socket activation**: parcialmente — el broker matchea flow
171 consumer↔producer, pero no hay aún listen-on-fd-handoff como
172 `sd_listen_fds()`.
173- **User instances**: no hay todavía `arje --user`. Los procesos de
174 usuario los lanza un getty/login estándar.
175
176## 6. Checklist pre-primer-boot
177
178- [ ] `seeds/arje-host.card.json` ajustada a tu hardware/red/users.
179- [ ] `seeds/validate.sh` → OK.
180- [ ] `/usr/bin/dbus-daemon` existe (de lo contrario los shims no se
181 anuncian — `apt install dbus`).
182- [ ] `/usr/sbin/dhcpcd` o tu network stack preferido está instalado.
183- [ ] `/usr/sbin/sshd` instalado y `/etc/ssh/sshd_config` ok si querés
184 poder entrar remoto si la consola falla.
185- [ ] `/sbin/agetty` instalado (`util-linux`) para tty1.
186- [ ] `sudo scripts/install-arje-as-init.sh` ejecutado.
187- [ ] `grub-reboot` apuntado a la entrada arje **sólo para el próximo
188 boot** — no `grub-set-default`.
189- [ ] Listo para revertir vía menú GRUB si algo falla.
190
191## 7. Cuando arje sea estable: hacerlo default
192
193Cuando ya hayas booteado arje N veces sin problemas y querés que sea
194default:
195
196```bash
197# Listar entradas
198sudo grep -E "^\s*menuentry " /boot/grub/grub.cfg | nl
199
200# Ponerlo como default (usa el índice del menú o el title exacto)
201sudo grub-set-default "arje (init=/sbin/arje-zero) — kernel $(uname -r)"
202```
203
204Mantené la entrada systemd como rescue. Si querés acortar la lista de
205units que systemd arrancaría si bootea de nuevo: `systemctl disable
206ModemManager bluetooth cups …` — los nombres de §3a son una guía.
207
208## Apéndice — depurar el primer boot fallido
209
210Si arje no levanta el primer boot:
211
2121. Volver a GRUB → entrada systemd → bootea normal.
2132. `journalctl --boot=-1` (logs del intento previo).
2143. Buscar `arje-zero` o `Tarjeta Semilla`. Si nada → el ejecutable no
215 se lanzó (kernel panic temprano, falta `init=` correcto, etc.).
2164. Si arrancó pero falló alguna Card: la próxima vez, antes de reboot:
217 ```bash
218 sudo sed -i 's/console=tty1/console=tty1 ente.log=trace/' /etc/grub.d/40_custom
219 sudo update-grub
220 ```
221 y revisá `dmesg` desde systemd tras el reintento.
arje-replace-systemd.md
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
# 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:
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
-
Encontrá el binario y los argumentos. Mirá el .service original:
systemctl cat <unit>
Mirá ExecStart= y reproducí.
-
Agregá una Card al genesis de /ente/seed.card.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.
-
Validar:
seeds/validate.sh /ente/seed.card.json
-
Reboot a arje. Verificar:
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
7. Cuando arje sea estable: hacerlo default
Cuando ya hayas booteado arje N veces sin problemas y querés que sea
default:
# 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:
- Volver a GRUB → entrada systemd → bootea normal.
journalctl --boot=-1 (logs del intento previo).
- Buscar
arje-zero o Tarjeta Semilla. Si nada → el ejecutable no
se lanzó (kernel panic temprano, falta init= correcto, etc.).
- Si arrancó pero falló alguna Card: la próxima vez, antes de reboot:
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.