control: add iroh gossip transport announcements and ec-node control CLI

This commit is contained in:
every.channel 2026-02-22 01:57:20 -08:00
parent fba1f3a7d5
commit fe97623ba8
No known key found for this signature in database
5 changed files with 494 additions and 23 deletions

View file

@ -2,7 +2,7 @@
use anyhow::{Context, Result};
use bytes::Bytes;
use ec_core::StreamCatalogEntry;
use ec_core::{StreamCatalogEntry, StreamControlAnnouncement};
use futures_lite::StreamExt;
use iroh::address_lookup::{
DhtAddressLookup, DiscoveryEvent, DnsAddressLookup, MdnsAddressLookup, PkarrPublisher, UserData,
@ -23,6 +23,7 @@ use std::time::{Duration, Instant};
pub const ALPN_MOQ: &[u8] = b"every.channel/moq/0";
pub const DEFAULT_CATALOG_TOPIC: &str = "every.channel/catalog/v1";
pub const DEFAULT_CONTROL_TOPIC: &str = "every.channel/control/v1";
pub const MDNS_USER_DATA: &str = "every.channel";
#[derive(Debug, Clone)]
@ -176,6 +177,11 @@ pub fn catalog_topic() -> TopicId {
TopicId::from_bytes(*hash.as_bytes())
}
pub fn control_topic() -> TopicId {
let hash = blake3::hash(DEFAULT_CONTROL_TOPIC.as_bytes());
TopicId::from_bytes(*hash.as_bytes())
}
pub fn parse_endpoint_addr(value: &str) -> Result<EndpointAddr> {
let value = value.trim();
if value.starts_with('{') {
@ -326,3 +332,75 @@ impl CatalogGossip {
}
}
}
#[derive(Debug)]
pub struct ControlGossip {
sender: GossipSender,
receiver: GossipReceiver,
_router: Router,
_gossip: Gossip,
_memory_lookup: MemoryLookup,
}
impl ControlGossip {
pub async fn join(endpoint: Endpoint, peers: &[String]) -> Result<Self> {
let memory_lookup = MemoryLookup::new();
endpoint.address_lookup().add(memory_lookup.clone());
let gossip = Gossip::builder().spawn(endpoint.clone());
let router = Router::builder(endpoint.clone())
.accept(GOSSIP_ALPN, gossip.clone())
.spawn();
let peer_addrs = peers
.iter()
.map(|peer| parse_endpoint_addr(peer))
.collect::<Result<Vec<_>, _>>()
.context("failed to parse control peer addr")?;
for peer in &peer_addrs {
memory_lookup.add_endpoint_info(peer.clone());
}
let peer_ids = peer_addrs
.iter()
.map(|addr| addr.id)
.collect::<Vec<PublicKey>>();
let (sender, receiver) = gossip
.subscribe_and_join(control_topic(), peer_ids)
.await?
.split();
Ok(Self {
sender,
receiver,
_router: router,
_gossip: gossip,
_memory_lookup: memory_lookup,
})
}
pub async fn announce(&mut self, announcement: StreamControlAnnouncement) -> Result<()> {
let bytes = serde_json::to_vec(&announcement)?;
self.sender.broadcast(Bytes::from(bytes)).await?;
Ok(())
}
pub async fn next_announcement(&mut self) -> Result<Option<StreamControlAnnouncement>> {
while let Some(event) = self.receiver.try_next().await? {
if let Event::Received(msg) = event {
if let Ok(announcement) =
serde_json::from_slice::<StreamControlAnnouncement>(&msg.content)
{
return Ok(Some(announcement));
}
}
}
Ok(None)
}
pub fn add_peers(&self, peers: Vec<EndpointAddr>) {
for peer in peers {
self._memory_lookup.add_endpoint_info(peer);
}
}
}