Files
fips/src/discovery.rs
T
Arjen 0aed417dbd feat(discovery): platform-pushed peer queue
Add a transport-agnostic seam for an embedding platform (e.g. an Android
Wi-Fi Aware radio) to push "peer npub reachable at addr over transport T"
events into a running node — the generalization of the UDP-only LAN mDNS
drain. A process-global queue (fips::discovery::platform) is drained each
tick by poll_platform_discovery, which selects the transport family-aware
(an IPv6 target picks an IPv6 socket) and initiates a Noise IK handshake;
the pushed npub is only a routing hint, the handshake authenticates. An
event for an already-active peer starts an alternate-path handshake, and
a Lost event closes the pooled connection.
2026-07-14 13:54:05 +02:00

106 lines
3.8 KiB
Rust

//! Bootstrap handoff types.
//!
//! These types model the boundary between an external rendezvous/bootstrap
//! runtime and the core FIPS transport/handshake stack. The rendezvous side
//! owns Nostr/STUN/UDP hole punching; once a direct UDP path is established,
//! it hands the live socket and selected remote endpoint to FIPS so the
//! existing Noise/FMP transport path can take over.
pub mod lan;
pub mod nostr;
pub mod platform;
use crate::config::UdpConfig;
use crate::{NodeAddr, TransportId};
use std::net::{SocketAddr, UdpSocket};
/// Punch-probe magic ("NPTC", network byte order). First byte `0x4E`
/// collides with FMP's prefix-version high-nibble check, so the UDP
/// transport silently filters packets carrying this magic to keep
/// post-adoption handshake logs clean. Defined at the top-level
/// `discovery` module so the UDP filter and the nostr submodule's
/// punch sender share the same constant.
pub const PUNCH_MAGIC: u32 = 0x4E505443;
/// Punch-probe-ack magic ("NPTA", network byte order). Same filter as
/// [`PUNCH_MAGIC`].
pub const PUNCH_ACK_MAGIC: u32 = 0x4E505441;
/// Returns `true` if the first four bytes of `data` match a punch-probe or
/// punch-ack magic. Used by the UDP transport's receive loop to silently
/// drop stray probes that arrive on an adopted socket after the remote
/// peer's punch attempt has already timed out.
pub fn is_punch_packet(data: &[u8]) -> bool {
if data.len() < 4 {
return false;
}
let magic = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
magic == PUNCH_MAGIC || magic == PUNCH_ACK_MAGIC
}
/// Result of handing an established traversal session into FIPS.
#[derive(Debug, Clone)]
pub struct BootstrapHandoffResult {
/// Newly allocated transport ID used for the adopted UDP socket.
pub transport_id: TransportId,
/// Local socket address now owned by the FIPS UDP transport.
pub local_addr: SocketAddr,
/// Confirmed remote UDP endpoint selected by traversal.
pub remote_addr: SocketAddr,
/// Peer node address derived from the supplied peer identity.
pub peer_node_addr: NodeAddr,
/// Nostr session identifier used by the bootstrap runtime.
pub session_id: String,
}
/// Established UDP traversal ready to be handed into FIPS.
///
/// The socket must already be bound and must be the same socket used for the
/// traversal runtime's STUN and punch traffic so the NAT mapping is preserved.
#[derive(Debug)]
pub struct EstablishedTraversal {
/// Rendezvous session identifier for logging/correlation.
pub session_id: String,
/// Remote peer identity in `npub` form.
pub peer_npub: String,
/// The selected remote UDP endpoint to use for the FIPS handshake.
pub remote_addr: SocketAddr,
/// The live UDP socket carrying the established mapping.
pub socket: UdpSocket,
/// Optional name for the adopted UDP transport.
pub transport_name: Option<String>,
/// Optional UDP transport tuning overrides.
pub transport_config: Option<UdpConfig>,
}
impl EstablishedTraversal {
/// Construct an established traversal handoff.
pub fn new(
session_id: impl Into<String>,
peer_npub: impl Into<String>,
remote_addr: SocketAddr,
socket: UdpSocket,
) -> Self {
Self {
session_id: session_id.into(),
peer_npub: peer_npub.into(),
remote_addr,
socket,
transport_name: None,
transport_config: None,
}
}
/// Attach an explicit transport name to the adopted UDP transport.
pub fn with_transport_name(mut self, name: impl Into<String>) -> Self {
self.transport_name = Some(name.into());
self
}
/// Override UDP transport tuning for the adopted socket.
pub fn with_transport_config(mut self, config: UdpConfig) -> Self {
self.transport_config = Some(config);
self
}
}