tawasuyu

tawasuyu/brahman

Rust 332TOML 127JSON 17Markdown 154710 Sutichaykuna

feat(ente-zero): enchufa el handshake server al Init real

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<Mutex<Broker>> 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) <noreply@anthropic.com>

df9d10cc52c3de65da43b3b66d7635874a10ff0b · ruwaq Sergio · (5 killa ñawpaq) · tayta 07d77a335 · kay commitpi sach'ata qhaway

Semántico sayk'uy · 8

crates/core/brahman-handshake/examples/probe.rs

crates/core/brahman-handshake/src/lib.rs

crates/core/brahman-handshake/src/transport.rs

crates/core/ente-zero/src/main.rs

7 qillqakuna · +147 −0

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?;