feat(brahman-handshake): Fase 2 — discovery remoto via DHT por flow type
Tercer paso del plan "el encuentro entre Entes no se restringe a
local". Cuando un Init local acepta una sesion cuya Card declara
outputs, anuncia al DHT (Kademlia, via brahman-net) que el provee
esos flow types. Cualquier nodo conectado al mismo DHT puede
consultar y obtener la lista de PeerId's que sirven el flow.
API nueva en brahman_handshake::network:
- flow_dht_key(flow_name, type_ref) -> [u8; 32]: blake3 hash de
"brahman-flow|v1|{flow}|{type_canon}". Determinista cross-host.
Cambiar la canonicalizacion rompe compatibilidad — el prefijo v1
documenta version del esquema y obliga a bump al modificar.
- announce_outputs(net, card): start_providing por cada flow.output.
Idempotente, fire-and-forget.
- find_remote_providers(net, flow_name, type_ref) -> Vec<PeerId>:
query DHT. Lista vacia si nadie anuncia.
Wire en el server:
- ServerConfig gana pub net: Option<Arc<BrahmanNet>>. Si esta set,
cada Card registrada con outputs se anuncia automaticamente al DHT
desde register_session. None = server "ciego al DHT".
- Debug manual de ServerConfig (BrahmanNet no es Debug).
Canonicalizacion del TypeRef:
- Primitive { name } -> "prim:{name}"
- Wit { package, interface, name } -> "wit:{package}#{interface_or_empty}#{name}"
Tests: 2 nuevos en tests/network_discovery.rs:
- dht_discovery_finds_remote_provider: 2 nodos, A registra Card con
flow.output = monad-list:json, B dial-ea a A, B llama
find_remote_providers y descubre el peer_id de A.
- dht_discovery_negative_unknown_flow: B busca flow inexistente,
devuelve [] sin colgarse.
Callers actualizados con net: None: tests existentes + ente-zero
(arje aun no expone red; pasar Some(Arc<BrahmanNet>) cuando quiera
publicar al DHT remoto).
Lo que esto desbloquea: un nouser daemon en maquina A puede ser
descubierto por nouser-explorer en maquina B sin conocimiento previo
del peer — solo necesitan compartir DHT (via bootstrap inicial).
Pendiente para Fase 3: trust (firma Ed25519 en Cards remotas) +
stop_providing al cleanup de sesion.
This commit is contained in:
@@ -7,6 +7,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use brahman_broker::{Broker, Endpoint};
|
||||
use brahman_card::{Card, ResolvedCard, WitInterface, CARD_SCHEMA_VERSION};
|
||||
use brahman_net::BrahmanNet;
|
||||
use tokio::io::{split, AsyncRead, AsyncWrite, WriteHalf};
|
||||
use tokio::net::{UnixListener, UnixStream};
|
||||
use tokio::sync::{mpsc, Mutex};
|
||||
@@ -39,7 +40,7 @@ type LastMatches = Arc<Mutex<HashMap<SessionId, HashMap<String, Endpoint>>>>;
|
||||
const PUSH_CHANNEL_CAPACITY: usize = 32;
|
||||
|
||||
/// Configuración del servidor.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct ServerConfig {
|
||||
/// `true` si el Init está atado al servidor (se reporta en `HelloAck`).
|
||||
pub init_attached: bool,
|
||||
@@ -47,6 +48,27 @@ pub struct ServerConfig {
|
||||
/// `register` tras un Hello aceptado y `unregister` al cerrar la
|
||||
/// sesión (Farewell o EOF). Si es `None`, el broker no se usa.
|
||||
pub broker: Option<SharedBroker>,
|
||||
/// Capa P2P compartida. Si está presente, cada Card registrada
|
||||
/// con outputs se anuncia automáticamente al DHT vía
|
||||
/// [`brahman_handshake::network::announce_outputs`], permitiendo
|
||||
/// que un consumer remoto los descubra con
|
||||
/// [`brahman_handshake::network::find_remote_providers`]. Si es
|
||||
/// `None`, el server queda "ciego al DHT" — sólo matchea sesiones
|
||||
/// locales (lo cual es correcto cuando no hay conectividad o no
|
||||
/// se desea exponer al exterior).
|
||||
pub net: Option<Arc<BrahmanNet>>,
|
||||
}
|
||||
|
||||
// Manual Debug porque BrahmanNet no implementa Debug (libp2p Swarm
|
||||
// no es Debug). Sólo loggeamos los campos relevantes para tracing.
|
||||
impl std::fmt::Debug for ServerConfig {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ServerConfig")
|
||||
.field("init_attached", &self.init_attached)
|
||||
.field("broker", &self.broker.as_ref().map(|_| "<broker>"))
|
||||
.field("net", &self.net.as_ref().map(|_| "<net>"))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Servidor de handshake escuchando en un Unix socket.
|
||||
@@ -508,6 +530,12 @@ async fn register_session(
|
||||
.await
|
||||
.register(session_id, &card, wit.clone());
|
||||
}
|
||||
// Si el server tiene net configurado, anunciar los outputs al
|
||||
// DHT para que peers remotos puedan descubrirlos. Idempotente
|
||||
// y best-effort — fallos de Kad no propagan al handshake.
|
||||
if let Some(net) = &config.net {
|
||||
crate::network::announce_outputs(net, &card);
|
||||
}
|
||||
let resolved = match wit {
|
||||
Some(w) => ResolvedCard::from_conscious(card, w),
|
||||
None => ResolvedCard::from_agnostic(card),
|
||||
|
||||
Reference in New Issue
Block a user