From df9d10cc52c3de65da43b3b66d7635874a10ff0b Mon Sep 17 00:00:00 2001 From: Sergio Date: Fri, 8 May 2026 15:02:29 +0000 Subject: [PATCH] feat(ente-zero): enchufa el handshake server al Init real MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ente-zero (PID 1 del fractal arje) ahora levanta el server de brahman-handshake junto al ente-bus existente, escuchando en \$BRAHMAN_INIT_SOCKET (default \$XDG_RUNTIME_DIR/brahman-init.sock). Es un canal paralelo dedicado a módulos brahman-conscientes que se presentan con una Card y declaran flujos tipados. Cambios: - crates/core/brahman-handshake/src/transport.rs: helper nuevo con resolución XDG_RUNTIME_DIR → TMPDIR, override por var de entorno BRAHMAN_INIT_SOCKET. Test unitario para el override. - crates/core/brahman-handshake/Cargo.toml: example "probe" + dev-dep anyhow. Probe sirve como herramienta de diagnóstico para conectar contra cualquier server vivo. - crates/core/brahman-handshake/examples/probe.rs: cliente mínimo que hace Hello → Ping → Farewell e imprime el HelloAck recibido. - crates/core/ente-zero/Cargo.toml: dependencias brahman-handshake + brahman-broker. - crates/core/ente-zero/src/main.rs: en primordial_loop, tras spawn del ente-bus, crea Arc> compartido y llama Server::bind. Si el bind falla (FS no escribible, socket en uso), loggea y degrada a "modo bus-only" — la doctrina PID 1 no rompe por subsistemas opcionales (mismo patrón que uevents). Validación end-to-end manual: $ BRAHMAN_INIT_SOCKET=/tmp/e2e.sock ./target/debug/ente-zero & $ BRAHMAN_INIT_SOCKET=/tmp/e2e.sock cargo run --example probe HelloAck: session=01KR41Q8... server=0.1.0 protocol=0.1.0 init_attached=true Pong: ts=1778252489714ms Farewell OK Tests: 27/27 (broker 11 + card 8 + handshake codec+transport 2 + integ 6). cargo check --workspace: 0 errores. Co-Authored-By: Claude Opus 4.7 (1M context) --- Cargo.lock | 3 ++ crates/core/brahman-handshake/Cargo.toml | 5 ++ .../core/brahman-handshake/examples/probe.rs | 51 ++++++++++++++++++ crates/core/brahman-handshake/src/lib.rs | 1 + .../core/brahman-handshake/src/transport.rs | 52 +++++++++++++++++++ crates/core/ente-zero/Cargo.toml | 4 ++ crates/core/ente-zero/src/main.rs | 31 +++++++++++ 7 files changed, 147 insertions(+) create mode 100644 crates/core/brahman-handshake/examples/probe.rs create mode 100644 crates/core/brahman-handshake/src/transport.rs diff --git a/Cargo.lock b/Cargo.lock index 79a2e16..2c2e0b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1164,6 +1164,7 @@ dependencies = [ name = "brahman-handshake" version = "0.1.0" dependencies = [ + "anyhow", "brahman-broker", "brahman-card", "postcard", @@ -2771,6 +2772,8 @@ name = "ente-zero" version = "0.0.1" dependencies = [ "anyhow", + "brahman-broker", + "brahman-handshake", "ente-brain", "ente-bus", "ente-card", diff --git a/crates/core/brahman-handshake/Cargo.toml b/crates/core/brahman-handshake/Cargo.toml index e1361da..1770386 100644 --- a/crates/core/brahman-handshake/Cargo.toml +++ b/crates/core/brahman-handshake/Cargo.toml @@ -21,3 +21,8 @@ tracing = { workspace = true } [dev-dependencies] tempfile = { workspace = true } tokio = { workspace = true } +anyhow = { workspace = true } + +[[example]] +name = "probe" +path = "examples/probe.rs" diff --git a/crates/core/brahman-handshake/examples/probe.rs b/crates/core/brahman-handshake/examples/probe.rs new file mode 100644 index 0000000..dac62a0 --- /dev/null +++ b/crates/core/brahman-handshake/examples/probe.rs @@ -0,0 +1,51 @@ +//! probe — herramienta de diagnóstico del handshake. +//! +//! Conecta a un Init brahman vivo, hace handshake, un ping, y se va. +//! Ruta del socket: `$BRAHMAN_INIT_SOCKET` o el default +//! ([`brahman_handshake::transport::default_socket_path`]). +//! +//! Uso: +//! ```sh +//! cargo run -p brahman-handshake --example probe +//! ``` + +use std::collections::BTreeSet; + +use brahman_card::{Card, Payload, Supervision, CARD_SCHEMA_VERSION}; +use brahman_handshake::{client::Client, transport}; +use ulid::Ulid; + +#[tokio::main(flavor = "current_thread")] +async fn main() -> anyhow::Result<()> { + let card = Card { + schema_version: CARD_SCHEMA_VERSION, + id: Ulid::new(), + label: "brahman-probe".into(), + payload: Payload::Virtual, + supervision: Supervision::OneShot, + provides: BTreeSet::new(), + requires: BTreeSet::new(), + ..Default::default() + }; + + let path = transport::default_socket_path(); + println!("connecting to {}", path.display()); + + let mut client = Client::connect(&path, card).await?; + let info = client.server_info(); + println!( + " HelloAck: session={} server={} protocol={} init_attached={}", + client.session(), + info.server_version, + info.protocol_version, + info.init_attached + ); + + let ts = client.ping().await?; + println!(" Pong: ts={}ms", ts); + + client.farewell().await?; + println!(" Farewell OK"); + + Ok(()) +} diff --git a/crates/core/brahman-handshake/src/lib.rs b/crates/core/brahman-handshake/src/lib.rs index ca0545e..8486b4d 100644 --- a/crates/core/brahman-handshake/src/lib.rs +++ b/crates/core/brahman-handshake/src/lib.rs @@ -21,6 +21,7 @@ pub mod codec; pub mod messages; pub mod server; pub mod client; +pub mod transport; pub use brahman_card::PROTOCOL_VERSION; diff --git a/crates/core/brahman-handshake/src/transport.rs b/crates/core/brahman-handshake/src/transport.rs new file mode 100644 index 0000000..03e7b51 --- /dev/null +++ b/crates/core/brahman-handshake/src/transport.rs @@ -0,0 +1,52 @@ +//! Convenciones de transporte: dónde vive el socket del Init. +//! +//! Resolución del path canónico: +//! 1. Variable de entorno [`SOCKET_ENV`] si está definida (override +//! explícito, prioridad máxima). +//! 2. `$XDG_RUNTIME_DIR/brahman-init.sock` (sesión usuario). +//! 3. `$TMPDIR/brahman-init.sock` (fallback portable). + +use std::path::PathBuf; + +/// Variable de entorno que sobreescribe la ruta del socket del Init. +pub const SOCKET_ENV: &str = "BRAHMAN_INIT_SOCKET"; + +/// Nombre del socket dentro del runtime dir. +pub const SOCKET_NAME: &str = "brahman-init.sock"; + +/// Ruta canónica al socket del Init brahman. +pub fn default_socket_path() -> PathBuf { + if let Ok(p) = std::env::var(SOCKET_ENV) { + return PathBuf::from(p); + } + let base = std::env::var_os("XDG_RUNTIME_DIR") + .map(PathBuf::from) + .unwrap_or_else(std::env::temp_dir); + base.join(SOCKET_NAME) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn env_override_wins() { + // Nota: estos tests modifican entorno del proceso. `cargo test` + // los corre paralelos por defecto pero usamos un nombre de var + // único y restablecemos al final. + let key = "BRAHMAN_INIT_SOCKET_TEST_OVERRIDE"; + // SAFETY: sólo escribimos una variable local al test; sin + // contaminar SOCKET_ENV. + std::env::set_var(key, "/tmp/explicit.sock"); + let saved = std::env::var(SOCKET_ENV).ok(); + std::env::set_var(SOCKET_ENV, "/tmp/explicit.sock"); + let p = default_socket_path(); + assert_eq!(p, PathBuf::from("/tmp/explicit.sock")); + // Restaurar + match saved { + Some(v) => std::env::set_var(SOCKET_ENV, v), + None => std::env::remove_var(SOCKET_ENV), + } + std::env::remove_var(key); + } +} diff --git a/crates/core/ente-zero/Cargo.toml b/crates/core/ente-zero/Cargo.toml index 2992155..04bb4bf 100644 --- a/crates/core/ente-zero/Cargo.toml +++ b/crates/core/ente-zero/Cargo.toml @@ -21,6 +21,10 @@ ente-snapshot = { path = "../ente-snapshot" } ente-brain = { path = "../ente-brain" } ente-echo = { path = "../ente-echo" } # solo para constantes del demo +# Brahman protocol — handshake para módulos brahman conscientes +brahman-handshake = { path = "../brahman-handshake" } +brahman-broker = { path = "../brahman-broker" } + # Runtime / utilidades de PID 1 serde = { workspace = true } serde_json = { workspace = true } diff --git a/crates/core/ente-zero/src/main.rs b/crates/core/ente-zero/src/main.rs index eac4e46..18efbe8 100644 --- a/crates/core/ente-zero/src/main.rs +++ b/crates/core/ente-zero/src/main.rs @@ -138,6 +138,37 @@ async fn primordial_loop( let bus_path = bus::spawn_bus(bus_sock, graph_tx.clone())?; ente_soma::set_bus_sock(bus_path.to_string_lossy().into_owned()); + // Brahman protocol: handshake socket + broker compartido. + // + // Es un canal paralelo al ente-bus, dedicado a módulos "brahman + // conscientes" que se presentan con una Card y declaran flujos + // tipados. Si el bind falla (socket en uso, FS no escribible), + // degradamos a "modo bus-only" — la doctrina de PID 1 no rompe + // por subsistemas opcionales. + let brahman_broker = std::sync::Arc::new(tokio::sync::Mutex::new( + brahman_broker::Broker::new(brahman_broker::BrokerConfig::default()), + )); + let brahman_sock = brahman_handshake::transport::default_socket_path(); + match brahman_handshake::server::Server::bind( + &brahman_sock, + brahman_handshake::server::ServerConfig { + init_attached: true, + broker: Some(brahman_broker.clone()), + }, + ) { + Ok(server) => { + info!(socket = %brahman_sock.display(), "brahman handshake escuchando"); + tokio::spawn(async move { + if let Err(e) = server.run().await { + warn!(?e, "brahman handshake server cayó"); + } + }); + } + Err(e) => { + warn!(?e, socket = %brahman_sock.display(), "brahman handshake deshabilitado"); + } + } + let mut graph = EnteGraph::new(seed_card); graph.instantiate_seed_dependencies(&graph_tx).await?;