1use super::*;
2
3impl_veilid_log_facility!("veilid_api");
4
5pub(super) struct VeilidAPIInner {
8 context: Option<VeilidCoreContext>,
9 #[cfg(feature = "debug-api")]
10 pub(super) debug_cache: debug::DebugCache,
11}
12
13impl fmt::Debug for VeilidAPIInner {
14 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15 write!(f, "VeilidAPIInner")
16 }
17}
18
19impl Drop for VeilidAPIInner {
20 fn drop(&mut self) {
21 if let Some(context) = self.context.take() {
22 spawn_detached("api shutdown", api_shutdown(context));
23 }
24 }
25}
26
27#[derive(Clone, Debug)]
41#[must_use]
42pub struct VeilidAPI {
43 inner: Arc<Mutex<VeilidAPIInner>>,
44}
45
46impl VeilidAPI {
47 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = context.log_key()), skip_all))]
48 pub(crate) fn new(context: VeilidCoreContext) -> Self {
49 veilid_log!(context debug "VeilidAPI::new()");
50 record_duration(|| Self {
51 inner: Arc::new(Mutex::new(VeilidAPIInner {
52 context: Some(context),
53 #[cfg(feature = "debug-api")]
54 debug_cache: Default::default(),
55 })),
56 })
57 }
58
59 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip_all))]
64 pub async fn shutdown(self) {
65 let context = { self.inner.lock().context.take() };
66 let recorder = DurationRecorder::new("VeilidAPI::shutdown", |name, start| {
67 veilid_log!(self debug "{}[start={:#}]()", name, start);
68 });
69 recorder
70 .record_fut(
71 async {
72 if let Some(context) = context {
73 api_shutdown(context).await;
74 }
75 },
76 |name, start, dur, ret| {
77 veilid_log!(self debug "{}[start={:#} dur={:#}](ret: ())", name, start, dur);
78 ret
79 },
80 )
81 .await
82 }
83
84 #[must_use]
86 pub fn is_shutdown(&self) -> bool {
87 self.inner.lock().context.is_none()
88 }
89
90 pub fn config(&self) -> VeilidAPIResult<Arc<VeilidConfig>> {
97 let inner = self.inner.lock();
98 let Some(context) = &inner.context else {
99 return Err(VeilidAPIError::NotInitialized);
100 };
101 Ok(context.registry().config())
102 }
103
104 pub fn crypto(&self) -> VeilidAPIResult<VeilidComponentGuard<'_, Crypto>> {
108 let inner = self.inner.lock();
109 let Some(context) = &inner.context else {
110 return Err(VeilidAPIError::NotInitialized);
111 };
112 context
113 .registry()
114 .lookup::<Crypto>()
115 .ok_or(VeilidAPIError::NotInitialized)
116 }
117
118 pub fn table_store(&self) -> VeilidAPIResult<VeilidComponentGuard<'_, TableStore>> {
122 let inner = self.inner.lock();
123 let Some(context) = &inner.context else {
124 return Err(VeilidAPIError::NotInitialized);
125 };
126 context
127 .registry()
128 .lookup::<TableStore>()
129 .ok_or(VeilidAPIError::NotInitialized)
130 }
131
132 pub fn protected_store(&self) -> VeilidAPIResult<VeilidComponentGuard<'_, ProtectedStore>> {
136 let inner = self.inner.lock();
137 let Some(context) = &inner.context else {
138 return Err(VeilidAPIError::NotInitialized);
139 };
140 context
141 .registry()
142 .lookup::<ProtectedStore>()
143 .ok_or(VeilidAPIError::NotInitialized)
144 }
145
146 #[cfg(feature = "unstable-blockstore")]
150 pub fn block_store(&self) -> VeilidAPIResult<VeilidComponentGuard<'_, BlockStore>> {
151 let inner = self.inner.lock();
152 let Some(context) = &inner.context else {
153 return Err(VeilidAPIError::NotInitialized);
154 };
155 context
156 .registry()
157 .lookup::<BlockStore>()
158 .ok_or(VeilidAPIError::NotInitialized)
159 }
160
161 #[expect(clippy::unused_async)]
168 pub async fn get_state(&self) -> VeilidAPIResult<VeilidState> {
169 let attachment_manager = self.core_context()?.attachment_manager();
170 let network_manager = attachment_manager.network_manager();
171 let config = self.config()?;
172
173 let attachment = attachment_manager.get_veilid_state();
174 let network = network_manager.get_veilid_state();
175
176 Ok(VeilidState {
177 attachment,
178 network,
179 config: Box::new(VeilidStateConfig {
180 config: config.as_ref().clone(),
181 }),
182 })
183 }
184
185 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip_all, ret))]
193 pub async fn attach(&self) -> VeilidAPIResult<()> {
194 async {
195 let attachment_manager = self.core_context()?.attachment_manager();
196 let recorder = DurationRecorder::new("VeilidAPI::attach", |name, start| {
197 veilid_log!(self debug "{}[start={:#}]()", name, start);
198 });
199 recorder.record_fut(
200 async {
201 if !Box::pin(attachment_manager.attach()).await {
202 apibail_generic!("Already attached");
203 }
204 VeilidAPIResult::Ok(())
205 },
206 |name, start, dur, ret| {
207 veilid_log!(self debug "{}[start={:#} dur={:#}](ret: {:?})", name, start, dur, ret);
208 ret
209 },
210 ).await
211 }
212 .await
213 .inspect_err(log_veilid_api_error!(self))
214 }
215
216 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip_all, ret))]
224 pub async fn detach(&self) -> VeilidAPIResult<()> {
225 async {
226 let attachment_manager = self.core_context()?.attachment_manager();
227 let recorder = DurationRecorder::new("VeilidAPI::detach", |name, start| {
228 veilid_log!(self debug "{}[start={:#}]()", name, start);
229 });
230 recorder.record_fut(
231 async {
232 if !Box::pin(attachment_manager.detach()).await {
233 apibail_generic!("Already detached");
234 }
235 VeilidAPIResult::Ok(())
236 },
237 |name, start, dur, ret| {
238 veilid_log!(self debug "{}[start={:#} dur={:#}](ret: {:?})", name, start, dur, ret);
239 ret
240 },
241 ).await
242 }
243 .await
244 .inspect_err(log_veilid_api_error!(self))
245 }
246
247 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip_all, ret))]
254 pub fn routing_context(&self) -> VeilidAPIResult<RoutingContext> {
255 record_duration(|| {
256 veilid_log!(self debug "VeilidAPI::routing_context()");
257
258 RoutingContext::try_new(self.clone())
259 })
260 .inspect_err(log_veilid_api_error!(self))
261 }
262
263 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), ret))]
275 pub async fn get_dht_record_key(
276 &self,
277 schema: DHTSchema,
278 owner_key: PublicKey,
279 encryption_key: Option<SharedSecret>,
280 ) -> VeilidAPIResult<RecordKey> {
281 async {
282 schema.validate()?;
283 self.crypto()?.check_public_key(&owner_key)?;
284 if let Some(encryption_key) = encryption_key.as_ref() {
285 self.crypto()?.check_shared_secret(encryption_key)?;
286 }
287 let storage_manager = self.core_context()?.storage_manager();
288
289 let recorder = DurationRecorder::new("VeilidAPI::get_dht_record_key", |name, start| {
290 veilid_log!(self debug
291 "{}[start={:#}](self: {:?}, schema: {:?}, owner_key: {:?}, encryption_key: {:?})", name, start, self, schema, owner_key, encryption_key);
292 });
293 recorder.record_fut(
294 storage_manager.get_record_key(schema, &owner_key, encryption_key),
295 |name, start, dur, ret| {
296 veilid_log!(self debug
297 "{}[start={:#} dur={:#}](ret: {:?})", name, start, dur, ret);
298 ret
299 },
300 ).await
301 }.await.inspect_err(log_veilid_api_error!(self))
302 }
303
304 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", skip(self), fields(duration, __VEILID_LOG_KEY = self.log_key()), ret))]
309 pub fn generate_member_id(&self, writer_key: &PublicKey) -> VeilidAPIResult<MemberId> {
310 record_duration(move || {
311 veilid_log!(self debug "VeilidAPI::generate_member_id(writer_key: {:?}", writer_key);
312
313 self.crypto()?.check_public_key(writer_key)?;
314
315 let storage_manager = self.core_context()?.storage_manager();
316 storage_manager.generate_member_id(writer_key)
317 })
318 .inspect_err(log_veilid_api_error!(self))
319 }
320
321 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), ret))]
339 pub async fn transact_dht_records(
340 &self,
341 record_keys: Vec<RecordKey>,
342 options: Option<TransactDHTRecordsOptions>,
343 ) -> VeilidAPIResult<DHTTransaction> {
344 async {
345 let storage_manager = self.core_context()?.storage_manager();
346 for record_key in &record_keys {
347 storage_manager.check_record_key(record_key)?;
348 }
349
350 let recorder = DurationRecorder::new("VeilidAPI::transact_dht_records", |name, start| {
351 veilid_log!(self debug
352 "{}[start={:#}](self: {:?}, record_keys: {:?}, options: {:?})", name, start, self, record_keys, options);
353 });
354 recorder.record_fut(
355 async {
356 let handle = Box::pin(storage_manager.begin_transaction(record_keys, options)).await?;
357 DHTTransaction::new(self.clone(), handle)
358 },
359 |name, start, dur, ret| {
360 veilid_log!(self debug
361 "{}[start={:#} dur={:#}](ret: {:?})", name, start, dur, ret);
362 ret
363 },
364 ).await
365 }.await.inspect_err(log_veilid_api_error!(self))
366 }
367
368 pub async fn new_private_route(&self) -> VeilidAPIResult<RouteBlob> {
386 Box::pin(self.new_custom_private_route(PrivateSpec::default())).await
387 }
388
389 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip(self), ret))]
407 pub async fn new_custom_private_route(
408 &self,
409 mut private_spec: PrivateSpec,
410 ) -> VeilidAPIResult<RouteBlob> {
411 async {
412 let default_route_hop_count: usize =
413 self.config()?.network.rpc.default_route_hop_count.into();
414
415 if private_spec.crypto_kinds.is_empty() {
416 private_spec.crypto_kinds = VALID_CRYPTO_KINDS.to_vec();
417 } else {
418 for kind in &private_spec.crypto_kinds {
419 Crypto::validate_crypto_kind(*kind)?;
420 }
421 }
422 if private_spec.hop_count == 0 {
423 private_spec.hop_count = default_route_hop_count;
424 }
425
426 let routing_table = self.core_context()?.routing_table();
427 let rss = routing_table.route_spec_store();
428
429 let recorder = DurationRecorder::new("VeilidAPI::new_custom_private_route", |name, start| {
430 veilid_log!(self debug "{}[start={:#}](private_spec: {:?})", name, start, private_spec);
431 });
432 recorder.record_fut(
433 async {
434 let allocate_route_params = AllocateRouteParams {
435 crypto_kinds: private_spec.crypto_kinds,
436 hop_count: private_spec.hop_count,
437 stability: private_spec.stability,
438 sequencing: private_spec.sequencing,
439 directions: DirectionSet::all(),
440 avoid_nodes: Vec::new(),
441 automatic: false,
442 };
443 let RouteIdAndKeys {
444 route_id,
445 route_set_keys: _,
446 } = rss.allocate_route(allocate_route_params).await?;
447 let route_id_api: RouteId = route_id.clone().into();
448 match Box::pin(rss.test_route(route_id_api.clone())).await? {
449 Some(true) => {}
450 Some(false) => {
451 rss.release_route(route_id_api.clone());
452 apibail_try_again!("allocated route failed to test");
453 }
454 None => {
455 rss.release_route(route_id_api.clone());
456 apibail_try_again!("allocated route could not be tested");
457 }
458 }
459 let private_routes = rss.assemble_private_route_set(&route_id, Some(true)).await?;
460 let blob = match RouteSpecStore::private_routes_to_blob(&private_routes) {
461 Ok(v) => v,
462 Err(e) => {
463 rss.release_route(route_id_api);
464 return Err(e);
465 }
466 };
467 rss.mark_route_published(&route_id, true)?;
468 VeilidAPIResult::Ok(RouteBlob {
469 route_id: route_id_api,
470 blob: blob.into(),
471 })
472 },
473 |name, start, dur, ret| {
474 veilid_log!(self debug "{}[start={:#} dur={:#}](ret: {:?})", name, start, dur, ret);
475 ret
476 },
477 ).await
478 }
479 .await
480 .inspect_err(log_veilid_api_error!(self))
481 }
482
483 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip(self), ret))]
494 pub fn import_remote_private_route(&self, blob: Vec<u8>) -> VeilidAPIResult<RouteId> {
495 record_duration(|| {
496 veilid_log!(self debug
497 "VeilidAPI::import_remote_private_route(blob: {:?})", blob);
498 let routing_table = self.core_context()?.routing_table();
499 let rss = routing_table.route_spec_store();
500 rss.import_remote_route_blob(blob)
501 })
502 .inspect_err(log_veilid_api_error!(self))
503 }
504
505 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip(self), ret))]
516 pub fn release_private_route(&self, route_id: RouteId) -> VeilidAPIResult<()> {
517 record_duration(|| {
518 veilid_log!(self debug
519 "VeilidAPI::release_private_route(route_id: {:?})", route_id);
520
521 let routing_table = self.core_context()?.routing_table();
522 routing_table.check_route_id(&route_id)?;
523
524 let rss = routing_table.route_spec_store();
525 if !rss.release_route(route_id.clone()) {
526 apibail_invalid_argument!("release_private_route", "key", route_id);
527 }
528 Ok(())
529 })
530 .inspect_err(log_veilid_api_error!(self))
531 }
532
533 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip(self), ret))]
546 pub async fn app_call_reply(
547 &self,
548 call_id: OperationId,
549 message: Vec<u8>,
550 ) -> VeilidAPIResult<()> {
551 async {
552 let rpc_processor = self.core_context()?.rpc_processor();
553
554 let message_len = message.len();
555 let recorder = DurationRecorder::new("VeilidAPI::app_call_reply", |name, start| {
556 veilid_log!(self debug
557 "{}[start={:#}](call_id: {:?}, message_len: {})", name, start, call_id, message_len);
558 veilid_log!(self trace "message: {:?}", message);
559 });
560 recorder.record(
561 || rpc_processor
562 .app_call_reply(call_id, message.into())
563 .map_err(|e| e.into()),
564 |name, start, dur, ret| {
565 veilid_log!(self debug
566 "{}[start={:#} dur={:#}](ret: {:?})", name, start, dur, ret);
567 ret
568 },
569 )
570 }.await.inspect_err(log_veilid_api_error!(self))
571 }
572
573 #[cfg(feature = "unstable-tunnels")]
577 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip(self), ret))]
578 pub async fn start_tunnel(
579 &self,
580 _endpoint_mode: TunnelMode,
581 _depth: u8,
582 ) -> VeilidAPIResult<PartialTunnel> {
583 apibail_internal!("unimplemented");
584 }
585
586 #[cfg(feature = "unstable-tunnels")]
587 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip(self), ret))]
588 pub async fn complete_tunnel(
589 &self,
590 _endpoint_mode: TunnelMode,
591 _depth: u8,
592 _partial_tunnel: PartialTunnel,
593 ) -> VeilidAPIResult<FullTunnel> {
594 apibail_internal!("unimplemented");
595 }
596
597 #[cfg(feature = "unstable-tunnels")]
598 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip(self), ret))]
599 pub async fn cancel_tunnel(&self, _tunnel_id: TunnelId) -> VeilidAPIResult<bool> {
600 apibail_internal!("unimplemented");
601 }
602
603 pub(crate) fn core_context(&self) -> VeilidAPIResult<VeilidCoreContext> {
607 let inner = self.inner.lock();
608 let Some(context) = &inner.context else {
609 return Err(VeilidAPIError::NotInitialized);
610 };
611 Ok(context.clone())
612 }
613
614 #[cfg(feature = "debug-api")]
615 pub(crate) fn with_debug_cache<R, F: FnOnce(&mut debug::DebugCache) -> R>(
616 &self,
617 callback: F,
618 ) -> R {
619 let mut inner = self.inner.lock();
620 callback(&mut inner.debug_cache)
621 }
622
623 #[must_use]
624 pub(crate) fn log_key(&self) -> &str {
625 let inner = self.inner.lock();
626 let Some(context) = &inner.context else {
627 return "";
628 };
629 context.log_key()
630 }
631}