use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use agent_mesh_protocol::{Fingerprint, SignedEnvelope};
use async_trait::async_trait;
use tokio::sync::mpsc;
use tokio::sync::Mutex as AsyncMutex;
use crate::bus::PeerEndpoint;
use crate::{BusError, Result};
pub type ReplyRoute = Arc<dyn Any + Send + Sync>;
pub struct Inbound {
pub envelope: SignedEnvelope,
pub reply_route: ReplyRoute,
}
#[async_trait]
pub trait Transport: Send + Sync {
async fn send_to(&self, fp: Fingerprint, env: SignedEnvelope) -> Result<()>;
async fn send_to_endpoint(&self, peer: &PeerEndpoint, env: SignedEnvelope) -> Result<()>;
async fn reply(&self, fp: Fingerprint, route: &ReplyRoute, env: SignedEnvelope) -> Result<()>;
async fn recv(&self) -> Option<Inbound>;
fn local_port(&self) -> u16;
async fn close(&self);
}
#[derive(Default)]
pub struct MeshNet {
peers: Mutex<HashMap<Fingerprint, mpsc::UnboundedSender<Inbound>>>,
}
impl MeshNet {
#[must_use]
pub fn new() -> Arc<Self> {
Arc::new(Self::default())
}
pub fn transport_for(self: &Arc<Self>, me: Fingerprint) -> InMemoryTransport {
let (tx, rx) = mpsc::unbounded_channel();
self.peers.lock().unwrap().insert(me, tx);
InMemoryTransport {
me,
net: Arc::clone(self),
inbound: AsyncMutex::new(rx),
}
}
fn deliver(&self, to: Fingerprint, inbound: Inbound) -> Result<()> {
let peers = self.peers.lock().unwrap();
match peers.get(&to) {
Some(tx) => tx.send(inbound).map_err(|_| {
BusError::Unreachable(format!("in-memory peer {} has closed", to.short()))
}),
None => Err(BusError::Unreachable(format!(
"in-memory peer {} is not on the mesh",
to.short()
))),
}
}
fn remove(&self, me: &Fingerprint) {
self.peers.lock().unwrap().remove(me);
}
}
pub struct InMemoryTransport {
me: Fingerprint,
net: Arc<MeshNet>,
inbound: AsyncMutex<mpsc::UnboundedReceiver<Inbound>>,
}
impl InMemoryTransport {
fn push(&self, to: Fingerprint, env: SignedEnvelope) -> Result<()> {
self.net.deliver(
to,
Inbound {
envelope: env,
reply_route: Arc::new(self.me),
},
)
}
}
#[async_trait]
impl Transport for InMemoryTransport {
async fn send_to(&self, fp: Fingerprint, env: SignedEnvelope) -> Result<()> {
self.push(fp, env)
}
async fn send_to_endpoint(&self, peer: &PeerEndpoint, env: SignedEnvelope) -> Result<()> {
self.push(peer.fingerprint(), env)
}
async fn reply(&self, fp: Fingerprint, _route: &ReplyRoute, env: SignedEnvelope) -> Result<()> {
self.push(fp, env)
}
async fn recv(&self) -> Option<Inbound> {
self.inbound.lock().await.recv().await
}
fn local_port(&self) -> u16 {
0
}
async fn close(&self) {
self.net.remove(&self.me);
}
}
#[cfg(test)]
mod tests {
use super::*;
use agent_mesh_protocol::{AgentKey, AgentMetadata, Caveats, Recipient, UserKey};
fn agent(user: &UserKey, role: &str) -> AgentKey {
AgentKey::issue(
user,
AgentMetadata {
role: role.into(),
host: "test".into(),
capabilities: vec!["test".into()],
issued_at: "2026-05-28T00:00:00Z".into(),
expires_at: None,
caveats: Caveats::top(),
},
)
}
fn envelope(from: &AgentKey, to: Fingerprint, body: &[u8]) -> SignedEnvelope {
SignedEnvelope::new(from, Recipient::Direct { agent_fp: to }, 1, body.to_vec())
}
#[tokio::test]
async fn send_delivers_and_reply_route_round_trips() {
let user = UserKey::generate();
let a = agent(&user, "a");
let b = agent(&user, "b");
let (a_fp, b_fp) = (a.fingerprint(), b.fingerprint());
let net = MeshNet::new();
let ta = net.transport_for(a_fp);
let tb = net.transport_for(b_fp);
ta.send_to(b_fp, envelope(&a, b_fp, b"hi")).await.unwrap();
let inbound = tb.recv().await.expect("b receives");
assert_eq!(inbound.envelope.sender_agent_fp(), a_fp);
tb.reply(a_fp, &inbound.reply_route, envelope(&b, a_fp, b"yo"))
.await
.unwrap();
let back = ta.recv().await.expect("a receives reply");
assert_eq!(back.envelope.sender_agent_fp(), b_fp);
}
#[tokio::test]
async fn send_to_endpoint_routes_by_fingerprint() {
let user = UserKey::generate();
let a = agent(&user, "a");
let b = agent(&user, "b");
let (a_fp, b_fp) = (a.fingerprint(), b.fingerprint());
let net = MeshNet::new();
let ta = net.transport_for(a_fp);
let tb = net.transport_for(b_fp);
let peer = PeerEndpoint::new(b.public_bytes(), "127.0.0.1:1".parse().unwrap());
ta.send_to_endpoint(&peer, envelope(&a, b_fp, b"hi"))
.await
.unwrap();
assert!(tb.recv().await.is_some(), "endpoint send reaches b");
}
#[tokio::test]
async fn send_to_unregistered_peer_is_unreachable() {
let user = UserKey::generate();
let a = agent(&user, "a");
let a_fp = a.fingerprint();
let net = MeshNet::new();
let ta = net.transport_for(a_fp);
let phantom = Fingerprint([0x11u8; 32]);
match ta.send_to(phantom, envelope(&a, phantom, b"x")).await {
Err(BusError::Unreachable(_)) => {}
other => panic!("expected Unreachable, got {other:?}"),
}
assert_eq!(ta.local_port(), 0);
}
#[tokio::test]
async fn a_closed_peer_no_longer_receives() {
let user = UserKey::generate();
let a = agent(&user, "a");
let b = agent(&user, "b");
let (a_fp, b_fp) = (a.fingerprint(), b.fingerprint());
let net = MeshNet::new();
let ta = net.transport_for(a_fp);
let tb = net.transport_for(b_fp);
tb.close().await;
match ta.send_to(b_fp, envelope(&a, b_fp, b"x")).await {
Err(BusError::Unreachable(_)) => {}
other => panic!("expected Unreachable after close, got {other:?}"),
}
}
}