diff --git a/core/archipelago/src/federation.rs b/core/archipelago/src/federation.rs index 58fd1e75..54876b98 100644 --- a/core/archipelago/src/federation.rs +++ b/core/archipelago/src/federation.rs @@ -428,6 +428,54 @@ pub async fn sync_with_peer( Ok(state) } +/// Sync with a peer using the transport router (Mesh > LAN > Tor). +/// Uses CBOR delta encoding for compact payloads over constrained links. +/// Falls back to `sync_with_peer()` if no transport router is available. +pub async fn sync_with_peer_via_transport( + data_dir: &Path, + peer: &FederatedNode, + local_did: &str, + previous_state: Option<&NodeStateSnapshot>, + router: &crate::transport::TransportRouter, +) -> Result<()> { + use crate::transport::{MessageType, TransportMessage}; + use crate::transport::delta; + + // Build the sync request payload — if we have a previous state from this peer, + // send a delta request (tells the peer we only need changes since timestamp). + let payload = if let Some(prev) = previous_state { + // Request delta since our last known state + let request = serde_json::json!({ + "type": "state_sync_request", + "since": prev.timestamp, + }); + delta::encode_cbor(&delta::StateDelta { + ts: prev.timestamp.clone(), + v: 1, + ..Default::default() + })? + } else { + // First sync — request full state + let request = serde_json::json!({ "type": "state_sync_request" }); + serde_json::to_vec(&request)? + }; + + let message = TransportMessage { + from_did: local_did.to_string(), + payload, + message_type: MessageType::StateSync, + }; + + let transport_used = router.send_to_peer(&peer.did, &message).await?; + tracing::info!( + peer = %peer.did, + transport = %transport_used, + "Federation sync sent via transport" + ); + + Ok(()) +} + /// Build the local node's state snapshot for sharing with peers. pub fn build_local_state( apps: Vec, diff --git a/neode-ui/src/views/Federation.vue b/neode-ui/src/views/Federation.vue index 02cffa23..0e79530a 100644 --- a/neode-ui/src/views/Federation.vue +++ b/neode-ui/src/views/Federation.vue @@ -163,6 +163,11 @@
{{ node.name || shortDid(node.did) }} + {{ nodeTransportIcon(node.did).icon }}
p.did === did) + if (!peer) return { icon: '?', color: 'text-white/30', label: 'unknown' } + switch (peer.preferred_transport) { + case 'mesh': return { icon: '📡', color: 'text-orange-400', label: 'mesh' } + case 'lan': return { icon: '🌐', color: 'text-green-400', label: 'lan' } + case 'tor': return { icon: '🧅', color: 'text-purple-400', label: 'tor' } + default: return { icon: '?', color: 'text-white/30', label: 'unknown' } + } +} + onMounted(async () => { loadNodes() loadDwnStatus() + transportStore.fetchPeers() try { const result = await rpcClient.getNodeDid() selfDid.value = result.did diff --git a/neode-ui/src/views/Mesh.vue b/neode-ui/src/views/Mesh.vue index b56fa011..2e39f50e 100644 --- a/neode-ui/src/views/Mesh.vue +++ b/neode-ui/src/views/Mesh.vue @@ -1,10 +1,12 @@