fips_core/node/lifecycle/
control.rs1use super::*;
2
3impl Node {
4 pub(crate) async fn api_connect(
12 &mut self,
13 npub: &str,
14 address: &str,
15 transport: &str,
16 ) -> Result<serde_json::Value, String> {
17 let peer_config = PeerConfig {
18 npub: npub.to_string(),
19 alias: None,
20 addresses: vec![PeerAddress::new(transport, address)],
21 connect_policy: ConnectPolicy::Manual,
22 auto_reconnect: false,
23 discovery_fallback_transit: true,
24 };
25
26 if let Ok(identity) = PeerIdentity::from_npub(npub) {
28 self.peer_aliases
29 .insert(*identity.node_addr(), identity.short_npub());
30 self.register_identity(*identity.node_addr(), identity.pubkey_full());
31 }
32
33 self.initiate_peer_connection(&peer_config)
34 .await
35 .map(|()| {
36 info!(
37 npub = %npub,
38 address = %address,
39 transport = %transport,
40 "API connect initiated"
41 );
42 serde_json::json!({
43 "npub": npub,
44 "address": address,
45 "transport": transport,
46 })
47 })
48 .map_err(|e| e.to_string())
49 }
50
51 pub(crate) async fn api_disconnect(&mut self, npub: &str) -> Result<serde_json::Value, String> {
55 let peer_identity =
56 PeerIdentity::from_npub(npub).map_err(|e| format!("invalid npub '{npub}': {e}"))?;
57 let node_addr = *peer_identity.node_addr();
58
59 if !self.peers.contains_key(&node_addr) {
60 return Err(format!("peer not found: {npub}"));
61 }
62
63 self.send_disconnect_to_peer(&node_addr, DisconnectReason::ConfigurationChange)
64 .await;
65
66 self.remove_active_peer(&node_addr);
68
69 self.retry_pending.remove(&node_addr);
71
72 info!(npub = %npub, "API disconnect completed");
73
74 Ok(serde_json::json!({
75 "npub": npub,
76 "disconnected": true,
77 }))
78 }
79
80 pub async fn adopt_established_traversal(
87 &mut self,
88 traversal: EstablishedTraversal,
89 ) -> Result<BootstrapHandoffResult, NodeError> {
90 debug!(
91 peer_npub = %traversal.peer_npub,
92 session_id = %traversal.session_id,
93 remote_addr = %traversal.remote_addr,
94 "adopting established traversal socket"
95 );
96
97 if !self.state.is_operational() {
98 return Err(NodeError::NotStarted);
99 }
100
101 let packet_tx = self.packet_tx.clone().ok_or(NodeError::NotStarted)?;
102 let peer_identity = PeerIdentity::from_npub(&traversal.peer_npub).map_err(|e| {
103 NodeError::InvalidPeerNpub {
104 npub: traversal.peer_npub.clone(),
105 reason: e.to_string(),
106 }
107 })?;
108 let peer_node_addr = *peer_identity.node_addr();
109 if self.peers.contains_key(&peer_node_addr) {
110 debug!(
111 peer_npub = %traversal.peer_npub,
112 "Adopting NAT traversal handoff as alternate path for already-connected peer"
113 );
114 }
115
116 self.peer_aliases
117 .insert(peer_node_addr, peer_identity.short_npub());
118 self.register_identity(peer_node_addr, peer_identity.pubkey_full());
119
120 let transport_id = self.allocate_transport_id();
121 let inherited_config = traversal.transport_config.clone().unwrap_or_else(|| {
141 let mut cfg = self
142 .lookup_udp_config(traversal.transport_name.as_deref())
143 .or_else(|| self.lookup_udp_config(None))
144 .cloned()
145 .unwrap_or_default();
146 cfg.bind_addr = None;
147 cfg.external_addr = None;
148 cfg
149 });
150 let mut transport = crate::transport::udp::UdpTransport::new(
151 transport_id,
152 traversal.transport_name.clone(),
153 inherited_config,
154 packet_tx,
155 );
156
157 transport
158 .adopt_socket_async(traversal.socket)
159 .await
160 .map_err(|e| NodeError::BootstrapHandoff(e.to_string()))?;
161
162 let local_addr = transport.local_addr().ok_or_else(|| {
163 NodeError::BootstrapHandoff("adopted UDP transport has no local address".into())
164 })?;
165
166 self.transports.insert(
167 transport_id,
168 crate::transport::TransportHandle::Udp(transport),
169 );
170 self.udp_transport_resolution_cache.clear();
171 self.bootstrap_transports
172 .register(transport_id, traversal.peer_npub.clone());
173
174 let remote_addr = TransportAddr::from_string(&traversal.remote_addr.to_string());
175 if let Err(err) = self
176 .initiate_connection(transport_id, remote_addr.clone(), peer_identity)
177 .await
178 {
179 self.bootstrap_transports.remove(&transport_id);
180 if let Some(mut handle) = self.transports.remove(&transport_id) {
181 self.udp_transport_resolution_cache.clear();
182 let _ = handle.stop().await;
183 }
184 return Err(err);
185 }
186
187 info!(
188 peer = %self.peer_display_name(&peer_node_addr),
189 transport_id = %transport_id,
190 local_addr = %local_addr,
191 remote_addr = %traversal.remote_addr,
192 session_id = %traversal.session_id,
193 "adopted NAT traversal socket; handshake initiated"
194 );
195
196 Ok(BootstrapHandoffResult {
197 transport_id,
198 local_addr,
199 remote_addr: traversal.remote_addr,
200 peer_node_addr,
201 session_id: traversal.session_id,
202 })
203 }
204}