From 246056913e4d1e1ad0e21cf9d2d5c58c73760e38 Mon Sep 17 00:00:00 2001 From: Christien Rioux Date: Fri, 1 Sep 2023 21:13:05 -0400 Subject: [PATCH] doc work --- veilid-core/src/attachment_manager.rs | 28 ++++---- veilid-core/src/core_context.rs | 16 +++++ veilid-core/src/crypto/dh_cache.rs | 12 ++-- veilid-core/src/lib.rs | 14 +++- .../native/network_class_discovery.rs | 9 +-- .../coders/operations/operation_route.rs | 12 ++-- veilid-core/src/veilid_api/api.rs | 30 +++++++- veilid-core/src/veilid_api/debug.rs | 2 + veilid-core/src/veilid_api/routing_context.rs | 70 ++++++++++++++++--- .../src/veilid_api/types/app_message_call.rs | 16 +++-- veilid-tools/Cargo.toml | 1 + veilid-tools/src/lib.rs | 14 ++++ 12 files changed, 172 insertions(+), 52 deletions(-) diff --git a/veilid-core/src/attachment_manager.rs b/veilid-core/src/attachment_manager.rs index 0dcd6cdb..14b7856d 100644 --- a/veilid-core/src/attachment_manager.rs +++ b/veilid-core/src/attachment_manager.rs @@ -86,18 +86,18 @@ impl AttachmentManager { self.unlocked_inner.network_manager.clone() } - pub fn is_attached(&self) -> bool { - let s = self.inner.lock().last_attachment_state; - !matches!(s, AttachmentState::Detached | AttachmentState::Detaching) - } - pub fn is_detached(&self) -> bool { - let s = self.inner.lock().last_attachment_state; - matches!(s, AttachmentState::Detached) - } + // pub fn is_attached(&self) -> bool { + // let s = self.inner.lock().last_attachment_state; + // !matches!(s, AttachmentState::Detached | AttachmentState::Detaching) + // } + // pub fn is_detached(&self) -> bool { + // let s = self.inner.lock().last_attachment_state; + // matches!(s, AttachmentState::Detached) + // } - pub fn get_attach_timestamp(&self) -> Option { - self.inner.lock().attach_ts - } + // pub fn get_attach_timestamp(&self) -> Option { + // self.inner.lock().attach_ts + // } fn translate_routing_table_health( health: &RoutingTableHealth, @@ -321,9 +321,9 @@ impl AttachmentManager { } } - pub fn get_attachment_state(&self) -> AttachmentState { - self.inner.lock().last_attachment_state - } + // pub fn get_attachment_state(&self) -> AttachmentState { + // self.inner.lock().last_attachment_state + // } fn get_veilid_state_inner(inner: &AttachmentManagerInner) -> VeilidStateAttachment { VeilidStateAttachment { diff --git a/veilid-core/src/core_context.rs b/veilid-core/src/core_context.rs index f21440f1..4624b012 100644 --- a/veilid-core/src/core_context.rs +++ b/veilid-core/src/core_context.rs @@ -290,6 +290,14 @@ lazy_static::lazy_static! { static ref INITIALIZED: AsyncMutex = AsyncMutex::new(false); } +/// Initialize a Veilid node. +/// +/// Must be called only once at the start of an application +/// +/// * `update_callback` - called when internal state of the Veilid node changes, for example, when app-level messages are received, when private routes die and need to be reallocated, or when routing table states change +/// * `config_callback` - called at startup to supply a configuration object directly to Veilid +/// +/// Returns a [VeilidAPI] object that can be used to operate the node #[instrument(err, skip_all)] pub async fn api_startup( update_callback: UpdateCallback, @@ -313,6 +321,14 @@ pub async fn api_startup( Ok(veilid_api) } +/// Initialize a Veilid node, with the configuration in JSON format +/// +/// Must be called only once at the start of an application +/// +/// * `update_callback` - called when internal state of the Veilid node changes, for example, when app-level messages are received, when private routes die and need to be reallocated, or when routing table states change +/// * `config_json` - called at startup to supply a JSON configuration object +/// +/// Returns a [VeilidAPI] object that can be used to operate the node #[instrument(err, skip_all)] pub async fn api_startup_json( update_callback: UpdateCallback, diff --git a/veilid-core/src/crypto/dh_cache.rs b/veilid-core/src/crypto/dh_cache.rs index 17c5d4ae..18c7de04 100644 --- a/veilid-core/src/crypto/dh_cache.rs +++ b/veilid-core/src/crypto/dh_cache.rs @@ -3,20 +3,20 @@ use crate::*; // Diffie-Hellman key exchange cache #[derive(Serialize, Deserialize, PartialEq, Eq, Hash)] -pub struct DHCacheKey { +pub(crate) struct DHCacheKey { pub key: PublicKey, pub secret: SecretKey, } #[derive(Serialize, Deserialize)] -pub struct DHCacheValue { +pub(crate) struct DHCacheValue { pub shared_secret: SharedSecret, } -pub type DHCache = LruCache; -pub const DH_CACHE_SIZE: usize = 4096; +pub(crate) type DHCache = LruCache; +pub(crate) const DH_CACHE_SIZE: usize = 4096; -pub fn cache_to_bytes(cache: &DHCache) -> Vec { +pub(crate) fn cache_to_bytes(cache: &DHCache) -> Vec { let cnt: usize = cache.len(); let mut out: Vec = Vec::with_capacity(cnt * (32 + 32 + 32)); for e in cache.iter() { @@ -31,7 +31,7 @@ pub fn cache_to_bytes(cache: &DHCache) -> Vec { rev } -pub fn bytes_to_cache(bytes: &[u8], cache: &mut DHCache) { +pub(crate) fn bytes_to_cache(bytes: &[u8], cache: &mut DHCache) { for d in bytes.chunks(32 + 32 + 32) { let k = DHCacheKey { key: PublicKey::new(d[0..32].try_into().expect("asdf")), diff --git a/veilid-core/src/lib.rs b/veilid-core/src/lib.rs index 54ce58bd..a13bdf81 100644 --- a/veilid-core/src/lib.rs +++ b/veilid-core/src/lib.rs @@ -1,4 +1,4 @@ -//! The Veilid Framework +//! # The Veilid Framework //! //! Core library used to create a Veilid node and operate veilid services as part of an application. //! @@ -9,6 +9,18 @@ //! //! From there, a [RoutingContext] object can get you access to public and private routed operations. //! +//! ## Features +//! +//! The default `veilid-core` configurations are: +//! +//! * `default` - Uses `tokio` as the async runtime +//! +//! If you use `--no-default-features`, you can switch to other runtimes: +//! +//! * `default-async-std` - Uses `async-std` as the async runtime +//! * `default-wasm` - When building for the `wasm32` architecture, use this to enable `wasm-bindgen-futures` as the async runtime +//! + #![deny(clippy::all)] #![deny(unused_must_use)] #![recursion_limit = "256"] diff --git a/veilid-core/src/network_manager/native/network_class_discovery.rs b/veilid-core/src/network_manager/native/network_class_discovery.rs index 7faa1fef..6432e058 100644 --- a/veilid-core/src/network_manager/native/network_class_discovery.rs +++ b/veilid-core/src/network_manager/native/network_class_discovery.rs @@ -489,14 +489,12 @@ impl DiscoveryContext { #[instrument(level = "trace", skip(self), ret, err)] pub async fn protocol_process_nat(&self) -> EyreResult { // Get the external dial info for our use here - let (node_1, external_1_dial_info, external_1_address, protocol_type, address_type) = { + let (node_1, external_1_dial_info, protocol_type) = { let inner = self.inner.lock(); ( inner.node_1.as_ref().unwrap().clone(), inner.external_1_dial_info.as_ref().unwrap().clone(), - inner.external_1_address.unwrap(), inner.protocol_type.unwrap(), - inner.address_type.unwrap(), ) }; @@ -595,13 +593,10 @@ impl DiscoveryContext { // We are restricted, determine what kind of restriction // Get the external dial info for our use here - let (node_1, external_1_dial_info, external_1_address, protocol_type, address_type) = { + let (external_1_address, address_type) = { let inner = self.inner.lock(); ( - inner.node_1.as_ref().unwrap().clone(), - inner.external_1_dial_info.as_ref().unwrap().clone(), inner.external_1_address.unwrap(), - inner.protocol_type.unwrap(), inner.address_type.unwrap(), ) }; diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_route.rs b/veilid-core/src/rpc_processor/coders/operations/operation_route.rs index 38a7c3a1..622f20b5 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_route.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_route.rs @@ -50,9 +50,9 @@ impl RoutedOperation { &self.data } - pub fn destructure(self) -> (Sequencing, Vec, Nonce, Vec) { - (self.sequencing, self.signatures, self.nonce, self.data) - } + // pub fn destructure(self) -> (Sequencing, Vec, Nonce, Vec) { + // (self.sequencing, self.signatures, self.nonce, self.data) + // } pub fn decode(reader: &veilid_capnp::routed_operation::Reader) -> Result { let sigs_reader = reader.get_signatures().map_err(RPCError::protocol)?; @@ -125,9 +125,9 @@ impl RPCOperationRoute { pub fn safety_route(&self) -> &SafetyRoute { &self.safety_route } - pub fn operation(&self) -> &RoutedOperation { - &self.operation - } + // pub fn operation(&self) -> &RoutedOperation { + // &self.operation + // } pub fn destructure(self) -> (SafetyRoute, RoutedOperation) { (self.safety_route, self.operation) } diff --git a/veilid-core/src/veilid_api/api.rs b/veilid-core/src/veilid_api/api.rs index 0b910a98..70da215e 100644 --- a/veilid-core/src/veilid_api/api.rs +++ b/veilid-core/src/veilid_api/api.rs @@ -48,6 +48,7 @@ impl VeilidAPI { } } + /// Shut down Veilid and terminate the API #[instrument(skip_all)] pub async fn shutdown(self) { let context = { self.inner.lock().context.take() }; @@ -56,6 +57,7 @@ impl VeilidAPI { } } + /// Check to see if Veilid is already shut down pub fn is_shutdown(&self) -> bool { self.inner.lock().context.is_none() } @@ -63,6 +65,7 @@ impl VeilidAPI { //////////////////////////////////////////////////////////////// // Public Accessors + /// Access the configuration that Veilid was initialized with pub fn config(&self) -> VeilidAPIResult { let inner = self.inner.lock(); if let Some(context) = &inner.context { @@ -70,6 +73,8 @@ impl VeilidAPI { } Err(VeilidAPIError::NotInitialized) } + + /// Get the cryptosystem manager pub fn crypto(&self) -> VeilidAPIResult { let inner = self.inner.lock(); if let Some(context) = &inner.context { @@ -77,6 +82,8 @@ impl VeilidAPI { } Err(VeilidAPIError::NotInitialized) } + + /// Get the TableStore manager pub fn table_store(&self) -> VeilidAPIResult { let inner = self.inner.lock(); if let Some(context) = &inner.context { @@ -84,6 +91,8 @@ impl VeilidAPI { } Err(VeilidAPIError::not_initialized()) } + + /// Get the ProtectedStore manager pub fn protected_store(&self) -> VeilidAPIResult { let inner = self.inner.lock(); if let Some(context) = &inner.context { @@ -141,7 +150,7 @@ impl VeilidAPI { //////////////////////////////////////////////////////////////// // Attach/Detach - /// Get a full copy of the current state + /// Get a full copy of the current state of Veilid pub async fn get_state(&self) -> VeilidAPIResult { let attachment_manager = self.attachment_manager()?; let network_manager = attachment_manager.network_manager(); @@ -217,6 +226,9 @@ impl VeilidAPI { /// Allocate a new private route set with default cryptography and network options /// Returns a route id and a publishable 'blob' with the route encrypted with each crypto kind /// Those nodes importing the blob will have their choice of which crypto kind to use + /// + /// Returns a route id and 'blob' that can be published over some means (DHT or otherwise) to be + /// imported by another Veilid node. pub async fn new_private_route(&self) -> VeilidAPIResult<(RouteId, Vec)> { self.new_custom_private_route( &VALID_CRYPTO_KINDS, @@ -226,7 +238,12 @@ impl VeilidAPI { .await } + /// Allocate a new private route and specify a specific cryptosystem, stability and sequencing preference + /// Returns a route id and a publishable 'blob' with the route encrypted with each crypto kind + /// Those nodes importing the blob will have their choice of which crypto kind to use /// + /// Returns a route id and 'blob' that can be published over some means (DHT or otherwise) to be + /// imported by another Veilid node. pub async fn new_custom_private_route( &self, crypto_kinds: &[CryptoKind], @@ -282,12 +299,19 @@ impl VeilidAPI { Ok((route_id, blob)) } + /// Import a private route blob as a remote private route. + /// + /// Returns a route id that can be used to send private messages to the node creating this route. pub fn import_remote_private_route(&self, blob: Vec) -> VeilidAPIResult { let rss = self.routing_table()?.route_spec_store(); rss.import_remote_private_route(blob) .map_err(|e| VeilidAPIError::invalid_argument(e, "blob", "private route blob")) } + /// Release either a locally allocated or remotely imported private route + /// + /// This will deactivate the route and free its resources and it can no longer be sent to + /// or received from. pub fn release_private_route(&self, route_id: RouteId) -> VeilidAPIResult<()> { let rss = self.routing_table()?.route_spec_store(); if !rss.release_route(route_id) { @@ -299,6 +323,10 @@ impl VeilidAPI { //////////////////////////////////////////////////////////////// // App Calls + /// Respond to an AppCall received over a [VeilidUpdate::AppCall]. + /// + /// * `call_id` - specifies which call to reply to, and it comes from a [VeilidUpdate::AppCall], specifically the [VeilidAppCall::id()] value. + /// * `message` - is an answer blob to be returned by the remote node's [RoutingContext::app_call()] function, and may be up to 32768 bytes pub async fn app_call_reply( &self, call_id: OperationId, diff --git a/veilid-core/src/veilid_api/debug.rs b/veilid-core/src/veilid_api/debug.rs index 4b93db45..9c169c5b 100644 --- a/veilid-core/src/veilid_api/debug.rs +++ b/veilid-core/src/veilid_api/debug.rs @@ -1363,6 +1363,7 @@ impl VeilidAPI { } } + /// Get the help text for 'internal debug' commands pub async fn debug_help(&self, _args: String) -> VeilidAPIResult { Ok(r#"buckets [dead|reliable] dialinfo @@ -1425,6 +1426,7 @@ record list .to_owned()) } + /// Execute an 'internal debug command' pub async fn debug(&self, args: String) -> VeilidAPIResult { let res = { let args = args.trim_start(); diff --git a/veilid-core/src/veilid_api/routing_context.rs b/veilid-core/src/veilid_api/routing_context.rs index 2aeebc17..5b335845 100644 --- a/veilid-core/src/veilid_api/routing_context.rs +++ b/veilid-core/src/veilid_api/routing_context.rs @@ -2,10 +2,13 @@ use super::*; /////////////////////////////////////////////////////////////////////////////////////// +/// Valid destinations for a message sent over a routing context #[derive(Clone, Debug)] pub enum Target { - NodeId(TypedKey), // Node by its public key - PrivateRoute(RouteId), // Remote private route by its id + /// Node by its public key + NodeId(TypedKey), + /// Remote private route by its id + PrivateRoute(RouteId), } pub struct RoutingContextInner {} @@ -24,6 +27,12 @@ impl Drop for RoutingContextInner { } } +/// Routing contexts are the way you specify the communication preferences for Veilid. +/// +/// By default routing contexts are 'direct' from node to node, offering no privacy. To enable sender +/// privacy, use [RoutingContext::with_privacy()]. To enable receiver privacy, you should send to a private route RouteId that you have +/// imported, rather than directly to a NodeId. +/// #[derive(Clone)] pub struct RoutingContext { /// Veilid API handle @@ -45,6 +54,15 @@ impl RoutingContext { } } + /// Turn on sender privacy, enabling the use of safety routes. + /// + /// Default values for hop count, stability and sequencing preferences are used. + /// + /// * Hop count default is dependent on config, but is set to 1 extra hop. + /// * Stability default is to choose 'low latency' routes, preferring them over long-term reliability. + /// * Sequencing default is to have no preference for ordered vs unordered message delivery + /// + /// To modify these defaults, use [RoutingContext::with_custom_privacy()]. pub fn with_privacy(self) -> VeilidAPIResult { let config = self.api.config()?; let c = config.get(); @@ -57,6 +75,7 @@ impl RoutingContext { })) } + /// Turn on privacy using a custom [SafetySelection] pub fn with_custom_privacy(self, safety_selection: SafetySelection) -> VeilidAPIResult { Ok(Self { api: self.api.clone(), @@ -65,6 +84,7 @@ impl RoutingContext { }) } + /// Use a specified [Sequencing] preference, with or without privacy pub fn with_sequencing(self, sequencing: Sequencing) -> Self { Self { api: self.api.clone(), @@ -90,6 +110,7 @@ impl RoutingContext { } } + /// Get the [VeilidAPI] object that created this [RoutingContext] pub fn api(&self) -> VeilidAPI { self.api.clone() } @@ -135,6 +156,14 @@ impl RoutingContext { //////////////////////////////////////////////////////////////// // App-level Messaging + /// App-level bidirectional call that expects a response to be returned. + /// + /// Veilid apps may use this for arbitrary message passing. + /// + /// * `target` - can be either a direct node id or a private route + /// * `message` - an arbitrary message blob of up to 32768 bytes + /// + /// Returns an answer blob of up to 32768 bytes pub async fn app_call(&self, target: Target, message: Vec) -> VeilidAPIResult> { let rpc_processor = self.api.rpc_processor()?; @@ -162,6 +191,12 @@ impl RoutingContext { Ok(answer.answer) } + /// App-level unidirectional message that does not expect any value to be returned. + /// + /// Veilid apps may use this for arbitrary message passing. + /// + /// * `target` - can be either a direct node id or a private route + /// * `message` - an arbitrary message blob of up to 32768 bytes pub async fn app_message(&self, target: Target, message: Vec) -> VeilidAPIResult<()> { let rpc_processor = self.api.rpc_processor()?; @@ -192,7 +227,10 @@ impl RoutingContext { /// DHT Records /// Creates a new DHT record a specified crypto kind and schema - /// Returns the newly allocated DHT record's key if successful. The records is considered 'open' after the create operation succeeds. + /// + /// The record is considered 'open' after the create operation succeeds. + /// + /// Returns the newly allocated DHT record's key if successful. pub async fn create_dht_record( &self, schema: DHTSchema, @@ -206,9 +244,12 @@ impl RoutingContext { .await } - /// Opens a DHT record at a specific key. Associates a secret if one is provided to provide writer capability. + /// Opens a DHT record at a specific key + /// + /// Associates a secret if one is provided to provide writer capability. + /// Records may only be opened or created. To re-open with a different routing context, first close the value. + /// /// Returns the DHT record descriptor for the opened record if successful - /// Records may only be opened or created . To re-open with a different routing context, first close the value. pub async fn open_dht_record( &self, key: TypedKey, @@ -222,6 +263,7 @@ impl RoutingContext { } /// Closes a DHT record at a specific key that was opened with create_dht_record or open_dht_record. + /// /// Closing a record allows you to re-open it with a different routing context pub async fn close_dht_record(&self, key: TypedKey) -> VeilidAPIResult<()> { Crypto::validate_crypto_kind(key.kind)?; @@ -229,7 +271,9 @@ impl RoutingContext { storage_manager.close_record(key).await } - /// Deletes a DHT record at a specific key. If the record is opened, it must be closed before it is deleted. + /// Deletes a DHT record at a specific key + /// + /// If the record is opened, it must be closed before it is deleted. /// Deleting a record does not delete it from the network, but will remove the storage of the record /// locally, and will prevent its value from being refreshed on the network by this node. pub async fn delete_dht_record(&self, key: TypedKey) -> VeilidAPIResult<()> { @@ -239,9 +283,11 @@ impl RoutingContext { } /// Gets the latest value of a subkey + /// /// May pull the latest value from the network, but by settings 'force_refresh' you can force a network data refresh - /// Returns None if the value subkey has not yet been set - /// Returns Some(data) if the value subkey has valid data + /// + /// Returns `None` if the value subkey has not yet been set + /// Returns `Some(data)` if the value subkey has valid data pub async fn get_dht_value( &self, key: TypedKey, @@ -254,8 +300,9 @@ impl RoutingContext { } /// Pushes a changed subkey value to the network - /// Returns None if the value was successfully put - /// Returns Some(data) if the value put was older than the one available on the network + /// + /// Returns `None` if the value was successfully put + /// Returns `Some(data)` if the value put was older than the one available on the network pub async fn set_dht_value( &self, key: TypedKey, @@ -268,9 +315,11 @@ impl RoutingContext { } /// Watches changes to an opened or created value + /// /// Changes to subkeys within the subkey range are returned via a ValueChanged callback /// If the subkey range is empty, all subkey changes are considered /// Expiration can be infinite to keep the watch for the maximum amount of time + /// /// Return value upon success is the amount of time allowed for the watch pub async fn watch_dht_values( &self, @@ -287,6 +336,7 @@ impl RoutingContext { } /// Cancels a watch early + /// /// This is a convenience function that cancels watching all subkeys in a range pub async fn cancel_dht_watch( &self, diff --git a/veilid-core/src/veilid_api/types/app_message_call.rs b/veilid-core/src/veilid_api/types/app_message_call.rs index f47be2af..500588be 100644 --- a/veilid-core/src/veilid_api/types/app_message_call.rs +++ b/veilid-core/src/veilid_api/types/app_message_call.rs @@ -3,15 +3,13 @@ use super::*; /// Direct statement blob passed to hosting application for processing #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] pub struct VeilidAppMessage { - /// Some(sender) if the message was sent directly, None if received via a private/safety route #[serde(with = "as_human_opt_string")] #[schemars(with = "Option")] - pub sender: Option, + sender: Option, - /// The content of the message to deliver to the application #[serde(with = "as_human_base64")] #[schemars(with = "String")] - pub message: Vec, + message: Vec, } impl VeilidAppMessage { @@ -19,9 +17,12 @@ impl VeilidAppMessage { Self { sender, message } } + /// Some(sender) if the message was sent directly, None if received via a private/safety route pub fn sender(&self) -> Option<&TypedKey> { self.sender.as_ref() } + + /// The content of the message to deliver to the application pub fn message(&self) -> &[u8] { &self.message } @@ -30,17 +31,14 @@ impl VeilidAppMessage { /// Direct question blob passed to hosting application for processing to send an eventual AppReply #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] pub struct VeilidAppCall { - /// Some(sender) if the request was sent directly, None if received via a private/safety route #[serde(with = "as_human_opt_string")] #[schemars(with = "Option")] sender: Option, - /// The content of the request to deliver to the application #[serde(with = "as_human_base64")] #[schemars(with = "String")] message: Vec, - /// The id to reply to #[serde(with = "as_human_string")] #[schemars(with = "String")] call_id: OperationId, @@ -55,12 +53,16 @@ impl VeilidAppCall { } } + /// Some(sender) if the request was sent directly, None if received via a private/safety route pub fn sender(&self) -> Option<&TypedKey> { self.sender.as_ref() } + /// The content of the request to deliver to the application pub fn message(&self) -> &[u8] { &self.message } + + /// The id to specify as `call_id` in the [VeilidAPI::app_call_reply] function pub fn id(&self) -> OperationId { self.call_id } diff --git a/veilid-tools/Cargo.toml b/veilid-tools/Cargo.toml index c55c9539..30f7adc6 100644 --- a/veilid-tools/Cargo.toml +++ b/veilid-tools/Cargo.toml @@ -74,6 +74,7 @@ wasm-bindgen = "0.2.87" js-sys = "0.3.64" wasm-bindgen-futures = "0.4.37" async_executors = { version = "0.7.0", default-features = false } + async-lock = "2.8.0" send_wrapper = { version = "0.6.0", features = ["futures"] } diff --git a/veilid-tools/src/lib.rs b/veilid-tools/src/lib.rs index b23c1001..c824f696 100644 --- a/veilid-tools/src/lib.rs +++ b/veilid-tools/src/lib.rs @@ -1,3 +1,5 @@ +//! # Veilid Tools +//! //! A collection of baseline tools for Rust development use by Veilid and Veilid-enabled Rust applications //! //! These are used by `veilid-core`, `veilid-server`, `veilid-cli` and may be used by any other applications @@ -6,6 +8,18 @@ //! remain free of boilerplate and utility classes that could be reused elsewhere. //! //! Everything added to this crate must be extensively unit-tested. +//! +//! ## Features +//! +//! The default `veilid-tools` configurations are: +//! +//! * `default` - Uses `tokio` as the async runtime +//! +//! If you use `--no-default-features`, you can switch to other runtimes: +//! +//! * `rt-async-std` - Uses `async-std` as the async runtime +//! * `rt-wasm-bindgen` - When building for the `wasm32` architecture, use this to enable `wasm-bindgen-futures` as the async runtime +//! // pub mod bump_port; pub mod assembly_buffer;