1use std::fmt;
2
3use serde::{Deserialize, Deserializer, Serialize};
4use serde_json::Value;
5
6fn deserialize_null_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
7where
8 D: Deserializer<'de>,
9 T: Deserialize<'de> + Default,
10{
11 Option::<T>::deserialize(deserializer).map(Option::unwrap_or_default)
12}
13
14#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
16#[serde(rename_all = "lowercase")]
17#[non_exhaustive]
18pub enum ProcessState {
19 Running,
21 Stop,
23 Error,
25 #[default]
27 #[serde(other)]
28 Unknown,
29}
30
31#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
33#[serde(default)]
34pub struct ResourceUsage {
35 pub current: u64,
37 pub total: u64,
39}
40
41#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
43#[serde(default)]
44pub struct DiskIo {
45 pub read: u64,
47 pub write: u64,
49}
50
51#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
53#[serde(default, rename_all = "camelCase")]
54pub struct NetworkIo {
55 pub up: u64,
57 pub down: u64,
59 pub pkt_up: u64,
61 pub pkt_down: u64,
63}
64
65#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
67#[serde(default, rename_all = "camelCase")]
68pub struct NetworkTraffic {
69 pub sent: u64,
71 pub recv: u64,
73 pub pkt_sent: u64,
75 pub pkt_recv: u64,
77}
78
79#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
81#[serde(default, rename_all = "camelCase")]
82pub struct XrayStatus {
83 pub state: ProcessState,
85 pub error_msg: String,
87 pub version: String,
89}
90
91#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
93#[serde(default, rename_all = "camelCase")]
94pub struct AmneziaWgPeerActivity {
95 #[serde(rename = "interface")]
97 pub interface_name: String,
98 pub tag: String,
100 pub inbound_id: i64,
102 pub email: String,
104 pub endpoint: String,
106 #[serde(rename = "allowedIPs")]
108 pub allowed_ips: String,
109 pub handshake: i64,
111 pub up: i64,
113 pub down: i64,
115 pub online: bool,
117}
118
119#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
121#[serde(default)]
122pub struct AmneziaWgLogs {
123 pub peers: Vec<AmneziaWgPeerActivity>,
125 pub events: Vec<String>,
127 pub running: bool,
129}
130
131#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
133#[serde(default)]
134pub struct AmneziaWgStatus {
135 pub configured: bool,
137 pub running: bool,
139}
140
141#[derive(Clone, Deserialize, PartialEq, Serialize)]
147#[serde(transparent)]
148pub struct XrayConfig(Value);
149
150impl XrayConfig {
151 pub const fn as_value(&self) -> &Value {
153 &self.0
154 }
155
156 pub fn into_value(self) -> Value {
158 self.0
159 }
160}
161
162impl From<Value> for XrayConfig {
163 fn from(value: Value) -> Self {
164 Self(value)
165 }
166}
167
168impl fmt::Debug for XrayConfig {
169 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
170 formatter
171 .debug_tuple("XrayConfig")
172 .field(&"[REDACTED]")
173 .finish()
174 }
175}
176
177#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
179#[serde(default)]
180pub struct PublicAddresses {
181 pub ipv4: String,
183 pub ipv6: String,
185}
186
187#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
189#[serde(default)]
190pub struct AppStats {
191 pub threads: u32,
193 pub mem: u64,
195 pub uptime: u64,
197}
198
199#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
201#[serde(default, rename_all = "camelCase")]
202pub struct ServerStatus {
203 pub cpu: f64,
205 pub cpu_cores: i32,
207 pub logical_pro: i32,
209 pub cpu_speed_mhz: f64,
211 pub mem: ResourceUsage,
213 pub swap: ResourceUsage,
215 pub disk: ResourceUsage,
217 #[serde(rename = "diskIO")]
219 pub disk_io: DiskIo,
220 pub disk_traffic: DiskIo,
222 pub xray: XrayStatus,
224 pub amneziawg: AmneziaWgStatus,
226 pub panel_version: String,
228 pub panel_guid: String,
230 pub uptime: u64,
232 #[serde(deserialize_with = "deserialize_null_default")]
234 pub loads: Vec<f64>,
235 pub tcp_count: i32,
237 pub udp_count: i32,
239 #[serde(rename = "netIO")]
241 pub net_io: NetworkIo,
242 pub net_traffic: NetworkTraffic,
244 #[serde(rename = "publicIP")]
246 pub public_ip: PublicAddresses,
247 pub app_stats: AppStats,
249}
250
251#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
257#[non_exhaustive]
258pub enum HistoryBucket {
259 Minutes2,
261 Minutes30,
263 Hour1,
265 Hours3,
267 Hours6,
269 Hours12,
271 Day1,
273 Days2,
275 Days7,
277}
278
279impl HistoryBucket {
280 pub(crate) const fn seconds(self) -> u32 {
281 match self {
282 Self::Minutes2 => 2,
283 Self::Minutes30 => 30,
284 Self::Hour1 => 60,
285 Self::Hours3 => 180,
286 Self::Hours6 => 360,
287 Self::Hours12 => 720,
288 Self::Day1 => 1_440,
289 Self::Days2 => 2_880,
290 Self::Days7 => 10_080,
291 }
292 }
293}
294
295#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
297#[non_exhaustive]
298pub enum SystemMetric {
299 Cpu,
301 Memory,
303 Swap,
305 NetworkUp,
307 NetworkDown,
309 PacketUp,
311 PacketDown,
313 DiskRead,
315 DiskWrite,
317 DiskUsage,
319 TcpCount,
321 UdpCount,
323 Online,
325 Load1,
327 Load5,
329 Load15,
331}
332
333impl SystemMetric {
334 pub(crate) const fn as_str(self) -> &'static str {
335 match self {
336 Self::Cpu => "cpu",
337 Self::Memory => "mem",
338 Self::Swap => "swap",
339 Self::NetworkUp => "netUp",
340 Self::NetworkDown => "netDown",
341 Self::PacketUp => "pktUp",
342 Self::PacketDown => "pktDown",
343 Self::DiskRead => "diskRead",
344 Self::DiskWrite => "diskWrite",
345 Self::DiskUsage => "diskUsage",
346 Self::TcpCount => "tcpCount",
347 Self::UdpCount => "udpCount",
348 Self::Online => "online",
349 Self::Load1 => "load1",
350 Self::Load5 => "load5",
351 Self::Load15 => "load15",
352 }
353 }
354}
355
356#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
358#[non_exhaustive]
359pub enum XrayMetric {
360 Alloc,
362 Sys,
364 HeapObjects,
366 NumGc,
368 PauseNs,
370}
371
372impl XrayMetric {
373 pub(crate) const fn as_str(self) -> &'static str {
374 match self {
375 Self::Alloc => "xrAlloc",
376 Self::Sys => "xrSys",
377 Self::HeapObjects => "xrHeapObjects",
378 Self::NumGc => "xrNumGC",
379 Self::PauseNs => "xrPauseNs",
380 }
381 }
382}
383
384#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize)]
386pub struct MetricPoint {
387 #[serde(rename = "t")]
389 pub timestamp: i64,
390 #[serde(rename = "v")]
392 pub value: f64,
393}
394
395#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize)]
397pub struct LegacyCpuPoint {
398 #[serde(rename = "t")]
400 pub timestamp: i64,
401 pub cpu: f64,
403}
404
405#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
407#[serde(default)]
408pub struct XrayMetricsState {
409 pub enabled: bool,
411 pub listen: String,
413 pub reason: String,
415}
416
417#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
419#[serde(default, rename_all = "camelCase")]
420pub struct XrayObservatoryEntry {
421 pub tag: String,
423 pub alive: bool,
425 pub delay: i64,
427 pub last_seen_time: i64,
429 pub last_try_time: i64,
431 pub updated_at: i64,
433}
434
435#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
437#[serde(default, rename_all = "camelCase")]
438pub struct PanelUpdateInfo {
439 pub channel: String,
441 pub current_version: String,
443 pub latest_version: String,
445 pub current_commit: String,
447 pub latest_commit: String,
449 pub update_available: bool,
451}
452
453#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
455#[serde(default, rename_all = "camelCase")]
456pub struct PanelUpdateRun {
457 pub run_id: String,
459}
460
461#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
463#[serde(rename_all = "lowercase")]
464#[non_exhaustive]
465pub enum PanelUpdateState {
466 Pending,
468 Success,
470 Failed,
472 #[default]
474 #[serde(other)]
475 Unknown,
476}
477
478#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
480#[serde(default, rename_all = "camelCase")]
481pub struct PanelUpdateStatus {
482 pub run_id: String,
484 pub state: PanelUpdateState,
486 pub exit_code: i32,
488 pub finished_at: i64,
490}
491
492#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
494#[serde(default)]
495#[allow(clippy::struct_excessive_bools)]
496pub struct Fail2banStatus {
497 pub enabled: bool,
499 pub installed: bool,
501 pub usable: bool,
503 pub windows: bool,
505}
506
507#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
509#[serde(default, rename_all = "camelCase")]
510pub struct NodeSummary {
511 pub guid: String,
513 pub parent_guid: String,
515 pub name: String,
517 pub address: String,
519 pub scheme: String,
521 pub port: i32,
523 pub status: String,
525 pub last_heartbeat: i64,
527 pub latency_ms: i32,
529 pub panel_version: String,
531 pub xray_version: String,
533 pub xray_state: String,
535 pub xray_error: String,
537}
538
539#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
541pub struct ClientIpObservation {
542 pub ip: String,
544 pub timestamp: i64,
546}
547
548#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
550#[serde(default, rename_all = "camelCase")]
551pub struct ClientIpRecord {
552 pub id: i32,
554 pub client_email: String,
556 #[serde(deserialize_with = "deserialize_null_default")]
558 pub ips: Vec<ClientIpObservation>,
559}
560
561#[derive(Clone, Deserialize, Eq, PartialEq, Serialize)]
563#[serde(rename_all = "camelCase")]
564pub struct X25519KeyPair {
565 pub private_key: String,
567 pub public_key: String,
569}
570
571impl fmt::Debug for X25519KeyPair {
572 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
573 formatter
574 .debug_struct("X25519KeyPair")
575 .field("private_key", &"[REDACTED]")
576 .field("public_key", &self.public_key)
577 .finish()
578 }
579}
580
581#[derive(Clone, Deserialize, Eq, PartialEq, Serialize)]
583pub struct Mldsa65KeyPair {
584 pub seed: String,
586 pub verify: String,
588}
589
590impl fmt::Debug for Mldsa65KeyPair {
591 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
592 formatter
593 .debug_struct("Mldsa65KeyPair")
594 .field("seed", &"[REDACTED]")
595 .field("verify", &self.verify)
596 .finish()
597 }
598}
599
600#[derive(Clone, Deserialize, Eq, PartialEq, Serialize)]
602pub struct Mlkem768KeyPair {
603 pub seed: String,
605 pub client: String,
607}
608
609impl fmt::Debug for Mlkem768KeyPair {
610 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
611 formatter
612 .debug_struct("Mlkem768KeyPair")
613 .field("seed", &"[REDACTED]")
614 .field("client", &self.client)
615 .finish()
616 }
617}
618
619#[derive(Clone, Deserialize, Eq, PartialEq, Serialize)]
621#[serde(rename_all = "camelCase")]
622pub struct EchKeyPair {
623 pub ech_server_keys: String,
625 pub ech_config_list: String,
627}
628
629impl fmt::Debug for EchKeyPair {
630 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
631 formatter
632 .debug_struct("EchKeyPair")
633 .field("ech_server_keys", &"[REDACTED]")
634 .field("ech_config_list", &self.ech_config_list)
635 .finish()
636 }
637}
638
639#[derive(Clone, Deserialize, Eq, PartialEq, Serialize)]
641pub struct VlessEncryptionAuth {
642 pub id: String,
644 pub label: String,
646 pub encryption: String,
648 pub decryption: String,
650}
651
652impl fmt::Debug for VlessEncryptionAuth {
653 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
654 formatter
655 .debug_struct("VlessEncryptionAuth")
656 .field("id", &self.id)
657 .field("label", &self.label)
658 .field("encryption", &"[REDACTED]")
659 .field("decryption", &"[REDACTED]")
660 .finish()
661 }
662}
663
664#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
666#[serde(default)]
667pub struct VlessEncryptionOptions {
668 #[serde(deserialize_with = "deserialize_null_default")]
670 pub auths: Vec<VlessEncryptionAuth>,
671}
672
673#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
675#[serde(default, rename_all = "camelCase")]
676pub struct WebCertificateFiles {
677 pub web_cert_file: String,
679 pub web_key_file: String,
681}
682
683#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
685#[serde(default, rename_all = "camelCase")]
686#[allow(clippy::struct_excessive_bools)]
687pub struct RealityScanResult {
688 pub target: String,
690 pub host: String,
692 #[serde(rename = "ip")]
694 pub ip_address: String,
695 pub port: i32,
697 pub feasible: bool,
699 #[serde(rename = "tls13", alias = "TLS13")]
701 pub tls13: bool,
702 #[serde(rename = "tlsVersion", alias = "TLSVersion")]
704 pub tls_version: String,
705 #[serde(rename = "h2", alias = "H2")]
707 pub h2: bool,
708 #[serde(rename = "alpn", alias = "ALPN")]
710 pub alpn: String,
711 #[serde(rename = "x25519", alias = "X25519")]
713 pub x25519: bool,
714 #[serde(rename = "curveID", alias = "curveId")]
716 pub curve_id: String,
717 pub private_target: bool,
719 pub cert_chain_valid: bool,
721 pub cert_chain_bytes: i32,
723 pub cert_valid: bool,
725 pub cert_subject: String,
727 pub cert_issuer: String,
729 pub not_after: String,
731 #[serde(deserialize_with = "deserialize_null_default")]
733 pub server_names: Vec<String>,
734 pub latency_ms: i32,
736 pub reason: String,
738}
739
740#[derive(Clone, Debug, Eq, PartialEq)]
742pub struct RealityScanRequest {
743 pub target: String,
745 pub xray_version: i32,
747}
748
749impl RealityScanRequest {
750 pub fn new(target: impl Into<String>) -> Self {
752 Self {
753 target: target.into(),
754 xray_version: 0,
755 }
756 }
757}
758
759#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
761#[non_exhaustive]
762pub enum LogLevel {
763 #[default]
765 All,
766 Debug,
768 Info,
770 Warning,
772 Error,
774}
775
776impl LogLevel {
777 pub(crate) const fn as_str(self) -> &'static str {
778 match self {
779 Self::All => "",
780 Self::Debug => "debug",
781 Self::Info => "info",
782 Self::Warning => "warning",
783 Self::Error => "error",
784 }
785 }
786}
787
788#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
790pub struct PanelLogRequest {
791 pub count: u32,
793 pub level: LogLevel,
795 pub syslog: bool,
797}
798
799#[derive(Clone, Debug, Default, Eq, PartialEq)]
801pub struct XrayLogRequest {
802 pub count: u32,
804 pub filter: String,
806 pub show_direct: bool,
808 pub show_blocked: bool,
810 pub show_proxy: bool,
812}
813
814#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
816#[non_exhaustive]
817pub enum XrayLogEvent {
818 #[default]
820 Direct,
821 Blocked,
823 Proxied,
825 Unknown,
827}
828
829impl From<i32> for XrayLogEvent {
830 fn from(value: i32) -> Self {
831 match value {
832 0 => Self::Direct,
833 1 => Self::Blocked,
834 2 => Self::Proxied,
835 _ => Self::Unknown,
836 }
837 }
838}
839
840impl From<XrayLogEvent> for i32 {
841 fn from(value: XrayLogEvent) -> Self {
842 match value {
843 XrayLogEvent::Direct => 0,
844 XrayLogEvent::Blocked => 1,
845 XrayLogEvent::Proxied => 2,
846 XrayLogEvent::Unknown => -1,
847 }
848 }
849}
850
851impl<'de> Deserialize<'de> for XrayLogEvent {
852 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
853 where
854 D: Deserializer<'de>,
855 {
856 i32::deserialize(deserializer).map(Self::from)
857 }
858}
859
860impl Serialize for XrayLogEvent {
861 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
862 where
863 S: serde::Serializer,
864 {
865 i32::from(*self).serialize(serializer)
866 }
867}
868
869#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
871#[serde(default, rename_all = "PascalCase")]
872pub struct XrayLogEntry {
873 pub date_time: String,
875 pub from_address: String,
877 pub to_address: String,
879 pub inbound: String,
881 pub outbound: String,
883 pub email: String,
885 pub event: XrayLogEvent,
887}
888
889#[derive(Clone, Default, Eq, PartialEq)]
891pub struct DatabaseFile {
892 pub filename: Option<String>,
894 pub content_type: Option<String>,
896 pub bytes: Vec<u8>,
898}
899
900impl fmt::Debug for DatabaseFile {
901 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
902 formatter
903 .debug_struct("DatabaseFile")
904 .field("filename", &self.filename)
905 .field("content_type", &self.content_type)
906 .field(
907 "bytes",
908 &format_args!("[REDACTED; {} bytes]", self.bytes.len()),
909 )
910 .finish()
911 }
912}
913
914#[cfg(test)]
915mod tests {
916 use super::*;
917
918 #[test]
919 fn history_variants_match_the_source_allowlists() {
920 let buckets = [
921 HistoryBucket::Minutes2,
922 HistoryBucket::Minutes30,
923 HistoryBucket::Hour1,
924 HistoryBucket::Hours3,
925 HistoryBucket::Hours6,
926 HistoryBucket::Hours12,
927 HistoryBucket::Day1,
928 HistoryBucket::Days2,
929 HistoryBucket::Days7,
930 ]
931 .map(HistoryBucket::seconds);
932 assert_eq!(buckets, [2, 30, 60, 180, 360, 720, 1_440, 2_880, 10_080]);
933
934 let metrics = [
935 SystemMetric::Cpu,
936 SystemMetric::Memory,
937 SystemMetric::Swap,
938 SystemMetric::NetworkUp,
939 SystemMetric::NetworkDown,
940 SystemMetric::PacketUp,
941 SystemMetric::PacketDown,
942 SystemMetric::DiskRead,
943 SystemMetric::DiskWrite,
944 SystemMetric::DiskUsage,
945 SystemMetric::TcpCount,
946 SystemMetric::UdpCount,
947 SystemMetric::Online,
948 SystemMetric::Load1,
949 SystemMetric::Load5,
950 SystemMetric::Load15,
951 ]
952 .map(SystemMetric::as_str);
953 assert_eq!(
954 metrics,
955 [
956 "cpu",
957 "mem",
958 "swap",
959 "netUp",
960 "netDown",
961 "pktUp",
962 "pktDown",
963 "diskRead",
964 "diskWrite",
965 "diskUsage",
966 "tcpCount",
967 "udpCount",
968 "online",
969 "load1",
970 "load5",
971 "load15",
972 ]
973 );
974 }
975
976 #[test]
977 fn open_xray_config_has_explicit_secret_accessors() {
978 let value = serde_json::json!({"privateKey": "xray-private-secret"});
979 let config = XrayConfig::from(value.clone());
980 assert_eq!(config.as_value(), &value);
981 assert!(!format!("{config:?}").contains("xray-private-secret"));
982 assert_eq!(config.into_value(), value);
983 }
984
985 #[test]
986 fn xray_metrics_and_log_filters_match_wire_vocabulary() {
987 assert_eq!(
988 [
989 XrayMetric::Alloc,
990 XrayMetric::Sys,
991 XrayMetric::HeapObjects,
992 XrayMetric::NumGc,
993 XrayMetric::PauseNs,
994 ]
995 .map(XrayMetric::as_str),
996 ["xrAlloc", "xrSys", "xrHeapObjects", "xrNumGC", "xrPauseNs"]
997 );
998 assert_eq!(
999 [
1000 LogLevel::All,
1001 LogLevel::Debug,
1002 LogLevel::Info,
1003 LogLevel::Warning,
1004 LogLevel::Error,
1005 ]
1006 .map(LogLevel::as_str),
1007 ["", "debug", "info", "warning", "error"]
1008 );
1009
1010 for (wire, event) in [
1011 (0, XrayLogEvent::Direct),
1012 (1, XrayLogEvent::Blocked),
1013 (2, XrayLogEvent::Proxied),
1014 (99, XrayLogEvent::Unknown),
1015 ] {
1016 assert_eq!(XrayLogEvent::from(wire), event);
1017 let expected = if event == XrayLogEvent::Unknown {
1018 -1
1019 } else {
1020 wire
1021 };
1022 assert_eq!(i32::from(event), expected);
1023 assert_eq!(serde_json::to_value(event).unwrap(), expected);
1024 assert_eq!(
1025 serde_json::from_value::<XrayLogEvent>(serde_json::json!(wire)).unwrap(),
1026 event
1027 );
1028 }
1029 }
1030
1031 #[test]
1032 fn generated_key_material_is_redacted_selectively() {
1033 let x25519 = X25519KeyPair {
1034 private_key: "x25519-private-secret".into(),
1035 public_key: "x25519-public".into(),
1036 };
1037 let mldsa = Mldsa65KeyPair {
1038 seed: "mldsa-seed-secret".into(),
1039 verify: "mldsa-verify".into(),
1040 };
1041 let mlkem = Mlkem768KeyPair {
1042 seed: "mlkem-seed-secret".into(),
1043 client: "mlkem-client".into(),
1044 };
1045 let ech = EchKeyPair {
1046 ech_server_keys: "ech-private-secret".into(),
1047 ech_config_list: "ech-public".into(),
1048 };
1049 let vless = VlessEncryptionAuth {
1050 id: "option-1".into(),
1051 label: "recommended".into(),
1052 encryption: "vless-encryption-secret".into(),
1053 decryption: "vless-decryption-secret".into(),
1054 };
1055 let cases = [
1056 (
1057 format!("{x25519:?}"),
1058 "x25519-private-secret",
1059 "x25519-public",
1060 ),
1061 (format!("{mldsa:?}"), "mldsa-seed-secret", "mldsa-verify"),
1062 (format!("{mlkem:?}"), "mlkem-seed-secret", "mlkem-client"),
1063 (format!("{ech:?}"), "ech-private-secret", "ech-public"),
1064 (
1065 format!("{vless:?}"),
1066 "vless-encryption-secret",
1067 "recommended",
1068 ),
1069 ];
1070 for (output, secret, visible) in cases {
1071 assert!(!output.contains(secret));
1072 assert!(output.contains(visible));
1073 }
1074 assert!(!format!("{vless:?}").contains("vless-decryption-secret"));
1075 }
1076}