1use crate::*;
2
3cfg_if::cfg_if! {
4 if #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] {
5 use sysinfo::System;
6 use lazy_static::*;
7 use directories::ProjectDirs;
8
9 lazy_static! {
10 static ref SYSTEM:System = {
11 sysinfo::System::new_with_specifics(
12 sysinfo::RefreshKind::new().with_memory(sysinfo::MemoryRefreshKind::everything()),
13 )
14 };
15 }
16 }
17}
18
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
30#[cfg_attr(
31 all(target_arch = "wasm32", target_os = "unknown"),
32 derive(Tsify),
33 tsify(into_wasm_abi, from_wasm_abi)
34)]
35#[cfg_attr(feature = "json-camel-case", serde(rename_all = "camelCase"))]
36#[must_use]
37pub struct VeilidConfigHTTPS {
38 pub enabled: bool,
39 pub listen_address: String,
40 pub path: String,
41 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), tsify(optional))]
42 pub url: Option<String>, }
44impl Default for VeilidConfigHTTPS {
45 fn default() -> Self {
46 Self {
47 enabled: false,
48 listen_address: String::from(""),
49 path: String::from("app"),
50 url: None,
51 }
52 }
53}
54
55#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
66#[cfg_attr(
67 all(target_arch = "wasm32", target_os = "unknown"),
68 derive(Tsify),
69 tsify(into_wasm_abi, from_wasm_abi)
70)]
71#[cfg_attr(feature = "json-camel-case", serde(rename_all = "camelCase"))]
72#[must_use]
73pub struct VeilidConfigHTTP {
74 pub enabled: bool,
75 pub listen_address: String,
76 pub path: String,
77 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), tsify(optional))]
78 pub url: Option<String>,
79}
80
81impl Default for VeilidConfigHTTP {
82 fn default() -> Self {
83 Self {
84 enabled: false,
85 listen_address: String::from(""),
86 path: String::from("app"),
87 url: None,
88 }
89 }
90}
91
92#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
103#[cfg_attr(
104 all(target_arch = "wasm32", target_os = "unknown"),
105 derive(Tsify),
106 tsify(into_wasm_abi, from_wasm_abi)
107)]
108#[cfg_attr(feature = "json-camel-case", serde(rename_all = "camelCase"))]
109#[must_use]
110pub struct VeilidConfigUDP {
111 pub enabled: bool,
112 pub socket_pool_size: u32,
113 pub listen_address: String,
114 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), tsify(optional))]
115 pub public_address: Option<String>,
116}
117
118impl Default for VeilidConfigUDP {
119 fn default() -> Self {
120 cfg_if::cfg_if! {
121 if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
122 let enabled = false;
123 } else {
124 let enabled = true;
125 }
126 }
127 Self {
128 enabled,
129 socket_pool_size: 0,
130 listen_address: String::from(""),
131 public_address: None,
132 }
133 }
134}
135
136#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
147#[cfg_attr(
148 all(target_arch = "wasm32", target_os = "unknown"),
149 derive(Tsify),
150 tsify(into_wasm_abi, from_wasm_abi)
151)]
152#[cfg_attr(feature = "json-camel-case", serde(rename_all = "camelCase"))]
153#[must_use]
154pub struct VeilidConfigTCP {
155 pub connect: bool,
156 pub listen: bool,
157 pub max_connections: u32,
158 pub listen_address: String,
159 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), tsify(optional))]
160 pub public_address: Option<String>,
161}
162
163impl Default for VeilidConfigTCP {
164 fn default() -> Self {
165 cfg_if::cfg_if! {
166 if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
167 let connect = false;
168 let listen = false;
169 } else {
170 let connect = true;
171 let listen = true;
172 }
173 }
174 Self {
175 connect,
176 listen,
177 max_connections: 32,
178 listen_address: String::from(""),
179 public_address: None,
180 }
181 }
182}
183
184#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
196#[cfg_attr(
197 all(target_arch = "wasm32", target_os = "unknown"),
198 derive(Tsify),
199 tsify(into_wasm_abi, from_wasm_abi)
200)]
201#[cfg_attr(feature = "json-camel-case", serde(rename_all = "camelCase"))]
202#[must_use]
203pub struct VeilidConfigWS {
204 pub connect: bool,
205 pub listen: bool,
206 pub max_connections: u32,
207 pub listen_address: String,
208 pub path: String,
209 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), tsify(optional))]
210 pub url: Option<String>,
211}
212
213impl Default for VeilidConfigWS {
214 fn default() -> Self {
215 cfg_if::cfg_if! {
216 if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
217 let connect = true;
218 let listen = false;
219 } else {
220 let connect = true;
221 let listen = true;
222 }
223 }
224 Self {
225 connect,
226 listen,
227 max_connections: 32,
228 listen_address: String::from(""),
229 path: String::from("ws"),
230 url: None,
231 }
232 }
233}
234
235#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
247#[cfg_attr(
248 all(target_arch = "wasm32", target_os = "unknown"),
249 derive(Tsify),
250 tsify(into_wasm_abi, from_wasm_abi)
251)]
252#[cfg_attr(feature = "json-camel-case", serde(rename_all = "camelCase"))]
253#[must_use]
254#[cfg(feature = "enable-protocol-wss")]
255pub struct VeilidConfigWSS {
256 pub connect: bool,
257 pub listen: bool,
258 pub max_connections: u32,
259 pub listen_address: String,
260 pub path: String,
261 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), tsify(optional))]
262 pub url: Option<String>, }
264
265#[cfg(feature = "enable-protocol-wss")]
266impl Default for VeilidConfigWSS {
267 fn default() -> Self {
268 Self {
269 connect: true,
270 listen: false,
271 max_connections: 32,
272 listen_address: String::from(""),
273 path: String::from("ws"),
274 url: None,
275 }
276 }
277}
278
279#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
287#[cfg_attr(
288 all(target_arch = "wasm32", target_os = "unknown"),
289 derive(Tsify),
290 tsify(into_wasm_abi, from_wasm_abi)
291)]
292#[cfg_attr(feature = "json-camel-case", serde(rename_all = "camelCase"))]
293#[must_use]
294pub struct VeilidConfigProtocol {
295 pub udp: VeilidConfigUDP,
296 pub tcp: VeilidConfigTCP,
297 pub ws: VeilidConfigWS,
298 #[cfg(feature = "enable-protocol-wss")]
299 pub wss: VeilidConfigWSS,
300}
301
302#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
310#[cfg_attr(
311 target_arch = "wasm32",
312 derive(Tsify),
313 tsify(into_wasm_abi, from_wasm_abi)
314)]
315#[cfg_attr(feature = "json-camel-case", serde(rename_all = "camelCase"))]
316#[must_use]
317pub struct VeilidConfigPrivacy {
318 pub require_inbound_relay: bool,
319 #[cfg(feature = "geolocation")]
320 pub country_code_denylist: Vec<CountryCode>,
321}
322
323#[cfg(feature = "virtual-network")]
331#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
332#[cfg_attr(
333 target_arch = "wasm32",
334 derive(Tsify),
335 tsify(into_wasm_abi, from_wasm_abi)
336)]
337#[cfg_attr(feature = "json-camel-case", serde(rename_all = "camelCase"))]
338#[must_use]
339pub struct VeilidConfigVirtualNetwork {
340 pub enabled: bool,
341 pub server_address: String,
342}
343
344#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
353#[cfg_attr(
354 all(target_arch = "wasm32", target_os = "unknown"),
355 derive(Tsify),
356 tsify(into_wasm_abi, from_wasm_abi)
357)]
358#[cfg_attr(feature = "json-camel-case", serde(rename_all = "camelCase"))]
359#[must_use]
360pub struct VeilidConfigTLS {
361 pub certificate_path: String,
362 pub private_key_path: String,
363 pub connection_initial_timeout_ms: u32,
364}
365
366impl Default for VeilidConfigTLS {
367 fn default() -> Self {
368 Self {
369 certificate_path: "".to_string(),
370 private_key_path: "".to_string(),
371 connection_initial_timeout_ms: 2000,
372 }
373 }
374}
375
376#[cfg_attr(
377 all(target_arch = "wasm32", target_os = "unknown"),
378 allow(unused_variables)
379)]
380#[must_use]
381pub fn get_default_ssl_directory(
382 program_name: &str,
383 organization: &str,
384 qualifier: &str,
385 sub_path: &str,
386) -> String {
387 cfg_if::cfg_if! {
388 if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
389 "".to_owned()
390 } else {
391 use std::path::PathBuf;
392 ProjectDirs::from(qualifier, organization, program_name)
393 .map(|dirs| dirs.data_local_dir().join("ssl").join(sub_path))
394 .unwrap_or_else(|| PathBuf::from("./ssl").join(sub_path))
395 .to_string_lossy()
396 .into()
397 }
398 }
399}
400
401#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
406#[cfg_attr(
407 all(target_arch = "wasm32", target_os = "unknown"),
408 derive(Tsify),
409 tsify(into_wasm_abi, from_wasm_abi)
410)]
411#[cfg_attr(feature = "json-camel-case", serde(rename_all = "camelCase"))]
412#[must_use]
413pub struct VeilidConfigDHT {
414 pub max_find_node_count: u32,
415 pub resolve_node_timeout_ms: u32,
416 pub resolve_node_count: u32,
417 pub resolve_node_fanout: u32,
418 pub get_value_timeout_ms: u32,
419 pub get_value_count: u32,
420 pub get_value_fanout: u32,
421 pub set_value_timeout_ms: u32,
422 pub set_value_count: u32,
423 pub set_value_fanout: u32,
424 pub consensus_width: u32,
425 pub min_peer_count: u32,
426 pub min_peer_refresh_time_ms: u32,
427 pub validate_dial_info_receipt_time_ms: u32,
428 pub local_subkey_cache_size: u32,
429 pub local_max_subkey_cache_memory_mb: u32,
430 pub remote_subkey_cache_size: u32,
431 pub remote_max_records: u32,
432 pub remote_max_subkey_cache_memory_mb: u32,
433 pub remote_max_storage_space_mb: u32,
434 pub public_watch_limit: u32,
435 pub member_watch_limit: u32,
436 pub max_watch_expiration_ms: u32,
437 pub public_transaction_limit: u32,
438 pub member_transaction_limit: u32,
439}
440
441impl Default for VeilidConfigDHT {
442 fn default() -> Self {
443 cfg_if::cfg_if! {
444 if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
445 let local_subkey_cache_size = 128;
446 let local_max_subkey_cache_memory_mb = 256;
447 let remote_subkey_cache_size = 64;
448 let remote_max_records = 64;
449 let remote_max_subkey_cache_memory_mb = 256;
450 let remote_max_storage_space_mb = 128;
451 } else {
452 let local_subkey_cache_size = 1024;
453 let local_max_subkey_cache_memory_mb = if sysinfo::IS_SUPPORTED_SYSTEM {
454 (SYSTEM.total_memory() / 32u64 / (1024u64 * 1024u64)) as u32
455 } else {
456 256
457 };
458 let remote_subkey_cache_size = 128;
459 let remote_max_records = 128;
460 let remote_max_subkey_cache_memory_mb = if sysinfo::IS_SUPPORTED_SYSTEM {
461 (SYSTEM.total_memory() / 32u64 / (1024u64 * 1024u64)) as u32
462 } else {
463 256
464 };
465 let remote_max_storage_space_mb = 256;
466 }
467 }
468
469 Self {
470 max_find_node_count: 20,
471 resolve_node_timeout_ms: 10000,
472 resolve_node_count: 1,
473 resolve_node_fanout: 5,
474 get_value_timeout_ms: 10000,
475 get_value_count: 3,
476 get_value_fanout: 5,
477 set_value_timeout_ms: 10000,
478 set_value_count: 5,
479 set_value_fanout: 5,
480 consensus_width: 10,
481 min_peer_count: 20,
482 min_peer_refresh_time_ms: 60000,
483 validate_dial_info_receipt_time_ms: 2000,
484 local_subkey_cache_size,
485 local_max_subkey_cache_memory_mb,
486 remote_subkey_cache_size,
487 remote_max_records,
488 remote_max_subkey_cache_memory_mb,
489 remote_max_storage_space_mb,
490 public_watch_limit: 32,
491 member_watch_limit: 8,
492 max_watch_expiration_ms: 600000,
493 public_transaction_limit: 4,
494 member_transaction_limit: 1,
495 }
496 }
497}
498
499#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
502#[cfg_attr(
503 all(target_arch = "wasm32", target_os = "unknown"),
504 derive(Tsify),
505 tsify(into_wasm_abi, from_wasm_abi)
506)]
507#[cfg_attr(feature = "json-camel-case", serde(rename_all = "camelCase"))]
508#[must_use]
509pub struct VeilidConfigRPC {
510 pub concurrency: u32,
511 pub queue_size: u32,
512 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), tsify(optional))]
513 pub max_timestamp_behind_ms: Option<u32>,
514 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), tsify(optional))]
515 pub max_timestamp_ahead_ms: Option<u32>,
516 pub timeout_ms: u32,
517 pub max_route_hop_count: u8,
518 pub default_route_hop_count: u8,
519}
520
521impl Default for VeilidConfigRPC {
522 fn default() -> Self {
523 Self {
524 concurrency: 0,
525 queue_size: 1024,
526 max_timestamp_behind_ms: Some(10000),
527 max_timestamp_ahead_ms: Some(10000),
528 timeout_ms: 5000,
529 max_route_hop_count: 4,
530 default_route_hop_count: 1,
531 }
532 }
533}
534
535#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
538#[cfg_attr(
539 all(target_arch = "wasm32", target_os = "unknown"),
540 derive(Tsify),
541 tsify(into_wasm_abi, from_wasm_abi)
542)]
543#[cfg_attr(feature = "json-camel-case", serde(rename_all = "camelCase"))]
544#[must_use]
545pub struct VeilidConfigRoutingTable {
546 #[schemars(with = "Vec<String>")]
547 #[cfg_attr(
548 all(target_arch = "wasm32", target_os = "unknown"),
549 tsify(type = "string[]")
550 )]
551 pub public_keys: PublicKeyGroup,
552 #[schemars(with = "Vec<String>")]
553 #[cfg_attr(
554 all(target_arch = "wasm32", target_os = "unknown"),
555 tsify(type = "string[]")
556 )]
557 pub secret_keys: SecretKeyGroup,
558 pub bootstrap: Vec<String>,
559 #[schemars(with = "Vec<String>")]
560 #[cfg_attr(
561 all(target_arch = "wasm32", target_os = "unknown"),
562 tsify(type = "string[]")
563 )]
564 pub bootstrap_keys: Vec<PublicKey>,
565 pub limit_over_attached: u32,
566 pub limit_fully_attached: u32,
567 pub limit_attached_strong: u32,
568 pub limit_attached_good: u32,
569 pub limit_attached_weak: u32,
570 }
573
574impl Default for VeilidConfigRoutingTable {
575 fn default() -> Self {
576 cfg_if::cfg_if! {
577 if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
578 let bootstrap = vec!["ws://bootstrap-v1.veilid.net:5150/ws".to_string()];
579 } else {
580 let bootstrap = vec!["bootstrap-v1.veilid.net".to_string()];
581 }
582 }
583 let bootstrap_keys = vec![
584 PublicKey::from_str("VLD0:Vj0lKDdUQXmQ5Ol1SZdlvXkBHUccBcQvGLN9vbLSI7k").unwrap_or_log(),
586 PublicKey::from_str("VLD0:QeQJorqbXtC7v3OlynCZ_W3m76wGNeB5NTF81ypqHAo").unwrap_or_log(),
588 PublicKey::from_str("VLD0:QNdcl-0OiFfYVj9331XVR6IqZ49NG-E18d5P7lwi4TA").unwrap_or_log(),
590 ];
591
592 Self {
593 public_keys: PublicKeyGroup::default(),
594 secret_keys: SecretKeyGroup::default(),
595 bootstrap,
596 bootstrap_keys,
597 limit_over_attached: 64,
598 limit_fully_attached: 32,
599 limit_attached_strong: 16,
600 limit_attached_good: 8,
601 limit_attached_weak: 4,
602 }
603 }
604}
605
606#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
607#[cfg_attr(
608 all(target_arch = "wasm32", target_os = "unknown"),
609 derive(Tsify),
610 tsify(into_wasm_abi, from_wasm_abi)
611)]
612#[cfg_attr(feature = "json-camel-case", serde(rename_all = "camelCase"))]
613#[must_use]
614pub struct VeilidConfigNetwork {
615 pub connection_initial_timeout_ms: u32,
616 pub connection_inactivity_timeout_ms: u32,
617 pub max_connections_per_ip4: u32,
618 pub max_connections_per_ip6_prefix: u32,
619 pub max_connections_per_ip6_prefix_size: u32,
620 pub max_connection_frequency_per_min: u32,
621 pub client_allowlist_timeout_ms: u32,
622 pub reverse_connection_receipt_time_ms: u32,
623 pub hole_punch_receipt_time_ms: u32,
624 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), tsify(optional))]
625 pub network_key_password: Option<String>,
626 pub routing_table: VeilidConfigRoutingTable,
627 pub rpc: VeilidConfigRPC,
628 pub dht: VeilidConfigDHT,
629 pub upnp: bool,
630 pub detect_address_changes: Option<bool>,
631 pub restricted_nat_retries: u32,
632 pub tls: VeilidConfigTLS,
633 pub protocol: VeilidConfigProtocol,
634 pub privacy: VeilidConfigPrivacy,
635 #[cfg(feature = "virtual-network")]
636 pub virtual_network: VeilidConfigVirtualNetwork,
637}
638
639impl Default for VeilidConfigNetwork {
640 fn default() -> Self {
641 Self {
642 connection_initial_timeout_ms: 2000,
643 connection_inactivity_timeout_ms: 60000,
644 max_connections_per_ip4: 32,
645 max_connections_per_ip6_prefix: 32,
646 max_connections_per_ip6_prefix_size: 56,
647 max_connection_frequency_per_min: 128,
648 client_allowlist_timeout_ms: 300000,
649 reverse_connection_receipt_time_ms: 5000,
650 hole_punch_receipt_time_ms: 5000,
651 network_key_password: None,
652 routing_table: VeilidConfigRoutingTable::default(),
653 rpc: VeilidConfigRPC::default(),
654 dht: VeilidConfigDHT::default(),
655 upnp: true,
656 detect_address_changes: Some(true),
657 restricted_nat_retries: 0,
658 tls: VeilidConfigTLS::default(),
659 protocol: VeilidConfigProtocol::default(),
660 privacy: VeilidConfigPrivacy::default(),
661 #[cfg(feature = "virtual-network")]
662 virtual_network: VeilidConfigVirtualNetwork::default(),
663 }
664 }
665}
666
667#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
668#[cfg_attr(
669 all(target_arch = "wasm32", target_os = "unknown"),
670 derive(Tsify),
671 tsify(into_wasm_abi, from_wasm_abi)
672)]
673#[cfg_attr(feature = "json-camel-case", serde(rename_all = "camelCase"))]
674#[must_use]
675pub struct VeilidConfigTableStore {
676 pub directory: String,
677 pub delete: bool,
678}
679
680#[cfg_attr(
681 all(target_arch = "wasm32", target_os = "unknown"),
682 allow(unused_variables)
683)]
684#[must_use]
685fn get_default_store_path(
686 program_name: &str,
687 organization: &str,
688 qualifier: &str,
689 store_type: &str,
690) -> String {
691 cfg_if::cfg_if! {
692 if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
693 "".to_owned()
694 } else {
695 use std::path::PathBuf;
696 ProjectDirs::from(qualifier, organization, program_name)
697 .map(|dirs| dirs.data_local_dir().to_path_buf())
698 .unwrap_or_else(|| PathBuf::from("./"))
699 .join(store_type)
700 .to_string_lossy()
701 .into()
702 }
703 }
704}
705
706#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
707#[cfg_attr(
708 all(target_arch = "wasm32", target_os = "unknown"),
709 derive(Tsify),
710 tsify(into_wasm_abi, from_wasm_abi)
711)]
712#[cfg_attr(feature = "json-camel-case", serde(rename_all = "camelCase"))]
713#[must_use]
714pub struct VeilidConfigBlockStore {
715 pub directory: String,
716 pub delete: bool,
717}
718
719impl Default for VeilidConfigBlockStore {
720 fn default() -> Self {
721 Self {
722 directory: "".to_string(),
723 delete: false,
724 }
725 }
726}
727
728#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
729#[cfg_attr(
730 all(target_arch = "wasm32", target_os = "unknown"),
731 derive(Tsify),
732 tsify(into_wasm_abi, from_wasm_abi)
733)]
734#[cfg_attr(feature = "json-camel-case", serde(rename_all = "camelCase"))]
735#[must_use]
736pub struct VeilidConfigProtectedStore {
737 pub allow_insecure_fallback: bool,
738 pub always_use_insecure_storage: bool,
739 pub directory: String,
740 pub delete: bool,
741 pub device_encryption_key_password: String,
742 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), tsify(optional))]
743 pub new_device_encryption_key_password: Option<String>,
744}
745
746impl Default for VeilidConfigProtectedStore {
747 fn default() -> Self {
748 Self {
749 allow_insecure_fallback: false,
750 always_use_insecure_storage: false,
751 directory: "".to_string(),
752 delete: false,
753 device_encryption_key_password: "".to_owned(),
754 new_device_encryption_key_password: None,
755 }
756 }
757}
758
759#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
760#[cfg_attr(
761 all(target_arch = "wasm32", target_os = "unknown"),
762 derive(Tsify),
763 tsify(into_wasm_abi, from_wasm_abi)
764)]
765#[cfg_attr(feature = "json-camel-case", serde(rename_all = "camelCase"))]
766#[must_use]
767pub struct VeilidConfigCapabilities {
768 pub disable: Vec<VeilidCapability>,
769}
770
771#[derive(
772 Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize, JsonSchema,
773)]
774#[cfg_attr(
775 all(target_arch = "wasm32", target_os = "unknown"),
776 derive(Tsify),
777 tsify(namespace, into_wasm_abi, from_wasm_abi)
778)]
779#[must_use]
780#[derive(Default)]
781pub enum VeilidConfigLogLevel {
782 #[default]
783 Off,
784 Error,
785 Warn,
786 Info,
787 Debug,
788 Trace,
789}
790
791impl From<VeilidLogLevel> for VeilidConfigLogLevel {
792 fn from(value: VeilidLogLevel) -> Self {
793 match value {
794 VeilidLogLevel::Error => Self::Error,
795 VeilidLogLevel::Warn => Self::Warn,
796 VeilidLogLevel::Info => Self::Info,
797 VeilidLogLevel::Debug => Self::Debug,
798 VeilidLogLevel::Trace => Self::Trace,
799 }
800 }
801}
802
803impl From<Option<VeilidLogLevel>> for VeilidConfigLogLevel {
804 fn from(value: Option<VeilidLogLevel>) -> Self {
805 match value {
806 None => Self::Off,
807 Some(VeilidLogLevel::Error) => Self::Error,
808 Some(VeilidLogLevel::Warn) => Self::Warn,
809 Some(VeilidLogLevel::Info) => Self::Info,
810 Some(VeilidLogLevel::Debug) => Self::Debug,
811 Some(VeilidLogLevel::Trace) => Self::Trace,
812 }
813 }
814}
815
816impl From<tracing::level_filters::LevelFilter> for VeilidConfigLogLevel {
817 fn from(value: tracing::level_filters::LevelFilter) -> Self {
818 match value {
819 tracing::level_filters::LevelFilter::OFF => Self::Off,
820 tracing::level_filters::LevelFilter::ERROR => Self::Error,
821 tracing::level_filters::LevelFilter::WARN => Self::Warn,
822 tracing::level_filters::LevelFilter::INFO => Self::Info,
823 tracing::level_filters::LevelFilter::DEBUG => Self::Debug,
824 tracing::level_filters::LevelFilter::TRACE => Self::Trace,
825 }
826 }
827}
828
829impl From<VeilidConfigLogLevel> for tracing::level_filters::LevelFilter {
830 fn from(val: VeilidConfigLogLevel) -> Self {
831 match val {
832 VeilidConfigLogLevel::Off => tracing::level_filters::LevelFilter::OFF,
833 VeilidConfigLogLevel::Error => tracing::level_filters::LevelFilter::ERROR,
834 VeilidConfigLogLevel::Warn => tracing::level_filters::LevelFilter::WARN,
835 VeilidConfigLogLevel::Info => tracing::level_filters::LevelFilter::INFO,
836 VeilidConfigLogLevel::Debug => tracing::level_filters::LevelFilter::DEBUG,
837 VeilidConfigLogLevel::Trace => tracing::level_filters::LevelFilter::TRACE,
838 }
839 }
840}
841
842impl From<tracing::log::LevelFilter> for VeilidConfigLogLevel {
843 fn from(value: tracing::log::LevelFilter) -> Self {
844 match value {
845 tracing::log::LevelFilter::Off => Self::Off,
846 tracing::log::LevelFilter::Error => Self::Error,
847 tracing::log::LevelFilter::Warn => Self::Warn,
848 tracing::log::LevelFilter::Info => Self::Info,
849 tracing::log::LevelFilter::Debug => Self::Debug,
850 tracing::log::LevelFilter::Trace => Self::Trace,
851 }
852 }
853}
854
855impl From<VeilidConfigLogLevel> for tracing::log::LevelFilter {
856 fn from(val: VeilidConfigLogLevel) -> Self {
857 match val {
858 VeilidConfigLogLevel::Off => tracing::log::LevelFilter::Off,
859 VeilidConfigLogLevel::Error => tracing::log::LevelFilter::Error,
860 VeilidConfigLogLevel::Warn => tracing::log::LevelFilter::Warn,
861 VeilidConfigLogLevel::Info => tracing::log::LevelFilter::Info,
862 VeilidConfigLogLevel::Debug => tracing::log::LevelFilter::Debug,
863 VeilidConfigLogLevel::Trace => tracing::log::LevelFilter::Trace,
864 }
865 }
866}
867
868impl TryFrom<&str> for VeilidConfigLogLevel {
869 type Error = VeilidAPIError;
870
871 fn try_from(value: &str) -> Result<Self, <Self as TryFrom<&str>>::Error> {
872 Self::from_str(value)
873 }
874}
875
876impl TryFrom<String> for VeilidConfigLogLevel {
877 type Error = VeilidAPIError;
878
879 fn try_from(value: String) -> Result<Self, <Self as TryFrom<String>>::Error> {
880 Self::from_str(value.as_str())
881 }
882}
883
884impl TryFrom<&String> for VeilidConfigLogLevel {
885 type Error = VeilidAPIError;
886
887 fn try_from(value: &String) -> Result<Self, <Self as TryFrom<&String>>::Error> {
888 Self::from_str(value.as_str())
889 }
890}
891
892impl FromStr for VeilidConfigLogLevel {
893 type Err = VeilidAPIError;
894 fn from_str(s: &str) -> Result<Self, Self::Err> {
895 Ok(match s.to_ascii_lowercase().as_str() {
896 "off" => Self::Off,
897 "error" => Self::Error,
898 "warn" => Self::Warn,
899 "info" => Self::Info,
900 "debug" => Self::Debug,
901 "trace" => Self::Trace,
902 _ => {
903 apibail_invalid_argument!("invalid VeilidConfigLogLevel string", "s", s);
904 }
905 })
906 }
907}
908impl fmt::Display for VeilidConfigLogLevel {
909 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
910 let text = match self {
911 Self::Off => "Off",
912 Self::Error => "Error",
913 Self::Warn => "Warn",
914 Self::Info => "Info",
915 Self::Debug => "Debug",
916 Self::Trace => "Trace",
917 };
918 write!(f, "{}", text)
919 }
920}
921
922#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
924#[cfg_attr(
925 all(target_arch = "wasm32", target_os = "unknown"),
926 derive(Tsify),
927 tsify(into_wasm_abi, from_wasm_abi)
928)]
929#[cfg_attr(feature = "json-camel-case", serde(rename_all = "camelCase"))]
930#[must_use]
931pub struct VeilidConfig {
932 pub program_name: String,
941 pub namespace: String,
949 pub capabilities: VeilidConfigCapabilities,
951 pub protected_store: VeilidConfigProtectedStore,
953 pub table_store: VeilidConfigTableStore,
955 pub block_store: VeilidConfigBlockStore,
957 pub network: VeilidConfigNetwork,
959}
960
961impl VeilidConfig {
962 pub fn new(
979 program_name: &str,
980 organization: &str,
981 qualifier: &str,
982 storage_directory: Option<&str>,
983 config_directory: Option<&str>,
984 ) -> Self {
985 let mut out = Self {
986 program_name: program_name.to_owned(),
987 ..Default::default()
988 };
989
990 if let Some(storage_directory) = storage_directory {
991 out.protected_store.directory = (std::path::PathBuf::from(storage_directory)
992 .join("protected_store"))
993 .to_string_lossy()
994 .to_string();
995 out.table_store.directory = (std::path::PathBuf::from(storage_directory)
996 .join("table_store"))
997 .to_string_lossy()
998 .to_string();
999 out.block_store.directory = (std::path::PathBuf::from(storage_directory)
1000 .join("block_store"))
1001 .to_string_lossy()
1002 .to_string();
1003 } else {
1004 out.protected_store.directory =
1005 get_default_store_path(program_name, organization, qualifier, "protected_store");
1006 out.table_store.directory =
1007 get_default_store_path(program_name, organization, qualifier, "table_store");
1008 out.block_store.directory =
1009 get_default_store_path(program_name, organization, qualifier, "block_store");
1010 }
1011
1012 if let Some(config_directory) = config_directory {
1013 out.network.tls.certificate_path = (std::path::PathBuf::from(config_directory)
1014 .join("ssl/certs/server.crt"))
1015 .to_string_lossy()
1016 .to_string();
1017 out.network.tls.private_key_path = (std::path::PathBuf::from(config_directory)
1018 .join("ssl/keys/server.key"))
1019 .to_string_lossy()
1020 .to_string();
1021 } else {
1022 out.network.tls.certificate_path = get_default_ssl_directory(
1023 program_name,
1024 organization,
1025 qualifier,
1026 "certs/server.crt",
1027 );
1028 out.network.tls.private_key_path =
1029 get_default_ssl_directory(program_name, organization, qualifier, "keys/server.key");
1030 }
1031
1032 out
1033 }
1034
1035 #[must_use]
1036 pub fn safe(&self) -> Arc<VeilidConfig> {
1037 let mut safe_cfg = self.clone();
1038
1039 safe_cfg.network.routing_table.secret_keys = SecretKeyGroup::new();
1041 "".clone_into(&mut safe_cfg.protected_store.device_encryption_key_password);
1042 safe_cfg.protected_store.new_device_encryption_key_password = None;
1043
1044 Arc::new(safe_cfg)
1045 }
1046
1047 pub fn get_key_json(&self, key: &str, pretty: bool) -> VeilidAPIResult<String> {
1048 let jc = serde_json::to_string(self).map_err(VeilidAPIError::generic)?;
1050 let jvc = json::parse(&jc).map_err(VeilidAPIError::generic)?;
1051
1052 if key.is_empty() {
1054 Ok(if pretty {
1055 jvc.pretty(2)
1056 } else {
1057 jvc.to_string()
1058 })
1059 } else {
1060 let keypath: Vec<&str> = key.split('.').collect();
1062 let mut out = &jvc;
1063 for k in keypath {
1064 if !out.has_key(k) {
1065 apibail_parse_error!(format!("invalid subkey in key '{}'", key), k);
1066 }
1067 out = &out[k];
1068 }
1069 Ok(if pretty {
1070 out.pretty(2)
1071 } else {
1072 out.to_string()
1073 })
1074 }
1075 }
1076
1077 fn validate_program_name(program_name: &str) -> VeilidAPIResult<()> {
1078 if program_name.is_empty() {
1079 apibail_generic!("Program name must not be empty in 'program_name'");
1080 }
1081 if !sanitize_filename::is_sanitized_with_options(
1082 program_name,
1083 sanitize_filename::OptionsForCheck {
1084 windows: true,
1085 truncate: true,
1086 },
1087 ) {
1088 apibail_generic!("'program_name' must not be an invalid filename");
1089 }
1090 Ok(())
1091 }
1092
1093 fn validate_namespace(namespace: &str) -> VeilidAPIResult<()> {
1094 if namespace.is_empty() {
1095 return Ok(());
1096 }
1097 if !sanitize_filename::is_sanitized_with_options(
1098 namespace,
1099 sanitize_filename::OptionsForCheck {
1100 windows: true,
1101 truncate: true,
1102 },
1103 ) {
1104 apibail_generic!("'namespace' must not be an invalid filename");
1105 }
1106
1107 Ok(())
1108 }
1109
1110 pub fn validate(&self) -> VeilidAPIResult<()> {
1111 Self::validate_program_name(&self.program_name)?;
1112 Self::validate_namespace(&self.namespace)?;
1113
1114 if self.network.protocol.tcp.listen {
1118 if self.network.protocol.tcp.max_connections == 0 {
1120 apibail_generic!("TCP max connections must be > 0 in config key 'network.protocol.tcp.max_connections'");
1121 }
1122 }
1123 if self.network.protocol.ws.listen {
1124 if self.network.protocol.ws.max_connections == 0 {
1126 apibail_generic!("WS max connections must be > 0 in config key 'network.protocol.ws.max_connections'");
1127 }
1128 }
1129 #[cfg(feature = "enable-protocol-wss")]
1130 if self.network.protocol.wss.listen {
1131 if self.network.protocol.wss.max_connections == 0 {
1133 apibail_generic!("WSS max connections must be > 0 in config key 'network.protocol.wss.max_connections'");
1134 }
1135 if self
1136 .network
1137 .protocol
1138 .wss
1139 .url
1140 .as_ref()
1141 .map(|u| u.is_empty())
1142 .unwrap_or_default()
1143 {
1144 apibail_generic!(
1145 "WSS URL must be specified in config key 'network.protocol.wss.url'"
1146 );
1147 }
1148 }
1149 if self.network.rpc.max_route_hop_count == 0 {
1150 apibail_generic!(
1151 "max route hop count must be >= 1 in 'network.rpc.max_route_hop_count'"
1152 );
1153 }
1154 if self.network.rpc.max_route_hop_count > 5 {
1155 apibail_generic!(
1156 "max route hop count must be <= 5 in 'network.rpc.max_route_hop_count'"
1157 );
1158 }
1159 if self.network.rpc.default_route_hop_count == 0 {
1160 apibail_generic!(
1161 "default route hop count must be >= 1 in 'network.rpc.default_route_hop_count'"
1162 );
1163 }
1164 if self.network.rpc.default_route_hop_count > self.network.rpc.max_route_hop_count {
1165 apibail_generic!(
1166 "default route hop count must be <= max route hop count in 'network.rpc.default_route_hop_count <= network.rpc.max_route_hop_count'"
1167 );
1168 }
1169 if self.network.rpc.queue_size < 256 {
1170 apibail_generic!("rpc queue size must be >= 256 in 'network.rpc.queue_size'");
1171 }
1172 if self.network.rpc.timeout_ms < 1000 {
1173 apibail_generic!("rpc timeout must be >= 1000 in 'network.rpc.timeout_ms'");
1174 }
1175 if self.network.dht.consensus_width < self.network.dht.set_value_count {
1176 apibail_generic!(
1177 "consensus width must be >= set value count in 'network.dht.consensus_width'"
1178 );
1179 }
1180 if self.network.dht.get_value_count <= (self.network.dht.set_value_count / 2) {
1181 apibail_generic!("get consensus count must be >= (set value count / 2) in 'network.dht.get_value_count'");
1182 }
1183 if self.network.dht.get_value_fanout < 1 {
1184 apibail_generic!("get value fanout must be >= 1 in 'network.dht.get_value_fanout'");
1185 }
1186 if self.network.dht.set_value_fanout < 1 {
1187 apibail_generic!("set value fanout must be >= 1 in 'network.dht.set_value_fanout'");
1188 }
1189 if self.network.dht.get_value_timeout_ms < (2 * self.network.rpc.timeout_ms) {
1190 apibail_generic!("get value timeout must be >= (2 * the rpc timeout) in 'network.dht.get_value_timeout_ms'");
1191 }
1192 if self.network.dht.set_value_timeout_ms < (2 * self.network.rpc.timeout_ms) {
1193 apibail_generic!("set value timeout must be >= (2 * the rpc timeout) in 'network.dht.set_value_timeout_ms'");
1194 }
1195
1196 if self.network.dht.public_watch_limit < 1 {
1197 apibail_generic!("public watch limit must be >= 1 in 'network.dht.public_watch_limit'");
1198 }
1199 if self.network.dht.member_watch_limit < 1 {
1200 apibail_generic!("member watch limit must be >= 1 in 'network.dht.member_watch_limit'");
1201 }
1202 if self.network.dht.max_watch_expiration_ms < (2 * self.network.rpc.timeout_ms) {
1203 apibail_generic!("max watch expiration must be >= (2 * rpc timeout) 'network.dht.max_watch_expiration_ms'");
1204 }
1205 if self.network.dht.public_transaction_limit < 1 {
1206 apibail_generic!(
1207 "public transaction limit must be >= 1 in 'network.dht.public_transaction_limit'"
1208 );
1209 }
1210 if self.network.dht.member_transaction_limit < 1 {
1211 apibail_generic!(
1212 "member transaction limit must be >= 1 in 'network.dht.member_transaction_limit'"
1213 );
1214 }
1215
1216 Ok(())
1217 }
1218}
1219
1220#[derive(Clone)]
1222#[must_use]
1223pub struct VeilidStartupOptions {
1224 update_cb: UpdateCallback,
1225 config: Arc<VeilidConfig>,
1226}
1227
1228impl fmt::Debug for VeilidStartupOptions {
1229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1230 f.debug_struct("VeilidConfig")
1231 .field("config", self.config.as_ref())
1232 .finish()
1233 }
1234}
1235
1236impl VeilidStartupOptions {
1237 pub(crate) fn try_new(
1238 config: VeilidConfig,
1239 update_cb: UpdateCallback,
1240 ) -> VeilidAPIResult<Self> {
1241 config.validate()?;
1242
1243 Ok(Self {
1244 update_cb,
1245 config: Arc::new(config),
1246 })
1247 }
1248
1249 #[must_use]
1250 pub fn update_callback(&self) -> UpdateCallback {
1251 self.update_cb.clone()
1252 }
1253
1254 #[must_use]
1255 pub fn config(&self) -> Arc<VeilidConfig> {
1256 self.config.clone()
1257 }
1258}
1259
1260#[must_use]
1262pub fn default_veilid_config() -> String {
1263 serialize_json(VeilidConfig::default())
1264}