# Changelog — init/ Init (PID 1) + encarnación Linux. Antes: `core/ente-{zero,kernel,soma,snapshot}` + `shared/ente-incarnate`. ### feat(ente-zero): wire de Arje con brahman-net (red P2P opcional + identidad persistente) Cierra el último pendiente del plan de red: Arje ahora puede arrancar opcionalmente con `BrahmanNet` configurado, persistir su identidad libp2p entre reboots, y participar en la malla brahman como nodo público. Sin breaking changes: usuarios actuales (sin env vars) siguen viendo el comportamiento Unix-only de antes. Activación por env vars: - **`BRAHMAN_LISTEN_MULTIADDR`** — si set, activa la red P2P. Ej: `/ip4/0.0.0.0/tcp/4101` (público), `/ip4/127.0.0.1/tcp/0` (loopback, port aleatorio). Sin la var, `brahman_net = None` y todo sigue como antes. - **`BRAHMAN_KEYPAIR_PATH`** — override del path donde se persiste la keypair Ed25519 de identidad libp2p del nodo. Defaults sensatos: - PID 1 (root): `/var/lib/brahman/init-keypair.bin`. - Dev mode: `$XDG_DATA_HOME/brahman/init-keypair.bin` → `$HOME/.local/share/brahman/init-keypair.bin` → `/tmp/brahman-init-keypair.bin` (último recurso). - **`BRAHMAN_BOOTSTRAP_PEERS`** — lista coma-separada de multiaddrs para dial-ear al arranque y entrar al DHT. Sin esto, el nodo arranca aislado hasta que alguien se conecte a él. Comportamiento al activarse: 1. `keypair_store::load_or_generate(path)` carga la keypair de disco o genera+persiste una nueva (32 bytes raw, permisos 0o600, atomic rename). Reboots conservan el `peer_id`. 2. `BrahmanNet::with_keypair(kp)` arma el swarm con esa identidad. 3. `net.listen(multiaddr)` espera dirección resuelta y la loggea. 4. `BRAHMAN_BOOTSTRAP_PEERS` (si set) → dial a cada multiaddr. 5. El handshake server se levanta con `ServerConfig.net = Some(net)`, que activa `announce_outputs` automático en el DHT por cada Card con outputs. 6. Además del Unix accept loop (existing), se monta un libp2p accept loop sobre el mismo `Server` compartido. Sesiones locales y remotas conviven en las mismas tablas (sessions, push_table, broker, last_matches). Refactor del Unix accept loop: antes consumía el server vía `server.run().await`; ahora usa `Arc::accept_one().await` en loop para coexistir con el libp2p accept loop sin moverse el server. Degradación grácil en cada paso: si la keypair no carga, si el multiaddr es inválido, si el listen falla, si el bootstrap dial revienta — loggeamos y seguimos en modo Unix-only. La doctrina de PID 1 ("ningún subsistema opcional rompe el bucle primordial") se mantiene. Tests: 4 unit en `keypair_store`: - `generate_persist_and_reload_yields_same_peer_id` — peer_id estable across reloads (la propiedad fundamental). - `rejects_corrupted_file` — archivo de tamaño incorrecto rechazado. - `persisted_file_is_owner_only` — permisos 0o600 verificados. - `default_path_honors_env` — `BRAHMAN_KEYPAIR_PATH` override respeta tanto dev como root mode. Ente-zero compila clean. Ningún test del workspace regresa. Lo que esto desbloquea hoy: - Para activar Arje como nodo público, basta: ```sh BRAHMAN_LISTEN_MULTIADDR=/ip4/0.0.0.0/tcp/4101 ente-zero ``` - Cualquier consumer (en otra máquina) puede luego dial-ar a ese multiaddr + descubrir Cards anunciadas via DHT + abrir handshake remoto firmado. - La identidad del nodo (su `peer_id`) sobrevive reboots, así que los nodos remotos pueden cachear "este peer_id es Arje en máquina X" sin invalidarse cada vez. Pendientes futuros: - `stop_providing` al cleanup de sesión (records DHT con TTL ~24h). - Allowlist/Denylist de peers (PKI explícito). - Rotación de keypair sin perder peer_id (multi-key identity).