1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(rename_all = "snake_case")]
8pub enum TransportMode {
9 #[default]
11 Tcp,
12 Stdio,
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct ProxyConfig {
20 pub listen_addr: String,
22
23 pub upstream_addr: String,
25
26 pub policy_paths: Vec<String>,
28
29 #[serde(default = "default_true")]
31 pub audit_enabled: bool,
32
33 #[serde(default)]
35 pub audit_file: Option<String>,
36
37 #[serde(default)]
40 pub fail_open: bool,
41
42 #[serde(default)]
44 pub hot_reload: bool,
45
46 #[serde(default)]
48 pub transport: TransportMode,
49
50 #[serde(default)]
53 pub upstream_command: Option<String>,
54
55 #[serde(default)]
58 pub upstream_args: Vec<String>,
59
60 #[serde(default)]
63 pub health_addr: Option<String>,
64
65 #[serde(default)]
68 pub kvlar_cloud_url: Option<String>,
69
70 #[serde(default)]
72 pub kvlar_api_key: Option<String>,
73
74 #[serde(default)]
77 pub kvlar_agent_id: Option<String>,
78
79 #[serde(default)]
82 pub kvlar_radar_url: Option<String>,
83}
84
85fn default_true() -> bool {
86 true
87}
88
89impl Default for ProxyConfig {
90 fn default() -> Self {
91 Self {
92 listen_addr: "127.0.0.1:9100".into(),
93 upstream_addr: "127.0.0.1:3000".into(),
94 policy_paths: vec!["policy.yaml".into()],
95 audit_enabled: true,
96 audit_file: None,
97 fail_open: false,
98 hot_reload: false,
99 transport: TransportMode::default(),
100 upstream_command: None,
101 upstream_args: Vec::new(),
102 health_addr: None,
103 kvlar_cloud_url: None,
104 kvlar_api_key: None,
105 kvlar_agent_id: None,
106 kvlar_radar_url: None,
107 }
108 }
109}
110
111impl ProxyConfig {
112 pub fn from_file(path: &std::path::Path) -> Result<Self, Box<dyn std::error::Error>> {
114 let yaml = std::fs::read_to_string(path)?;
115 let config: ProxyConfig = serde_yaml::from_str(&yaml)?;
116 Ok(config)
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[test]
125 fn test_default_config() {
126 let config = ProxyConfig::default();
127 assert_eq!(config.listen_addr, "127.0.0.1:9100");
128 assert!(config.audit_enabled);
129 assert!(!config.fail_open);
130 assert!(!config.hot_reload);
131 assert!(config.audit_file.is_none());
132 }
133
134 #[test]
135 fn test_config_serde_roundtrip() {
136 let config = ProxyConfig::default();
137 let json = serde_json::to_string(&config).unwrap();
138 let parsed: ProxyConfig = serde_json::from_str(&json).unwrap();
139 assert_eq!(parsed.listen_addr, config.listen_addr);
140 assert_eq!(parsed.upstream_addr, config.upstream_addr);
141 }
142
143 #[test]
144 fn test_config_from_yaml_string() {
145 let yaml = r#"
146listen_addr: "0.0.0.0:8080"
147upstream_addr: "localhost:4000"
148policy_paths:
149 - "policies/prod.yaml"
150 - "policies/custom.yaml"
151audit_enabled: true
152audit_file: "/var/log/kvlar/audit.jsonl"
153fail_open: false
154hot_reload: true
155"#;
156 let config: ProxyConfig = serde_yaml::from_str(yaml).unwrap();
157 assert_eq!(config.listen_addr, "0.0.0.0:8080");
158 assert_eq!(config.policy_paths.len(), 2);
159 assert!(config.hot_reload);
160 assert_eq!(
161 config.audit_file.as_deref(),
162 Some("/var/log/kvlar/audit.jsonl")
163 );
164 }
165}