Files
brahman/crates/protocol/brahman-dht/src/lib.rs
T
sergio 27603c906d feat(brahman-dht): B3 — discovery typed sobre el Kademlia compartido
brahman-net corre un único Kademlia para todo el ecosistema.
brahman-dht le pone arriba un esquema de claves namespaced para que
distintos dominios coexistan sin colisión en la misma malla.

- key — RecordKind (Code/Card/Persona/Service/Custom) + DhtKey.
  Wire: [kind_tag] ++ blake3(id) = 33 bytes longitud fija. Custom(n)
  usa 0x80|n: nunca choca con los kinds estándar.
- Dht — wrapper sobre BrahmanNet: announce/withdraw/find (modelo de
  provider records).

Consumidores: minga (Code), brahman-card-discovery (Card), agorapura
(Persona). 5 tests verdes (incl. smoke async sobre un nodo libp2p real).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 15:11:40 +00:00

73 lines
2.2 KiB
Rust

//! `brahman-dht` — capa de discovery typed sobre el Kademlia compartido.
//!
//! `brahman-net` corre un único Kademlia para todo el ecosistema. Este
//! crate le pone arriba un esquema de claves namespaced ([`DhtKey`]):
//! `minga` publica bloques de código, `brahman-card-discovery` publica
//! Cards, `agorapura` publica Personas — todo sobre la misma malla sin
//! colisión, porque cada clave lleva un byte de `kind`.
//!
//! El modelo es de **provider records**: un nodo `announce`-a que provee
//! una clave; otros `find`-an quién la provee y abren un stream directo.
#![forbid(unsafe_code)]
pub mod key;
pub use key::{DhtKey, RecordKind, DHT_KEY_LEN};
use brahman_net::BrahmanNet;
use libp2p::PeerId;
use std::sync::Arc;
/// Discovery typed sobre `brahman-net`.
#[derive(Clone)]
pub struct Dht {
net: Arc<BrahmanNet>,
}
impl Dht {
/// Crea la capa DHT sobre un nodo `brahman-net` ya inicializado.
pub fn new(net: Arc<BrahmanNet>) -> Self {
Self { net }
}
/// Anuncia que este nodo provee `key`. El registro de provider se
/// renueva solo mientras el nodo siga vivo en la malla.
pub fn announce(&self, key: &DhtKey) {
self.net.start_providing(&key.to_bytes());
}
/// Retira el anuncio de `key`.
pub fn withdraw(&self, key: &DhtKey) {
self.net.stop_providing(&key.to_bytes());
}
/// Busca los peers que proveen `key`.
pub async fn find(&self, key: &DhtKey) -> Vec<PeerId> {
self.net.find_providers(&key.to_bytes()).await
}
/// El nodo `brahman-net` subyacente (para abrir streams a un provider).
pub fn net(&self) -> &Arc<BrahmanNet> {
&self.net
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn announce_find_withdraw_on_a_live_node() {
// Smoke: un nodo solo. `find` de una clave que nadie provee
// devuelve vacío; announce/withdraw no panickean.
let net = Arc::new(BrahmanNet::new().expect("nodo libp2p"));
let dht = Dht::new(net);
let key = DhtKey::card("modulo-inexistente");
dht.announce(&key);
dht.withdraw(&key);
let found = dht.find(&DhtKey::card("nadie-lo-provee")).await;
assert!(found.is_empty());
}
}