1use crate::*;
2
3cfg_if::cfg_if! {
4 if #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] {
5 use directories::ProjectDirs;
6 }
7}
8
9cfg_if::cfg_if! {
10 if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
11 pub const MAX_CONNECTIONS_MIN: u32 = 16;
13 pub const MAX_CONNECTIONS_MAX: u32 = 64;
15 } else {
16 pub const MAX_CONNECTIONS_MIN: u32 = 32;
18 pub const MAX_CONNECTIONS_MAX: u32 = 512;
20 }
21}
22
23#[apply(api_data_struct!)]
34#[api(eq, ts(into_wasm_abi, from_wasm_abi))]
35pub struct VeilidConfigUDP {
36 pub enabled: bool,
38 pub listen_address: String,
40 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), tsify(optional))]
42 pub public_address: Option<String>,
43}
44
45impl Default for VeilidConfigUDP {
46 fn default() -> Self {
47 cfg_if::cfg_if! {
48 if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
49 let enabled = false;
50 } else {
51 let enabled = true;
52 }
53 }
54 Self {
55 enabled,
56 listen_address: String::from(""),
57 public_address: None,
58 }
59 }
60}
61
62#[apply(api_data_struct!)]
72#[api(eq, ts(into_wasm_abi, from_wasm_abi))]
73pub struct VeilidConfigTCP {
74 pub connect: bool,
76 pub listen: bool,
78 pub listen_address: String,
80 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), tsify(optional))]
82 pub public_address: Option<String>,
83}
84
85impl Default for VeilidConfigTCP {
86 fn default() -> Self {
87 cfg_if::cfg_if! {
88 if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
89 let connect = false;
90 let listen = false;
91 } else {
92 let connect = true;
93 let listen = true;
94 }
95 }
96 Self {
97 connect,
98 listen,
99 listen_address: String::from(""),
100 public_address: None,
101 }
102 }
103}
104
105#[apply(api_data_struct!)]
116#[api(eq, ts(into_wasm_abi, from_wasm_abi))]
117pub struct VeilidConfigWS {
118 pub connect: bool,
120 pub listen: bool,
122 pub listen_address: String,
124 pub path: String,
126 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), tsify(optional))]
128 pub url: Option<String>,
129}
130
131impl Default for VeilidConfigWS {
132 fn default() -> Self {
133 cfg_if::cfg_if! {
134 if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
135 let connect = true;
136 let listen = false;
137 } else {
138 let connect = true;
139 let listen = true;
140 }
141 }
142 Self {
143 connect,
144 listen,
145 listen_address: String::from(""),
146 path: String::from("ws"),
147 url: None,
148 }
149 }
150}
151
152#[cfg(feature = "enable-protocol-wss")]
163#[apply(api_data_struct!)]
164#[api(eq, ts(into_wasm_abi, from_wasm_abi))]
165pub struct VeilidConfigWSS {
166 pub connect: bool,
168 pub listen: bool,
170 pub listen_address: String,
172 pub path: String,
174 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), tsify(optional))]
176 pub url: Option<String>, }
178
179#[cfg(feature = "enable-protocol-wss")]
180impl Default for VeilidConfigWSS {
181 fn default() -> Self {
182 Self {
183 connect: true,
184 listen: false,
185 listen_address: String::from(""),
186 path: String::from("ws"),
187 url: None,
188 }
189 }
190}
191
192#[apply(api_data_struct!)]
200#[api(eq, default, ts(into_wasm_abi, from_wasm_abi))]
201pub struct VeilidConfigProtocol {
202 pub udp: VeilidConfigUDP,
204 pub tcp: VeilidConfigTCP,
206 pub ws: VeilidConfigWS,
208 #[cfg(feature = "enable-protocol-wss")]
210 pub wss: VeilidConfigWSS,
211}
212
213#[apply(api_data_struct!)]
221#[api(eq, default)]
222#[cfg_attr(
223 target_arch = "wasm32",
224 derive(Tsify),
225 tsify(into_wasm_abi, from_wasm_abi)
226)]
227pub struct VeilidConfigPrivacy {
228 pub require_inbound_relay: bool,
230 #[cfg(feature = "geolocation")]
232 pub country_code_denylist: Vec<CountryCode>,
233}
234
235#[cfg(feature = "virtual-network")]
243#[apply(api_data_struct!)]
244#[api(eq, default)]
245#[cfg_attr(
246 target_arch = "wasm32",
247 derive(Tsify),
248 tsify(into_wasm_abi, from_wasm_abi)
249)]
250pub struct VeilidConfigVirtualNetwork {
251 pub enabled: bool,
253 pub server_address: String,
255}
256
257#[apply(api_data_struct!)]
266#[api(eq, ts(into_wasm_abi, from_wasm_abi))]
267pub struct VeilidConfigTLS {
268 pub certificate_path: String,
270 pub private_key_path: String,
272 pub connection_initial_timeout_ms: u32,
274}
275
276impl Default for VeilidConfigTLS {
277 fn default() -> Self {
278 Self {
279 certificate_path: "".to_string(),
280 private_key_path: "".to_string(),
281 connection_initial_timeout_ms: 2000,
282 }
283 }
284}
285
286#[cfg_attr(
287 all(target_arch = "wasm32", target_os = "unknown"),
288 allow(unused_variables)
289)]
290#[must_use]
292pub fn get_default_ssl_directory(
293 program_name: &str,
294 organization: &str,
295 qualifier: &str,
296 sub_path: &str,
297) -> String {
298 cfg_if::cfg_if! {
299 if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
300 "".to_owned()
301 } else {
302 use std::path::PathBuf;
303 ProjectDirs::from(qualifier, organization, program_name)
304 .map(|dirs| dirs.data_local_dir().join("ssl").join(sub_path))
305 .unwrap_or_else(|| PathBuf::from("./ssl").join(sub_path))
306 .to_string_lossy()
307 .into()
308 }
309 }
310}
311
312#[apply(api_data_struct!)]
317#[api(eq, ts(into_wasm_abi, from_wasm_abi))]
318pub struct VeilidConfigDHT {
319 pub local_subkey_cache_size: u32,
321 pub local_max_subkey_cache_memory_mb: u32,
323 pub remote_subkey_cache_size: u32,
325 pub remote_max_records: u32,
327 pub remote_max_subkey_cache_memory_mb: u32,
329 pub remote_max_storage_space_mb: u32,
331 #[serde(default = "default_dht_max_concurrent_operations")]
333 pub max_concurrent_operations: u32,
334}
335
336fn default_dht_max_concurrent_operations() -> u32 {
338 cfg_if::cfg_if! {
339 if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
340 16
341 } else {
342 16
343 }
344 }
345}
346
347impl Default for VeilidConfigDHT {
348 fn default() -> Self {
349 cfg_if::cfg_if! {
350 if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
351 let local_subkey_cache_size = 128;
352 let local_max_subkey_cache_memory_mb = 256;
353 let remote_subkey_cache_size = 64;
354 let remote_max_records = 64;
355 let remote_max_subkey_cache_memory_mb = 256;
356 let remote_max_storage_space_mb = 128;
357 } else {
358 let local_subkey_cache_size = 1024;
359 let local_max_subkey_cache_memory_mb = match total_memory_bytes() {
360 Some(mem) => (mem / 32u64 / (1024u64 * 1024u64)) as u32,
361 None => 256,
362 };
363 let remote_subkey_cache_size = 128;
364 let remote_max_records = 128;
365 let remote_max_subkey_cache_memory_mb = match total_memory_bytes() {
366 Some(mem) => (mem / 32u64 / (1024u64 * 1024u64)) as u32,
367 None => 256,
368 };
369 let remote_max_storage_space_mb = 256;
370 }
371 }
372
373 let max_concurrent_operations = default_dht_max_concurrent_operations();
374
375 Self {
376 local_subkey_cache_size,
377 local_max_subkey_cache_memory_mb,
378 remote_subkey_cache_size,
379 remote_max_records,
380 remote_max_subkey_cache_memory_mb,
381 remote_max_storage_space_mb,
382 max_concurrent_operations,
383 }
384 }
385}
386
387#[apply(api_data_struct!)]
390#[api(eq, ts(into_wasm_abi, from_wasm_abi))]
391pub struct VeilidConfigRPC {
392 pub default_route_hop_count: u8,
394}
395
396impl Default for VeilidConfigRPC {
397 fn default() -> Self {
398 Self {
399 default_route_hop_count: 1,
400 }
401 }
402}
403
404#[apply(api_data_struct!)]
407#[api(eq, ts(into_wasm_abi, from_wasm_abi))]
408pub struct VeilidConfigRoutingTable {
409 #[cfg_attr(feature = "schemars", schemars(with = "Vec<String>"))]
411 #[cfg_attr(
412 all(target_arch = "wasm32", target_os = "unknown"),
413 tsify(type = "string[]")
414 )]
415 pub public_keys: PublicKeyGroup,
416 #[cfg_attr(feature = "schemars", schemars(with = "Vec<String>"))]
418 #[cfg_attr(
419 all(target_arch = "wasm32", target_os = "unknown"),
420 tsify(type = "string[]")
421 )]
422 pub secret_keys: SecretKeyGroup,
423 pub bootstrap: Vec<String>,
425 #[cfg_attr(feature = "schemars", schemars(with = "Vec<String>"))]
427 #[cfg_attr(
428 all(target_arch = "wasm32", target_os = "unknown"),
429 tsify(type = "string[]")
430 )]
431 pub bootstrap_keys: Vec<PublicKey>,
432 }
435
436impl Default for VeilidConfigRoutingTable {
437 fn default() -> Self {
438 cfg_if::cfg_if! {
439 if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
440 let bootstrap = vec!["ws://bootstrap-v1.veilid.net:5150/ws".to_string()];
441 } else {
442 let bootstrap = vec!["bootstrap-v1.veilid.net".to_string()];
443 }
444 }
445 let bootstrap_keys = vec![
446 PublicKey::from_str("VLD0:Vj0lKDdUQXmQ5Ol1SZdlvXkBHUccBcQvGLN9vbLSI7k").unwrap_or_log(),
448 PublicKey::from_str("VLD0:QeQJorqbXtC7v3OlynCZ_W3m76wGNeB5NTF81ypqHAo").unwrap_or_log(),
450 PublicKey::from_str("VLD0:QNdcl-0OiFfYVj9331XVR6IqZ49NG-E18d5P7lwi4TA").unwrap_or_log(),
452 ];
453
454 Self {
455 public_keys: PublicKeyGroup::default(),
456 secret_keys: SecretKeyGroup::default(),
457 bootstrap,
458 bootstrap_keys,
459 }
460 }
461}
462
463#[apply(api_data_enum!)]
465#[api(eq, copy, ord, hash, ts(namespace, into_wasm_abi, from_wasm_abi))]
466pub enum VeilidConfigAddressType {
467 #[serde(rename = "IPV4", alias = "ipv4", alias = "v4", alias = "4")]
469 Ipv4,
470 #[serde(rename = "IPV6", alias = "ipv6", alias = "v6", alias = "6")]
472 Ipv6,
473}
474
475impl fmt::Display for VeilidConfigAddressType {
476 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
477 match self {
478 VeilidConfigAddressType::Ipv4 => write!(f, "IPV4"),
479 VeilidConfigAddressType::Ipv6 => write!(f, "IPV6"),
480 }
481 }
482}
483
484impl FromStr for VeilidConfigAddressType {
485 type Err = VeilidAPIError;
486 fn from_str(s: &str) -> VeilidAPIResult<VeilidConfigAddressType> {
487 match s.to_ascii_lowercase().as_str() {
488 "v4" | "4" | "ipv4" => Ok(VeilidConfigAddressType::Ipv4),
489 "v6" | "6" | "ipv6" => Ok(VeilidConfigAddressType::Ipv6),
490 _ => apibail_invalid_argument!("invalid VeilidConfigAddressType string", "s", s),
491 }
492 }
493}
494
495#[apply(api_data_struct!)]
497#[api(eq, ts(into_wasm_abi, from_wasm_abi))]
498pub struct VeilidConfigNetwork {
499 pub max_connections: u32,
502 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), tsify(optional))]
504 pub network_key_password: Option<String>,
505 pub routing_table: VeilidConfigRoutingTable,
507 pub rpc: VeilidConfigRPC,
509 pub dht: VeilidConfigDHT,
511 #[serde(default)]
513 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), tsify(optional))]
514 pub address_types: Vec<VeilidConfigAddressType>,
515 pub upnp: bool,
517 pub detect_address_changes: Option<bool>,
519 pub tls: VeilidConfigTLS,
521 pub protocol: VeilidConfigProtocol,
523 pub privacy: VeilidConfigPrivacy,
525 #[cfg(feature = "virtual-network")]
527 pub virtual_network: VeilidConfigVirtualNetwork,
528}
529
530impl Default for VeilidConfigNetwork {
531 fn default() -> Self {
532 Self {
533 max_connections: 32,
534 network_key_password: None,
535 address_types: Vec::new(),
536 routing_table: VeilidConfigRoutingTable::default(),
537 rpc: VeilidConfigRPC::default(),
538 dht: VeilidConfigDHT::default(),
539 upnp: true,
540 detect_address_changes: Some(true),
541 tls: VeilidConfigTLS::default(),
542 protocol: VeilidConfigProtocol::default(),
543 privacy: VeilidConfigPrivacy::default(),
544 #[cfg(feature = "virtual-network")]
545 virtual_network: VeilidConfigVirtualNetwork::default(),
546 }
547 }
548}
549
550#[apply(api_data_struct!)]
552#[api(eq, ts(into_wasm_abi, from_wasm_abi))]
553pub struct VeilidConfigTableStore {
554 pub directory: String,
556 pub delete: bool,
558 pub wipe_on_invalid_device_encryption_key: bool,
560 pub max_value_size_mb: u32,
562}
563
564impl Default for VeilidConfigTableStore {
565 fn default() -> Self {
566 Self {
567 directory: "".to_string(),
568 delete: false,
569 wipe_on_invalid_device_encryption_key: true,
570 max_value_size_mb: 64,
571 }
572 }
573}
574
575#[cfg_attr(
576 all(target_arch = "wasm32", target_os = "unknown"),
577 allow(unused_variables)
578)]
579#[must_use]
580fn get_default_store_path(
581 program_name: &str,
582 organization: &str,
583 qualifier: &str,
584 store_type: &str,
585) -> String {
586 cfg_if::cfg_if! {
587 if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
588 "".to_owned()
589 } else {
590 use std::path::PathBuf;
591 ProjectDirs::from(qualifier, organization, program_name)
592 .map(|dirs| dirs.data_local_dir().to_path_buf())
593 .unwrap_or_else(|| PathBuf::from("./"))
594 .join(store_type)
595 .to_string_lossy()
596 .into()
597 }
598 }
599}
600
601#[apply(api_data_struct!)]
603#[api(eq, ts(into_wasm_abi, from_wasm_abi))]
604pub struct VeilidConfigBlockStore {
605 pub directory: String,
607 pub delete: bool,
609}
610
611impl Default for VeilidConfigBlockStore {
612 fn default() -> Self {
613 Self {
614 directory: "".to_string(),
615 delete: false,
616 }
617 }
618}
619
620#[apply(api_data_struct!)]
622#[api(eq, ts(into_wasm_abi, from_wasm_abi))]
623pub struct VeilidConfigProtectedStore {
624 pub allow_insecure_fallback: bool,
626 pub always_use_insecure_storage: bool,
628 pub directory: String,
630 pub delete: bool,
632 pub device_encryption_key_password: String,
634 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), tsify(optional))]
636 pub new_device_encryption_key_password: Option<String>,
637}
638
639impl Default for VeilidConfigProtectedStore {
640 fn default() -> Self {
641 Self {
642 allow_insecure_fallback: false,
643 always_use_insecure_storage: false,
644 directory: "".to_string(),
645 delete: false,
646 device_encryption_key_password: "".to_owned(),
647 new_device_encryption_key_password: None,
648 }
649 }
650}
651
652#[apply(api_data_struct!)]
654#[api(eq, default, ts(into_wasm_abi, from_wasm_abi))]
655pub struct VeilidConfigCapabilities {
656 pub disable: Vec<VeilidCapability>,
658}
659
660#[apply(api_data_enum!)]
662#[api(eq, copy, ord, default, ts(namespace, into_wasm_abi, from_wasm_abi))]
663pub enum VeilidConfigLogLevel {
664 #[default]
666 Off,
667 Error,
669 Warn,
671 Info,
673 Debug,
675 Trace,
677}
678
679impl From<VeilidLogLevel> for VeilidConfigLogLevel {
680 fn from(value: VeilidLogLevel) -> Self {
681 match value {
682 VeilidLogLevel::Error => Self::Error,
683 VeilidLogLevel::Warn => Self::Warn,
684 VeilidLogLevel::Info => Self::Info,
685 VeilidLogLevel::Debug => Self::Debug,
686 VeilidLogLevel::Trace => Self::Trace,
687 }
688 }
689}
690
691impl From<Option<VeilidLogLevel>> for VeilidConfigLogLevel {
692 fn from(value: Option<VeilidLogLevel>) -> Self {
693 match value {
694 None => Self::Off,
695 Some(VeilidLogLevel::Error) => Self::Error,
696 Some(VeilidLogLevel::Warn) => Self::Warn,
697 Some(VeilidLogLevel::Info) => Self::Info,
698 Some(VeilidLogLevel::Debug) => Self::Debug,
699 Some(VeilidLogLevel::Trace) => Self::Trace,
700 }
701 }
702}
703
704impl From<tracing::level_filters::LevelFilter> for VeilidConfigLogLevel {
705 fn from(value: tracing::level_filters::LevelFilter) -> Self {
706 match value {
707 tracing::level_filters::LevelFilter::OFF => Self::Off,
708 tracing::level_filters::LevelFilter::ERROR => Self::Error,
709 tracing::level_filters::LevelFilter::WARN => Self::Warn,
710 tracing::level_filters::LevelFilter::INFO => Self::Info,
711 tracing::level_filters::LevelFilter::DEBUG => Self::Debug,
712 tracing::level_filters::LevelFilter::TRACE => Self::Trace,
713 }
714 }
715}
716
717impl From<VeilidConfigLogLevel> for tracing::level_filters::LevelFilter {
718 fn from(val: VeilidConfigLogLevel) -> Self {
719 match val {
720 VeilidConfigLogLevel::Off => tracing::level_filters::LevelFilter::OFF,
721 VeilidConfigLogLevel::Error => tracing::level_filters::LevelFilter::ERROR,
722 VeilidConfigLogLevel::Warn => tracing::level_filters::LevelFilter::WARN,
723 VeilidConfigLogLevel::Info => tracing::level_filters::LevelFilter::INFO,
724 VeilidConfigLogLevel::Debug => tracing::level_filters::LevelFilter::DEBUG,
725 VeilidConfigLogLevel::Trace => tracing::level_filters::LevelFilter::TRACE,
726 }
727 }
728}
729
730impl From<tracing::log::LevelFilter> for VeilidConfigLogLevel {
731 fn from(value: tracing::log::LevelFilter) -> Self {
732 match value {
733 tracing::log::LevelFilter::Off => Self::Off,
734 tracing::log::LevelFilter::Error => Self::Error,
735 tracing::log::LevelFilter::Warn => Self::Warn,
736 tracing::log::LevelFilter::Info => Self::Info,
737 tracing::log::LevelFilter::Debug => Self::Debug,
738 tracing::log::LevelFilter::Trace => Self::Trace,
739 }
740 }
741}
742
743impl From<VeilidConfigLogLevel> for tracing::log::LevelFilter {
744 fn from(val: VeilidConfigLogLevel) -> Self {
745 match val {
746 VeilidConfigLogLevel::Off => tracing::log::LevelFilter::Off,
747 VeilidConfigLogLevel::Error => tracing::log::LevelFilter::Error,
748 VeilidConfigLogLevel::Warn => tracing::log::LevelFilter::Warn,
749 VeilidConfigLogLevel::Info => tracing::log::LevelFilter::Info,
750 VeilidConfigLogLevel::Debug => tracing::log::LevelFilter::Debug,
751 VeilidConfigLogLevel::Trace => tracing::log::LevelFilter::Trace,
752 }
753 }
754}
755
756impl TryFrom<&str> for VeilidConfigLogLevel {
757 type Error = VeilidAPIError;
758
759 fn try_from(value: &str) -> Result<Self, <Self as TryFrom<&str>>::Error> {
760 Self::from_str(value)
761 }
762}
763
764impl TryFrom<String> for VeilidConfigLogLevel {
765 type Error = VeilidAPIError;
766
767 fn try_from(value: String) -> Result<Self, <Self as TryFrom<String>>::Error> {
768 Self::from_str(value.as_str())
769 }
770}
771
772impl TryFrom<&String> for VeilidConfigLogLevel {
773 type Error = VeilidAPIError;
774
775 fn try_from(value: &String) -> Result<Self, <Self as TryFrom<&String>>::Error> {
776 Self::from_str(value.as_str())
777 }
778}
779
780impl FromStr for VeilidConfigLogLevel {
781 type Err = VeilidAPIError;
782 fn from_str(s: &str) -> Result<Self, Self::Err> {
783 Ok(match s.to_ascii_lowercase().as_str() {
784 "off" => Self::Off,
785 "error" => Self::Error,
786 "warn" => Self::Warn,
787 "info" => Self::Info,
788 "debug" => Self::Debug,
789 "trace" => Self::Trace,
790 _ => {
791 apibail_invalid_argument!("invalid VeilidConfigLogLevel string", "s", s);
792 }
793 })
794 }
795}
796impl fmt::Display for VeilidConfigLogLevel {
797 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
798 let text = match self {
799 Self::Off => "Off",
800 Self::Error => "Error",
801 Self::Warn => "Warn",
802 Self::Info => "Info",
803 Self::Debug => "Debug",
804 Self::Trace => "Trace",
805 };
806 write!(f, "{}", text)
807 }
808}
809
810#[apply(api_data_struct!)]
812#[api(eq, default, ts(into_wasm_abi, from_wasm_abi))]
813pub struct VeilidConfigInternalUDP {
814 pub socket_pool_size: u32,
816}
817
818#[apply(api_data_struct!)]
820#[api(eq, default, ts(into_wasm_abi, from_wasm_abi))]
821pub struct VeilidConfigInternalProtocol {
822 pub udp: VeilidConfigInternalUDP,
824}
825
826#[apply(api_data_struct!)]
828#[api(eq, ts(into_wasm_abi, from_wasm_abi))]
829pub struct VeilidConfigInternalRPC {
830 pub concurrency: u32,
832 pub queue_size: u32,
834 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), tsify(optional))]
836 pub max_timestamp_behind_ms: Option<u32>,
837 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), tsify(optional))]
839 pub max_timestamp_ahead_ms: Option<u32>,
840 pub timeout_ms: u32,
842 pub max_route_hop_count: u8,
844}
845impl Default for VeilidConfigInternalRPC {
846 fn default() -> Self {
847 Self {
848 concurrency: 0,
849 queue_size: 1024,
850 max_timestamp_behind_ms: Some(10000),
851 max_timestamp_ahead_ms: Some(10000),
852 timeout_ms: 5000,
853 max_route_hop_count: 4,
854 }
855 }
856}
857
858#[apply(api_data_struct!)]
862#[api(eq, ts(into_wasm_abi, from_wasm_abi))]
863pub struct VeilidConfigInternalDHT {
864 pub max_find_node_count: u32,
866 pub resolve_node_timeout_ms: u32,
868 pub resolve_node_count: u32,
870 pub resolve_node_fanout: u32,
872 pub get_value_timeout_ms: u32,
874 pub get_value_count: u32,
876 pub get_value_fanout: u32,
878 pub set_value_timeout_ms: u32,
880 pub set_value_count: u32,
882 pub set_value_fanout: u32,
884 pub consensus_width: u32,
886 pub min_peer_count: u32,
888 pub min_peer_refresh_time_ms: u32,
890 pub validate_dial_info_receipt_time_ms: u32,
892 pub max_watch_expiration_ms: u32,
894 pub public_watch_limit: u32,
896 pub member_watch_limit: u32,
898 pub public_transaction_limit: u32,
900 pub member_transaction_limit: u32,
902}
903impl Default for VeilidConfigInternalDHT {
904 fn default() -> Self {
905 Self {
906 max_find_node_count: 20,
907 resolve_node_timeout_ms: 10000,
908 resolve_node_count: 1,
909 resolve_node_fanout: 5,
910 get_value_timeout_ms: 10000,
911 get_value_count: 3,
912 get_value_fanout: 5,
913 set_value_timeout_ms: 10000,
914 set_value_count: 5,
915 set_value_fanout: 6,
916 consensus_width: 10,
917 min_peer_count: 20,
918 min_peer_refresh_time_ms: 60000,
919 validate_dial_info_receipt_time_ms: 1000,
920 max_watch_expiration_ms: 600000,
921 public_watch_limit: 32,
922 member_watch_limit: 8,
923 public_transaction_limit: 4,
924 member_transaction_limit: 1,
925 }
926 }
927}
928
929#[apply(api_data_struct!)]
931#[api(eq, ts(into_wasm_abi, from_wasm_abi))]
932pub struct VeilidConfigInternalNetwork {
933 pub connection_initial_timeout_ms: u32,
935 pub connection_inactivity_timeout_ms: u32,
937 pub max_connections_per_ip4: u32,
939 pub max_connections_per_ip6_prefix: u32,
941 pub max_connections_per_ip6_prefix_size: u32,
943 pub max_connection_frequency_per_min: u32,
945 pub client_allowlist_timeout_ms: u32,
947 pub reverse_connection_receipt_time_ms: u32,
949 pub hole_punch_receipt_time_ms: u32,
951 pub restricted_nat_retries: u32,
954 pub rpc: VeilidConfigInternalRPC,
956 pub dht: VeilidConfigInternalDHT,
958 pub protocol: VeilidConfigInternalProtocol,
960}
961impl Default for VeilidConfigInternalNetwork {
962 fn default() -> Self {
963 Self {
964 connection_initial_timeout_ms: 2000,
965 connection_inactivity_timeout_ms: 60000,
966 max_connections_per_ip4: 32,
967 max_connections_per_ip6_prefix: 32,
968 max_connections_per_ip6_prefix_size: 56,
969 max_connection_frequency_per_min: 128,
970 client_allowlist_timeout_ms: 300000,
971 reverse_connection_receipt_time_ms: 5000,
972 hole_punch_receipt_time_ms: 5000,
973 restricted_nat_retries: 0,
974 rpc: VeilidConfigInternalRPC::default(),
975 dht: VeilidConfigInternalDHT::default(),
976 protocol: VeilidConfigInternalProtocol::default(),
977 }
978 }
979}
980
981#[apply(api_data_struct!)]
988#[api(eq, default, ts(into_wasm_abi, from_wasm_abi))]
989pub struct VeilidConfigInternal {
990 pub network: VeilidConfigInternalNetwork,
992}
993
994#[apply(api_data_struct!)]
996#[api(eq, default, ts(into_wasm_abi, from_wasm_abi))]
997pub struct VeilidConfig {
998 pub program_name: String,
1007 pub namespace: String,
1015 pub capabilities: VeilidConfigCapabilities,
1017 pub protected_store: VeilidConfigProtectedStore,
1019 pub table_store: VeilidConfigTableStore,
1021 pub block_store: VeilidConfigBlockStore,
1023 pub network: VeilidConfigNetwork,
1025 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), tsify(optional))]
1028 pub internal: Option<VeilidConfigInternal>,
1029}
1030
1031impl VeilidConfig {
1032 pub fn internal(&self) -> &VeilidConfigInternal {
1035 static DEFAULT: std::sync::OnceLock<VeilidConfigInternal> = std::sync::OnceLock::new();
1036 let default = || DEFAULT.get_or_init(VeilidConfigInternal::default);
1037 #[cfg(feature = "footgun-config")]
1038 {
1039 self.internal.as_ref().unwrap_or_else(default)
1040 }
1041 #[cfg(not(feature = "footgun-config"))]
1042 {
1043 let _ = &self.internal;
1044 default()
1045 }
1046 }
1047 pub fn new(
1064 program_name: &str,
1065 organization: &str,
1066 qualifier: &str,
1067 storage_directory: Option<&str>,
1068 config_directory: Option<&str>,
1069 ) -> Self {
1070 let mut out = Self {
1071 program_name: program_name.to_owned(),
1072 ..Default::default()
1073 };
1074
1075 if let Some(storage_directory) = storage_directory {
1076 out.protected_store.directory = (std::path::PathBuf::from(storage_directory)
1077 .join("protected_store"))
1078 .to_string_lossy()
1079 .to_string();
1080 out.table_store.directory = (std::path::PathBuf::from(storage_directory)
1081 .join("table_store"))
1082 .to_string_lossy()
1083 .to_string();
1084 out.block_store.directory = (std::path::PathBuf::from(storage_directory)
1085 .join("block_store"))
1086 .to_string_lossy()
1087 .to_string();
1088 } else {
1089 out.protected_store.directory =
1090 get_default_store_path(program_name, organization, qualifier, "protected_store");
1091 out.table_store.directory =
1092 get_default_store_path(program_name, organization, qualifier, "table_store");
1093 out.block_store.directory =
1094 get_default_store_path(program_name, organization, qualifier, "block_store");
1095 }
1096
1097 if let Some(config_directory) = config_directory {
1098 out.network.tls.certificate_path = (std::path::PathBuf::from(config_directory)
1099 .join("ssl/certs/server.crt"))
1100 .to_string_lossy()
1101 .to_string();
1102 out.network.tls.private_key_path = (std::path::PathBuf::from(config_directory)
1103 .join("ssl/keys/server.key"))
1104 .to_string_lossy()
1105 .to_string();
1106 } else {
1107 out.network.tls.certificate_path = get_default_ssl_directory(
1108 program_name,
1109 organization,
1110 qualifier,
1111 "certs/server.crt",
1112 );
1113 out.network.tls.private_key_path =
1114 get_default_ssl_directory(program_name, organization, qualifier, "keys/server.key");
1115 }
1116
1117 out
1118 }
1119
1120 #[must_use]
1122 pub fn safe(&self) -> Arc<VeilidConfig> {
1123 let mut safe_cfg = self.clone();
1124
1125 safe_cfg.network.routing_table.secret_keys = SecretKeyGroup::new();
1127 "".clone_into(&mut safe_cfg.protected_store.device_encryption_key_password);
1128 safe_cfg.protected_store.new_device_encryption_key_password = None;
1129
1130 Arc::new(safe_cfg)
1131 }
1132
1133 pub fn get_key_json(&self, key: &str, pretty: bool) -> VeilidAPIResult<String> {
1135 let jvc = serde_json::to_value(self).map_err(VeilidAPIError::generic)?;
1137
1138 if key.is_empty() {
1140 Ok(if pretty {
1141 serde_json::to_string_pretty(&jvc).map_err(VeilidAPIError::generic)?
1142 } else {
1143 serde_json::to_string(&jvc).map_err(VeilidAPIError::generic)?
1144 })
1145 } else {
1146 let keypath: Vec<&str> = key.split('.').collect();
1148 let mut out = &jvc;
1149 for k in keypath {
1150 let Some(next_out) = out.get(k) else {
1151 apibail_parse_error!(format!("invalid subkey in key '{}'", key), k);
1152 };
1153 out = next_out;
1154 }
1155 if pretty {
1156 serde_json::to_string_pretty(out).map_err(VeilidAPIError::generic)
1157 } else {
1158 serde_json::to_string(out).map_err(VeilidAPIError::generic)
1159 }
1160 }
1161 }
1162
1163 fn is_valid_filename(s: &str) -> bool {
1165 if s.len() > 255 {
1166 return false;
1167 }
1168 if s.chars().any(|c| {
1169 matches!(c, '/' | '?' | '<' | '>' | '\\' | ':' | '*' | '|' | '"')
1170 || c <= '\u{1f}'
1171 || ('\u{80}'..='\u{9f}').contains(&c)
1172 }) {
1173 return false;
1174 }
1175 if !s.is_empty() && s.bytes().all(|b| b == b'.') {
1176 return false;
1177 }
1178 if s.ends_with('.') || s.ends_with(' ') {
1179 return false;
1180 }
1181 let base = s.split('.').next().unwrap_or_default().as_bytes();
1182 match base.len() {
1183 3 => {
1184 !(base.eq_ignore_ascii_case(b"con")
1185 || base.eq_ignore_ascii_case(b"prn")
1186 || base.eq_ignore_ascii_case(b"aux")
1187 || base.eq_ignore_ascii_case(b"nul"))
1188 }
1189 4 => {
1190 !((base[..3].eq_ignore_ascii_case(b"com")
1191 || base[..3].eq_ignore_ascii_case(b"lpt"))
1192 && base[3].is_ascii_digit())
1193 }
1194 _ => true,
1195 }
1196 }
1197
1198 fn validate_program_name(program_name: &str) -> VeilidAPIResult<()> {
1199 if program_name.is_empty() {
1200 apibail_generic!("Program name must not be empty in 'program_name'");
1201 }
1202 if !Self::is_valid_filename(program_name) {
1203 apibail_generic!("'program_name' must not be an invalid filename");
1204 }
1205 Ok(())
1206 }
1207
1208 fn validate_namespace(namespace: &str) -> VeilidAPIResult<()> {
1209 if namespace.is_empty() {
1210 return Ok(());
1211 }
1212 if !Self::is_valid_filename(namespace) {
1213 apibail_generic!("'namespace' must not be an invalid filename");
1214 }
1215
1216 Ok(())
1217 }
1218
1219 fn validate_max_connections(max_connections: u32, key: &str) -> VeilidAPIResult<()> {
1220 if !(MAX_CONNECTIONS_MIN..=MAX_CONNECTIONS_MAX).contains(&max_connections) {
1221 apibail_generic!(format!(
1222 "max connections must be in the range {}-{} in config key '{}'",
1223 MAX_CONNECTIONS_MIN, MAX_CONNECTIONS_MAX, key
1224 ));
1225 }
1226 Ok(())
1227 }
1228
1229 pub fn validate(&self) -> VeilidAPIResult<()> {
1231 Self::validate_program_name(&self.program_name)?;
1232 Self::validate_namespace(&self.namespace)?;
1233
1234 Self::validate_max_connections(self.network.max_connections, "network.max_connections")?;
1236
1237 #[cfg(feature = "enable-protocol-wss")]
1241 if self.network.protocol.wss.listen {
1242 if self
1244 .network
1245 .protocol
1246 .wss
1247 .url
1248 .as_ref()
1249 .map(|u| u.is_empty())
1250 .unwrap_or_default()
1251 {
1252 apibail_generic!(
1253 "WSS URL must be specified in config key 'network.protocol.wss.url'"
1254 );
1255 }
1256 }
1257 if self.internal().network.rpc.max_route_hop_count == 0 {
1258 apibail_generic!(
1259 "max route hop count must be >= 1 in 'network.rpc.max_route_hop_count'"
1260 );
1261 }
1262 if self.internal().network.rpc.max_route_hop_count > 5 {
1263 apibail_generic!(
1264 "max route hop count must be <= 5 in 'network.rpc.max_route_hop_count'"
1265 );
1266 }
1267 if self.network.rpc.default_route_hop_count == 0 {
1268 apibail_generic!(
1269 "default route hop count must be >= 1 in 'network.rpc.default_route_hop_count'"
1270 );
1271 }
1272 if self.network.rpc.default_route_hop_count
1273 > self.internal().network.rpc.max_route_hop_count
1274 {
1275 apibail_generic!(
1276 "default route hop count must be <= max route hop count in 'network.rpc.default_route_hop_count <= network.rpc.max_route_hop_count'"
1277 );
1278 }
1279 if self.internal().network.rpc.queue_size < 256 {
1280 apibail_generic!("rpc queue size must be >= 256 in 'network.rpc.queue_size'");
1281 }
1282 if self.internal().network.rpc.timeout_ms < 1000 {
1283 apibail_generic!("rpc timeout must be >= 1000 in 'network.rpc.timeout_ms'");
1284 }
1285 if self.internal().network.dht.consensus_width < self.internal().network.dht.set_value_count
1286 {
1287 apibail_generic!(
1288 "consensus width must be >= set value count in 'network.dht.consensus_width'"
1289 );
1290 }
1291 if self.internal().network.dht.get_value_count
1292 <= (self.internal().network.dht.set_value_count / 2)
1293 {
1294 apibail_generic!("get consensus count must be >= (set value count / 2) in 'network.dht.get_value_count'");
1295 }
1296 if self.internal().network.dht.get_value_fanout < 1 {
1297 apibail_generic!("get value fanout must be >= 1 in 'network.dht.get_value_fanout'");
1298 }
1299 if self.internal().network.dht.set_value_fanout < 1 {
1300 apibail_generic!("set value fanout must be >= 1 in 'network.dht.set_value_fanout'");
1301 }
1302 if self.internal().network.dht.get_value_timeout_ms
1303 < (2 * self.internal().network.rpc.timeout_ms)
1304 {
1305 apibail_generic!("get value timeout must be >= (2 * the rpc timeout) in 'network.dht.get_value_timeout_ms'");
1306 }
1307 if self.internal().network.dht.set_value_timeout_ms
1308 < (2 * self.internal().network.rpc.timeout_ms)
1309 {
1310 apibail_generic!("set value timeout must be >= (2 * the rpc timeout) in 'network.dht.set_value_timeout_ms'");
1311 }
1312
1313 if self.internal().network.dht.public_watch_limit < 1 {
1314 apibail_generic!("public watch limit must be >= 1 in 'network.dht.public_watch_limit'");
1315 }
1316 if self.internal().network.dht.member_watch_limit < 1 {
1317 apibail_generic!("member watch limit must be >= 1 in 'network.dht.member_watch_limit'");
1318 }
1319 if self.internal().network.dht.max_watch_expiration_ms
1320 < (2 * self.internal().network.rpc.timeout_ms)
1321 {
1322 apibail_generic!("max watch expiration must be >= (2 * rpc timeout) 'network.dht.max_watch_expiration_ms'");
1323 }
1324 if self.internal().network.dht.public_transaction_limit < 1 {
1325 apibail_generic!(
1326 "public transaction limit must be >= 1 in 'network.dht.public_transaction_limit'"
1327 );
1328 }
1329 if self.internal().network.dht.member_transaction_limit < 1 {
1330 apibail_generic!(
1331 "member transaction limit must be >= 1 in 'network.dht.member_transaction_limit'"
1332 );
1333 }
1334
1335 Ok(())
1336 }
1337}
1338
1339#[derive(Clone)]
1341#[must_use]
1342pub struct VeilidStartupOptions {
1343 update_cb: UpdateCallback,
1344 config: Arc<VeilidConfig>,
1345}
1346
1347impl fmt::Debug for VeilidStartupOptions {
1348 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1349 f.debug_struct("VeilidConfig")
1350 .field("config", self.config.as_ref())
1351 .finish()
1352 }
1353}
1354
1355impl VeilidStartupOptions {
1356 pub(crate) fn try_new(
1357 config: VeilidConfig,
1358 update_cb: UpdateCallback,
1359 ) -> VeilidAPIResult<Self> {
1360 config.validate()?;
1361
1362 Ok(Self {
1363 update_cb,
1364 config: Arc::new(config),
1365 })
1366 }
1367
1368 #[must_use]
1370 pub fn update_callback(&self) -> UpdateCallback {
1371 self.update_cb.clone()
1372 }
1373
1374 #[must_use]
1376 pub fn config(&self) -> Arc<VeilidConfig> {
1377 self.config.clone()
1378 }
1379}
1380
1381#[must_use]
1383pub fn default_veilid_config() -> String {
1384 serialize_json(VeilidConfig::default())
1385}