# 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<Server>::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).

