#[derive(Debug, Clone)]
struct FipsEndpointTransportConfig {
listen_port: u16,
advertised_endpoint: String,
advertise_public_endpoint: bool,
nostr_discovery_enabled: bool,
stun_servers: Vec<String>,
nostr_relays: Vec<String>,
share_local_candidates: bool,
}
const FIPS_STATIC_PEER_ENDPOINT_PRIORITY: u8 = 10;
const FIPS_DYNAMIC_PEER_ENDPOINT_PRIORITY: u8 = 100;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct FipsPeerAddressHint {
pub(crate) addr: String,
pub(crate) seen_at_ms: Option<u64>,
pub(crate) priority: u8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct FipsEndpointPeerTransportConfig {
pub(crate) npub: String,
pub(crate) addresses: Vec<FipsPeerAddressHint>,
pub(crate) auto_reconnect: bool,
pub(crate) discovery_fallback_transit: bool,
}
fn fips_peer_address_from_hint(hint: &FipsPeerAddressHint) -> PeerAddress {
let (transport, addr) = split_peer_transport_addr(&hint.addr);
let mut peer_address = PeerAddress::with_priority(transport, addr, hint.priority);
if let Some(seen_at_ms) = hint.seen_at_ms {
peer_address = peer_address.with_seen_at_ms(seen_at_ms);
}
peer_address
}
fn fips_endpoint_config(
peers: &[FipsEndpointPeerTransportConfig],
transport: Option<&FipsEndpointTransportConfig>,
mesh_mtu: MeshMtu,
nostr_discovery_policy: NostrDiscoveryPolicy,
) -> Config {
fips_endpoint_config_with_open_discovery_limit(
peers,
transport,
mesh_mtu,
nostr_discovery_policy,
FIPS_NOSTR_OPEN_DISCOVERY_MAX_PENDING,
None,
)
}
fn fips_endpoint_config_with_open_discovery_limit(
peers: &[FipsEndpointPeerTransportConfig],
transport: Option<&FipsEndpointTransportConfig>,
mesh_mtu: MeshMtu,
nostr_discovery_policy: NostrDiscoveryPolicy,
open_discovery_max_pending: usize,
connected_udp: Option<&ConnectedUdpConfig>,
) -> Config {
let mut config = Config::new();
apply_connected_udp_config(&mut config, connected_udp);
if peers.iter().any(|peer| {
peer.addresses.iter().any(|hint| {
hint.seen_at_ms.is_none() && split_peer_transport_addr(&hint.addr).0 == "udp"
})
}) {
config.node.connected_udp.enabled = false;
}
config.node.control.enabled = false;
config.node.routing.mode = RoutingMode::ReplyLearned;
config.node.retry.base_interval_secs = FIPS_RECONNECT_BACKOFF_BASE_SECS;
config.node.retry.max_backoff_secs = FIPS_RECONNECT_BACKOFF_MAX_SECS;
config.node.heartbeat_interval_secs = FIPS_ENDPOINT_HEARTBEAT_INTERVAL_SECS;
config.node.link_dead_timeout_secs = FIPS_ENDPOINT_LINK_DEAD_TIMEOUT_SECS;
config.node.fast_link_dead_timeout_secs = FIPS_ENDPOINT_FAST_LINK_DEAD_TIMEOUT_SECS;
config.node.session.idle_timeout_secs = FIPS_ENDPOINT_SESSION_IDLE_TIMEOUT_SECS;
config.node.session.pending_packets_per_dest = FIPS_ENDPOINT_PENDING_PACKETS_PER_DEST;
config.node.rekey.after_secs = FIPS_ENDPOINT_REKEY_AFTER_SECS;
config.dns.enabled = false;
config.node.discovery.backoff_base_secs = FIPS_DISCOVERY_BACKOFF_BASE_SECS;
config.node.discovery.backoff_max_secs = FIPS_DISCOVERY_BACKOFF_MAX_SECS;
config.node.discovery.forward_min_interval_secs = FIPS_DISCOVERY_FORWARD_MIN_INTERVAL_SECS;
let advertise_public_endpoint = transport
.map(|transport| transport.advertise_public_endpoint)
.unwrap_or(false);
let nostr_discovery_enabled = transport
.map(|transport| transport.nostr_discovery_enabled)
.unwrap_or(true);
let advertise_on_nostr = nostr_discovery_enabled && transport.is_some();
let nostr_enabled = nostr_discovery_enabled && (transport.is_some() || !peers.is_empty());
config.node.discovery.nostr.enabled = nostr_enabled;
config.node.discovery.nostr.advertise = advertise_on_nostr;
config.node.discovery.nostr.policy = nostr_discovery_policy;
config.node.discovery.nostr.open_discovery_max_pending = open_discovery_max_pending;
config.node.discovery.nostr.failure_streak_threshold = FIPS_NOSTR_FAILURE_STREAK_THRESHOLD;
config.node.discovery.nostr.extended_cooldown_secs = FIPS_NOSTR_EXTENDED_COOLDOWN_SECS;
config.node.discovery.nostr.startup_sweep_max_age_secs = FIPS_NOSTR_STARTUP_SWEEP_MAX_AGE_SECS;
config.node.discovery.nostr.share_local_candidates = transport
.map(|transport| transport.share_local_candidates)
.unwrap_or(false);
config.node.discovery.lan.enabled = transport
.map(|transport| transport.share_local_candidates)
.unwrap_or(false);
let bind_addr = transport.map(fips_udp_bind_addr);
let external_addr = transport
.filter(|_| advertise_public_endpoint)
.and_then(fips_udp_external_addr);
if let Some(transport) = transport {
config.node.discovery.nostr.stun_servers = transport.stun_servers.clone();
if !transport.nostr_relays.is_empty() {
config.node.discovery.nostr.advert_relays = transport.nostr_relays.clone();
config.node.discovery.nostr.dm_relays = transport.nostr_relays.clone();
}
}
config.transports.udp = TransportInstances::Single(UdpConfig {
bind_addr,
advertise_on_nostr: Some(advertise_on_nostr),
public: Some(external_addr.is_some()),
external_addr,
outbound_only: Some(transport.is_none()),
accept_connections: Some(transport.is_some()),
mtu: Some(mesh_mtu.underlay_udp),
send_buf_size: fips_udp_send_buf_size(),
..UdpConfig::default()
});
let needs_tcp = peers.iter().any(|peer| {
peer.addresses
.iter()
.any(|hint| split_peer_transport_addr(&hint.addr).0 == "tcp")
});
if needs_tcp {
#[allow(clippy::default_trait_access)]
{
config.transports.tcp = TransportInstances::Single(Default::default());
}
}
config.peers = peers
.iter()
.map(|peer| FipsPeerConfig {
npub: peer.npub.clone(),
alias: None,
addresses: peer
.addresses
.iter()
.map(fips_peer_address_from_hint)
.collect(),
connect_policy: ConnectPolicy::AutoConnect,
auto_reconnect: peer.auto_reconnect,
discovery_fallback_transit: peer.discovery_fallback_transit,
})
.collect();
config
}
fn apply_connected_udp_config(config: &mut Config, connected_udp: Option<&ConnectedUdpConfig>) {
let Some(connected_udp) = connected_udp else {
return;
};
if let Some(enabled) = connected_udp.enabled {
config.node.connected_udp.enabled = enabled;
}
if let Some(fd_reserve) = connected_udp.fd_reserve {
config.node.connected_udp.fd_reserve = fd_reserve;
}
}
fn fips_endpoint_peers_from_mesh(
mesh_peers: &[FipsMeshPeerConfig],
operator_static_endpoints: Vec<(String, Vec<String>)>,
recent_peer_endpoints: Vec<(String, Vec<(String, u64)>)>,
) -> Vec<FipsEndpointPeerTransportConfig> {
let mut peers = HashMap::<String, FipsEndpointPeerTransportConfig>::new();
for peer in mesh_peers {
let npub = normalize_fips_endpoint_npub(&peer.endpoint_npub);
peers
.entry(npub.clone())
.or_insert_with(|| FipsEndpointPeerTransportConfig {
npub,
addresses: Vec::new(),
auto_reconnect: true,
discovery_fallback_transit: !peer.advertises_default_route(),
});
}
for (npub, addresses) in operator_static_endpoints {
let npub = normalize_fips_endpoint_npub(&npub);
let peer = peers
.entry(npub.clone())
.or_insert_with(|| FipsEndpointPeerTransportConfig {
npub,
addresses: Vec::new(),
auto_reconnect: false,
discovery_fallback_transit: true,
});
for raw in addresses {
let trimmed = raw.trim();
if trimmed.is_empty() {
continue;
}
if let Some(existing) = peer.addresses.iter_mut().find(|hint| hint.addr == trimmed) {
existing.seen_at_ms = None;
existing.priority = existing.priority.min(FIPS_STATIC_PEER_ENDPOINT_PRIORITY);
continue;
}
peer.addresses.push(FipsPeerAddressHint {
addr: trimmed.to_string(),
seen_at_ms: None,
priority: FIPS_STATIC_PEER_ENDPOINT_PRIORITY,
});
}
}
for (npub, addresses) in recent_peer_endpoints {
let npub = normalize_fips_endpoint_npub(&npub);
let peer = peers
.entry(npub.clone())
.or_insert_with(|| FipsEndpointPeerTransportConfig {
npub,
addresses: Vec::new(),
auto_reconnect: false,
discovery_fallback_transit: true,
});
for (addr, seen_at_ms) in addresses {
let trimmed = addr.trim();
if trimmed.is_empty() {
continue;
}
if let Some(existing) = peer.addresses.iter_mut().find(|hint| hint.addr == trimmed) {
if let Some(existing_seen_at_ms) = existing.seen_at_ms {
existing.seen_at_ms = Some(existing_seen_at_ms.max(seen_at_ms));
existing.priority = existing.priority.min(FIPS_DYNAMIC_PEER_ENDPOINT_PRIORITY);
}
continue;
}
peer.addresses.push(FipsPeerAddressHint {
addr: trimmed.to_string(),
seen_at_ms: Some(seen_at_ms),
priority: FIPS_DYNAMIC_PEER_ENDPOINT_PRIORITY,
});
}
}
let mut peers = peers.into_values().collect::<Vec<_>>();
for peer in &mut peers {
peer.addresses.sort_by(|a, b| a.addr.cmp(&b.addr));
peer.addresses.dedup_by(|a, b| a.addr == b.addr);
}
peers.sort_by(|left, right| left.npub.cmp(&right.npub));
peers
}
fn normalize_fips_endpoint_npub(value: &str) -> String {
let trimmed = value.trim();
normalize_nostr_pubkey(trimmed)
.ok()
.and_then(|pubkey| {
PublicKey::from_hex(&pubkey)
.ok()
.and_then(|public_key| public_key.to_bech32().ok())
})
.unwrap_or_else(|| trimmed.to_string())
}
fn participant_pubkey_bytes(value: &str) -> Option<ParticipantPubkeyBytes> {
PublicKey::parse(value.trim())
.ok()
.map(|pubkey| *pubkey.as_bytes())
}
fn fips_udp_bind_addr(transport: &FipsEndpointTransportConfig) -> String {
SocketAddr::V4(SocketAddrV4::new(
std::net::Ipv4Addr::UNSPECIFIED,
transport.listen_port,
))
.to_string()
}
fn fips_udp_external_addr(transport: &FipsEndpointTransportConfig) -> Option<String> {
let endpoint = transport.advertised_endpoint.trim();
if endpoint.is_empty() {
return None;
}
endpoint.parse::<SocketAddr>().ok()?;
Some(endpoint.to_string())
}
#[derive(Debug, Clone)]
pub(crate) struct FipsPrivateTunnelConfig {
pub(crate) identity_nsec: String,
pub(crate) network_id: String,
pub(crate) iface: String,
pub(crate) local_address: String,
pub(crate) listen_port: u16,
pub(crate) advertised_endpoint: String,
pub(crate) advertise_public_endpoint: bool,
pub(crate) stun_servers: Vec<String>,
pub(crate) nostr_relays: Vec<String>,
pub(crate) share_local_candidates: bool,
pub(crate) peers: Vec<FipsMeshPeerConfig>,
pub(crate) endpoint_peers: Vec<FipsEndpointPeerTransportConfig>,
pub(crate) route_targets: Vec<String>,
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub(crate) fips_host: Option<FipsHostTunnelConfig>,
pub(crate) local_advertised_routes: Vec<String>,
pub(crate) wireguard_exit: WireGuardExitConfig,
pub(crate) exit_node_leak_protection: bool,
connected_udp: ConnectedUdpConfig,
nostr_discovery_enabled: bool,
nostr_discovery_policy: NostrDiscoveryPolicy,
open_discovery_max_pending: usize,
mesh_mtu: MeshMtu,
#[cfg(target_os = "linux")]
pub(crate) control_plane_bypass_hosts: Vec<Ipv4Addr>,
}