tawasuyu · Raíz
Arje: el init que recuerda — el sistema entero como un commit firmado
El primer programa que corre en tu computadora decide todo lo demás, y en casi todos los Linux ese programa es systemd: un gigante gobernado por el calendario de una corporación. Arje lo reemplaza con 48 mil líneas de Rust que hacen lo que systemd no sabe hacer: recordar cada estado del sistema, explicar cada muerte, y resucitar la máquina entera por el cable.
17 min de lecturaPor Redacción Gioser
Apretás el botón de encendido. Antes de que exista tu escritorio, tus archivos o tu red, el núcleo del sistema lanza un solo programa y le entrega la máquina: el init, el proceso número 1. De él desciende todo lo que va a correr ese día, y si él muere, el sistema entero muere con él. En casi todos los Linux del mundo ese programa es systemd. En las máquinas de tawasuyu es arje —del griego ἀρχή: origen, principio—, y este artículo cuenta qué hace distinto, con los números a la vista.
El programa que corre antes que todos
Un init hace, como mínimo, tres cosas: arranca los servicios en el orden correcto, adopta a los procesos huérfanos, y vuelve a levantar lo que se cae. Parece poco. No lo es: es el único proceso que no puede fallar nunca —si el proceso 1 muere, el núcleo entra en pánico y la pantalla se congela— y el único que ve nacer y morir a todos los demás. Quien escribe el init decide cómo arranca tu computadora, qué corre con qué permisos, y qué pasa cuando algo sale mal.
Por eso importa quién lo escribe. Y por eso conviene mirar quién lo escribe hoy.
systemd: cómo el arranque consiguió dueño
systemd nació en 2010 dentro de Red Hat para resolver problemas reales: los init de scripts de shell eran lentos, frágiles y no supervisaban nada. Hay que decirlo sin caricatura: ganó porque lo anterior era peor. Pero ganó de una forma particular: absorbiendo. Lo que empezó como un init hoy incluye el gestor de sesiones (logind), el de logs (journald, con formato binario propio), el de red (networkd), el de DNS (resolved), el de hora, el de usuarios (homed), el de particiones (repart), contenedores (nspawn), un cargador de arranque y una docena de piezas más. Ninguna distribución grande puede ya quitarle una pieza sin romper las demás: Debian lo adoptó tras una de las votaciones más ásperas de su historia, y GNOME directamente no arranca completo sin sus interfaces.
Eso es software corporativo en el sentido exacto del término: nació para resolverle los problemas a una empresa, creció absorbiendo lo que nadie le pidió absorber, y llegó a un punto donde bajarse ya no es una opción práctica. Su creador trabaja hoy en Microsoft; el proyecto lo gobierna el calendario de Red Hat/IBM. No hace falta teoría conspirativa alguna: alcanza con constatar que la pieza que decide cómo arranca casi todo Linux del planeta tiene un roadmap, y ese roadmap no lo escribís vos.
Y hay un argumento técnico, más frío que el político. El proceso 1 de systemd interpreta archivos de configuración con un parser de texto, habla D-Bus, y carga sobre sí una superficie enorme de código C. En el proyecto tawasuyu eso se resume en una doctrina de cuatro palabras: «un parser en el init es una bomba». Todo lo que el proceso 1 hace de más es superficie de ataque en el único proceso que no puede morir.
Arje, en una frase
Arje cubre desde «presionaste ENCENDER» hasta «el sistema está en pie»: un cargador de arranque UEFI propio, el proceso 1, la supervisión, el aislamiento y la compatibilidad con lo viejo. Y habla otro idioma, porque piensa distinto:
| systemd dice | arje dice | la diferencia |
|---|---|---|
| unit / service | Ente, descrito por una Card | la unidad no es un archivo .ini: es un objeto tipado, validado al compilar |
PID 1 (systemd) | arje-zero, «el Ente #0» | mínimo a propósito: nunca lógica de servicio en el proceso 1 |
default target + symlinks de enable | la Semilla | lo que arranca es un árbol declarado en un solo objeto, no una tabla de enlaces repartida por el disco |
| el conjunto de units activas | el fractal | un grafo vivo de Entes, con eventos tipados |
| D-Bus como plano de control | arje-bus | socket propio; la identidad de quien pide la inyecta el núcleo y no se puede falsear |
| sandbox por directivas | el soma de la Card | namespaces, cgroups, límites y jaulas declarados en el mismo objeto que describe al proceso |
Cinco decisiones que lo cambian todo
1 — La unidad es un tipo, no un archivo de texto. Una Card es una estructura de Rust serializada: no hay parser de sintaxis en el proceso 1, hay validación en compilación. Un campo mal escrito no es un warning en un log que nadie lee: es un error antes de que exista el binario.
2 — Las dependencias son capacidades, no nombres. systemd dice «necesito a nginx.service»; arje dice «necesito a quien provea esta capacidad». Quién la provee es intercambiable, y el orden de arranque no se declara: se calcula — un ordenamiento topológico sobre el grafo de capacidades, que detecta ciclos y descarta lo insatisfacible antes de lanzar nada.
3 — No hay enable ni disable: hay Semilla. Lo que arranca tu máquina es el árbol genesis de un solo objeto firmable. Cambiar el arranque es editar ese objeto — no crear symlinks que después nadie recuerda por qué están.
4 — El bus de control lo autentica el núcleo. Cada pedido privilegiado (matar un Ente, congelar, cambiar cuotas) llega con la identidad del proceso que lo pide inyectada por el kernel vía SO_PEERCRED — imposible de falsear — y un verbo clave: RunCard encarna un proceso nuevo con los permisos de quien lo pide, no con los del init. La escalada de privilegios por el bus es imposible por construcción, no por política.
5 — El supervisor es soberano. En systemd, systemctl stop gana siempre. En arje, si la Card dice «esto se supervisa», un stop externo es apenas una señal, y el supervisor decide si el Ente revive. Parar de verdad algo supervisado exige sacarlo del grafo — que es exactamente lo que un atacante con una shell no puede hacer.
El arranque que se ve: seis actos eran tres
La prueba más honesta de un init no es un benchmark: es mirar la pantalla desde que apretás el botón. En esa mirada, un arranque Linux típico es una sucesión de parpadeos — logo del firmware, texto de consola, pantalla negra, splash, otra pantalla negra, login. Mirando un arranque real de arje con ese ojo aparecieron cuatro actos de más (un cuadrado de color que era un placeholder olvidado, un menú que parpadeaba a cada segundo, texto de consola filtrado sobre el gráfico, un segundo menú redundante). La secuencia que queda son tres actos, sin un cuadro negro entre medio:
- loaderEl menú-grafo UEFI propio: los sistemas arrancables dibujados como grafo, con degradado y la chakana animada. Elegir acá sí tiene efecto — es antes del kernel.
- splashLa chakana nativa, dibujada por arje-splash directo al framebuffer, reusando el modo de video que dejó el loader. Sin Plymouth.
- greeterEl compositor retoma la misma chakana del splash y la deshace sobre el login gráfico. Entre una y otra no hay ni un cuadro negro.
Ese «ni un cuadro negro» costó una cacería medida en milisegundos. El instrumental del motor gráfico mostró que el grueso del hueco entre splash y escritorio era apagar y re-encender el panel — unos 1.063 ms de los 1.500 del hueco total. La cura fue no soltar nunca el panel: el splash mantiene la imagen en pantalla hasta que el compositor está listo para el relevo.
La última costura apareció filmando la pantalla en cámara lenta: entre la chakana del splash y la del escritorio quedaban 381 ms de panel encendido sin nada dibujado. El registro del kernel señaló al culpable —el compositor presentaba primero un cuadro liso, herencia de un relevo anterior— y hoy su primer cuadro ya es la chakana. Con un segundo monitor enchufado sí hay un apagón: para encenderlo, el controlador de video tiene que cambiar el reloj de pantalla, y eso apaga el panel un instante. Nadie puede evitarlo, pero sí elegir qué se ve: el cargador detecta la otra pantalla y funde a tiempo al fondo común, así el apagón cae entre dos fondos iguales en vez de cortar el menú.
Y el menú-grafo se instala con una red de seguridad pensada para máquinas que no tienen a nadie mirándolas: no fija el orden de arranque del firmware —irreversible en una máquina ciega— sino el intento único BootNext, que el firmware borra al usarlo y que un guardián re-arma en cada arranque sano. Un cargador roto consume su intento y la máquina cae sola al arranque de siempre. Un reset, sin rescate.
La máquina es un alma; el hardware, un cuerpo
Hasta acá, arje es un init mejor. Lo que sigue es otra categoría de cosa, y sale de una frase del plan del proyecto: «la máquina es un alma; el hardware es un cuerpo». El alma es la Semilla firmada, más la historia de sus estados, más un almacén direccionado por contenido donde cada binario vive bajo el hash de sus propios bytes. Cuatro consecuencias, las cuatro funcionando:
Linaje
Cada cambio del sistema —un servicio nuevo, un permiso que se movió— es un commit firmado en una cadena a prueba de bifurcaciones. arjectl log y arjectl diff contestan qué cambió y cuándo; el diff es semántico y nombra los permisos que se movieron.
Bootear es hacer checkout: la máquina arranca el commit elegido. Rollback de cero bytes — el estado viejo ya está entero en el almacén.
Cuerpos
Un hardware nuevo se levanta pidiendo por la red los bloques del último commit de un alma. Sin servidor, sin nube: por Ethernet crudo, verificando cada bloque contra su hash.
Certificado de punta a punta: un nodo B, con sólo los hashes, resucitó el alma del nodo A por el cable y la arrancó como proceso 1.
Jaula
El encierro de un proceso se deriva de su Card firmada, no se configura aparte: las rutas que declara se vuelven frontera del kernel (Landlock), y sus permisos, un filtro de llamadas al sistema (seccomp).
Sin concesión firmada, jaula máxima. El kernel devuelve «denegado» a lo no declarado — comprobado con la llamada en la mano.
Forense
Cada arranque deja una caja negra firmada y encadenada. Si la máquina murió a mitad del boot, el arranque siguiente lo explica solo, en tu idioma.
arjectl autopsy: «el boot anterior NO fue limpio — ente=mirada, paso=greeter». Y arjectl blame da el commit que tocó por última vez al culpable.
El paquete completo suena a control de versiones, y lo es: log, diff, checkout, blame, y hasta bisect — una regresión sembrada en una historia de cien commits se encuentra en siete pasos, igual que en git, pero sobre tu sistema operativo. Para quien conozca NixOS: no es eso. No hay lenguaje nuevo ni evaluación — el estado es la Semilla más el almacén, que ya existían. Lo que NixOS no tiene y acá sí: la historia es firmada y no repudiable, el diff es semántico (objetos, no texto), y dos estados divergentes se fusionan estructuralmente.
Esto no nació de un ejercicio teórico. Un día de septiembre, una unidad puesta a mano en la Semilla de un servidor desapareció: la Semilla se regenera desde el sistema vivo, y regenerar pisaba. Sin historia, un estado pisado no deja rastro — se notó de casualidad. Con el linaje, esa unidad perdida habría sido una línea de diff: − card minga-host. El init que recuerda existe porque el init que olvida ya costó caro.
La confianza se firma, no se supone
Antes de encarnar las piezas críticas del arranque, arje las atesta: el hash BLAKE3 de cada binario se contrasta contra concesiones firmadas con Ed25519, ancladas a una llave raíz que vive fuera de la Semilla — de modo que reescribir la Semilla no alcanza para colar un binario ajeno. La misma firma cubre las tres rutas por las que una Semilla puede nacer (instalador, empaquetador, absorción), con una sola implementación: el manifiesto es idéntico salga por donde salga. Y como el almacén direcciona por contenido, el mismo hash que firma la atestación es el nombre con el que un par puede pedir ese binario por la red y reproducir el sistema exacto.
Tus programas no se enteran
La objeción obvia: «todo el software de escritorio ya asume systemd». Es cierta, y arje la responde con una capa de compatibilidad explícita — no fingiendo ser systemd, sino sirviendo sus interfaces como borde, con la lógica real del otro lado:
| lo que el software espera | lo que arje le sirve |
|---|---|
org.freedesktop.login1 (logind) | un shim D-Bus con sesiones, asientos, inhibiciones reales y traspaso del master de video — el escritorio gestiona energía y pantalla como siempre |
libsystemd / libelogind (la C-ABI que enlazan mutter, polkit, gcr) | una libelogind.so propia de 28 funciones y una sola dependencia, verificada contra la libsystemd real |
org.freedesktop.systemd1, de sistema y de usuario | cada Ente aparece como «unit»; gnome-session arranca sus servicios con PIDs reales |
journald, journalctl | cada mensaje persiste al almacén por hash; journalctl -o export emite el formato de systemd, re-importable con las herramientas viejas |
sd_notify, tmpfiles, binfmt, timers, hostnamed, localed, timedated, resolved, machined, polkit… | 20 binarios de borde, uno por interfaz, con el parseo compartido y testeado en un solo lugar |
systemctl, loginctl, journalctl en tus dedos | el mismo binario de arjectl bajo otro nombre; y si systemd es el init vivo, reenvía al de verdad en vez de mentir |
La vara de esa capa fue el software más exigente que existe al respecto: GNOME 50 arranca y pinta el escritorio completo bajo arje — gnome-shell, sus servicios de sesión, ibus y portales, verificado en una VM Fedora con captura de pantalla incluida. KDE, que no depende de systemd --user, anduvo antes. Y para migrar una máquina existente está arje-absorb: lee la configuración del init que haya (sysvinit, runit, dinit, OpenRC) en modo sólo-lectura —no destruye nada— y produce una Semilla. En el primer servidor absorbido, 11 de los 20 servicios envueltos quedaron promovidos a daemons supervisados de verdad.
Medido en metal, no en diapositivas
El proyecto tiene una regla: lo que no se midió en una máquina de verdad no se afirma. Esta tabla es el estado de arje donde más duele:
| dónde | qué se midió |
|---|---|
| laptop de uso diario | arranca todos los días por su propio menú-grafo UEFI; arje-zero como proceso 1 con el escritorio completo encima (compositor DRM, paneles, audio), jornadas enteras de uptime; suspensión real medida en 5,9 s; apagado y reinicio por el bus con su motivo registrado |
| servidor de producción | una Semilla de 26 Entes sosteniendo SSH, el servidor web y el git público; el apagado ordenado baja el árbol entero en cascada, hojas primero |
| FreeBSD 14.5 | ps -p 1 contesta arje-zero — no delegó: ES el init, con los mecanismos reescritos sobre kqueue, jails y devd. El testigo del apagado es el hipervisor, que ve la máquina «off»; el del reinicio, el reloj de arranque del kernel |
| dos nodos por Ethernet crudo | la resurrección completa: el nodo B pide por el cable los bloques del alma de A, los verifica contra sus hashes, y arranca ese sistema como proceso 1 |
| vigilancia dura | un perro guardián de hardware acaricia /dev/watchdog desde el bucle del proceso 1: si el init se cuelga, el kernel reinicia en vez de dejar la máquina muerta |
Y la honestidad del método incluye contar los golpes. El más instructivo no fue de arje: en un servidor con init clásico, reboot no hacía nada — ni error, ni mensaje. La herramienta escribía ocho bytes en un archivo de control que ningún proceso tenía abierto, y salía con éxito. El peor modo de fallar. Arje tuvo los suyos (un deadlock del proceso 1 que un comentario ya advertía; 2.466 zombies por una máscara de señales que execve no restaura), y cada uno terminó convertido en un test y en una línea de estas tablas. Ningún init es inocente; la diferencia es si sus muertes dejan autopsia.
Lo que arje tiene y systemd no
| capacidad | qué significa |
|---|---|
| atestación de arranque | firmas Ed25519 sobre el hash de cada binario crítico, con ancla soberana externa que vence a una Semilla reescrita |
| el sistema como commit | log · diff · checkout · bisect · blame sobre el estado del sistema operativo; rollback de cero bytes |
| caja negra del boot | cada arranque deja su vuelo firmado; arjectl autopsy explica el boot que murió, en tres idiomas |
| resurrección por el cable | un cuerpo nuevo se levanta pidiendo los bloques del alma por Ethernet crudo, sin TCP/IP ni servidor |
| re-floor | si el proveedor de una capacidad cae, sus dependientes no se descartan: quedan aparcados y se re-erigen solos en orden topológico cuando vuelve |
| escalada imposible por construcción | la identidad en el bus la inyecta el kernel; encarnar algo nuevo hereda los permisos de quien lo pide, nunca los del init |
| auditoría con cadena de hashes | quién pidió qué —spawns, kills, energía, cuotas— anclado al almacén de contenido, consultable |
| reglas con razonador | un cerebro declarativo («si este proceso pasa del 80 % de CPU 30 s, bajale el peso») que cristaliza reglas de patrones observados, con su propia auditoría |
Lo que decide no hacer
La otra mitad del diseño es la lista de lo que arje rechaza con razones escritas, porque un init que absorbe todo ya existe y es el problema. No hay enable/disable (la Semilla es el modelo), no hay drop-ins ni recarga en caliente de unidades (inmutable después del boot: menos superficie, más razonabilidad), no hay gestor de usuarios ni de particiones ni de contenedores dentro del init (eso es de otras piezas de la suite), no hay matador heurístico de procesos por memoria (hay límites y reglas con política explícita, no una heurística que decide por vos). Cada «no» está documentado con su porqué — y ésa es quizás la diferencia cultural más grande con el software que vino a reemplazar.
Lo que todavía no
- Un segundo monitor todavía parpadea. Arrancar con un monitor externo enchufado apaga el panel un instante —es el hardware, y el cargador lo disimula fundiendo al fondo—; y enchufarlo con la sesión abierta hoy cuesta cuatro parpadeos donde alcanzaría uno.
- El relevo sin parpadeo del splash al escritorio está certificado sobre gráficos Intel. AMD y el driver nuevo
xeno están verificados todavía; NVIDIA propietario queda explícitamente fuera del camino sin costuras. - El logind de compatibilidad modela una sesión única. Cambio de consola virtual, multi-sesión, idle y tapa están servidos a medias; es el hueco que más se siente en el escritorio y está nombrado como tal en el plan.
- No hay red declarativa ni reglas udev. arje trae un alzado de red mínimo y traduce los eventos de hardware a capacidades; una topología de red compleja hoy se resuelve con las herramientas de siempre.
- El linaje no versiona tus datos. Versiona el sistema — la Semilla, las unidades, los binarios—; prometer «viaje en el tiempo de todo» sería mentir, y el plan lo dice con esas palabras.
- La atestación arranca en modo aviso. Pasarla a modo estricto es una ceremonia del operador —generar su llave raíz y anclarla—, deliberadamente manual: estrenar atestación nunca debe dejar una máquina sin arranque.
- En FreeBSD, arje es el init, pero la paridad no es total: los límites de recursos que en Linux son cgroups allá se degradan con aviso, y quedan piezas nombradas por escribir.
Cómo verlo
Arje vive en la región raíz del repositorio de tawasuyu —git.tawasuyu.net, en un servidor propio— bajo 03_ukupacha/arje, y también como repositorio propio que compila solo: git.tawasuyu.net/tawasuyu/arje. El documento de migración, DE-SYSTEMD-A-ARJE.md, mapea verbo a verbo lo que sabés de systemd a lo que arje hace distinto; los planes y sus mediciones están al lado, con fechas y números, tal como salieron.