1use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
12#[serde(rename_all = "camelCase", default)]
13pub struct GatewayConfig {
14 #[serde(skip_serializing_if = "Option::is_none")]
15 pub bind: Option<String>,
16 #[serde(skip_serializing_if = "Option::is_none")]
17 pub port: Option<u16>,
18 #[serde(skip_serializing_if = "Option::is_none")]
20 pub auth_token: Option<String>,
21 #[serde(skip_serializing_if = "Option::is_none")]
22 pub auth: Option<GatewayAuth>,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 pub tls: Option<TlsConfig>,
25 #[serde(skip_serializing_if = "Option::is_none")]
26 pub mdns: Option<MdnsConfig>,
27 #[serde(skip_serializing_if = "Option::is_none")]
28 pub control_ui: Option<ControlUiConfig>,
29 #[serde(skip_serializing_if = "Option::is_none")]
30 pub trusted_proxies: Option<Vec<String>>,
31}
32
33#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
34#[serde(rename_all = "camelCase", default)]
35pub struct GatewayAuth {
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub mode: Option<String>,
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub token: Option<String>,
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub password: Option<String>,
42}
43
44#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
45#[serde(rename_all = "camelCase", default)]
46pub struct TlsConfig {
47 #[serde(skip_serializing_if = "Option::is_none")]
48 pub enabled: Option<bool>,
49 #[serde(skip_serializing_if = "Option::is_none")]
50 pub cert: Option<String>,
51 #[serde(skip_serializing_if = "Option::is_none")]
52 pub key: Option<String>,
53}
54
55#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
56#[serde(rename_all = "camelCase", default)]
57pub struct MdnsConfig {
58 #[serde(skip_serializing_if = "Option::is_none")]
59 pub mode: Option<String>,
60}
61
62#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
63#[serde(rename_all = "camelCase", default)]
64pub struct ControlUiConfig {
65 #[serde(skip_serializing_if = "Option::is_none")]
66 pub dangerously_disable_device_auth: Option<bool>,
67 #[serde(skip_serializing_if = "Option::is_none")]
68 pub allow_insecure_auth: Option<bool>,
69}
70
71#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
73#[serde(rename_all = "camelCase", default)]
74pub struct ExecConfig {
75 #[serde(skip_serializing_if = "Option::is_none")]
76 pub approvals: Option<String>,
77 #[serde(skip_serializing_if = "Option::is_none")]
78 pub auto_approve: Option<Vec<String>>,
79}
80
81#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
83#[serde(rename_all = "camelCase", default)]
84pub struct SandboxConfig {
85 #[serde(skip_serializing_if = "Option::is_none")]
86 pub mode: Option<String>,
87 #[serde(skip_serializing_if = "Option::is_none")]
88 pub scope: Option<String>,
89 #[serde(skip_serializing_if = "Option::is_none")]
90 pub workspace_access: Option<String>,
91}
92
93#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
95#[serde(rename_all = "camelCase", default)]
96pub struct ToolsConfig {
97 #[serde(skip_serializing_if = "Option::is_none")]
98 pub exec: Option<ToolsExec>,
99}
100
101#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
102#[serde(rename_all = "camelCase", default)]
103pub struct ToolsExec {
104 #[serde(skip_serializing_if = "Option::is_none")]
105 pub host: Option<String>,
106}
107
108#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
110#[serde(rename_all = "camelCase", default)]
111pub struct SessionConfig {
112 #[serde(skip_serializing_if = "Option::is_none")]
113 pub dm_scope: Option<String>,
114}
115
116#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
118#[serde(rename_all = "camelCase", default)]
119pub struct LoggingConfig {
120 #[serde(skip_serializing_if = "Option::is_none")]
121 pub redact_sensitive: Option<String>,
122}
123
124#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
126#[serde(rename_all = "snake_case")]
127pub enum FailureMode {
128 BlockAll,
129 SafeMode,
130 ReadOnly,
131}
132
133#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
135#[serde(rename_all = "lowercase")]
136pub enum RiskProfile {
137 Strict,
138 Standard,
139 Permissive,
140}
141
142#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
144#[serde(rename_all = "camelCase", default)]
145pub struct SecureOpsConfig {
146 #[serde(skip_serializing_if = "Option::is_none")]
147 pub monitors: Option<MonitorsToggle>,
148 #[serde(skip_serializing_if = "Option::is_none")]
149 pub cost: Option<CostLimits>,
150 #[serde(skip_serializing_if = "Option::is_none")]
151 pub memory: Option<MemorySettings>,
152 #[serde(skip_serializing_if = "Option::is_none")]
153 pub skills: Option<SkillsSettings>,
154 #[serde(skip_serializing_if = "Option::is_none")]
155 pub network: Option<NetworkSettings>,
156 #[serde(skip_serializing_if = "Option::is_none")]
157 pub failure_mode: Option<FailureMode>,
158 #[serde(skip_serializing_if = "Option::is_none")]
159 pub risk_profile: Option<RiskProfile>,
160 #[serde(skip_serializing_if = "Option::is_none")]
161 pub risk_profiles: Option<HashMap<String, RiskProfileDef>>,
162 #[serde(skip_serializing_if = "Option::is_none")]
163 pub behavioral: Option<BehavioralSettings>,
164}
165
166#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
167#[serde(rename_all = "camelCase", default)]
168pub struct MonitorsToggle {
169 #[serde(skip_serializing_if = "Option::is_none")]
170 pub credentials: Option<bool>,
171 #[serde(skip_serializing_if = "Option::is_none")]
172 pub memory: Option<bool>,
173 #[serde(skip_serializing_if = "Option::is_none")]
174 pub skills: Option<bool>,
175 #[serde(skip_serializing_if = "Option::is_none")]
176 pub cost: Option<bool>,
177}
178
179#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
180#[serde(rename_all = "camelCase", default)]
181pub struct CostLimits {
182 #[serde(skip_serializing_if = "Option::is_none")]
183 pub hourly_limit_usd: Option<f64>,
184 #[serde(skip_serializing_if = "Option::is_none")]
185 pub daily_limit_usd: Option<f64>,
186 #[serde(skip_serializing_if = "Option::is_none")]
187 pub monthly_limit_usd: Option<f64>,
188 #[serde(skip_serializing_if = "Option::is_none")]
189 pub circuit_breaker_enabled: Option<bool>,
190}
191
192#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
193#[serde(rename_all = "camelCase", default)]
194pub struct MemorySettings {
195 #[serde(skip_serializing_if = "Option::is_none")]
196 pub integrity_checks: Option<bool>,
197 #[serde(skip_serializing_if = "Option::is_none")]
198 pub prompt_injection_scan: Option<bool>,
199 #[serde(skip_serializing_if = "Option::is_none")]
200 pub quarantine_enabled: Option<bool>,
201 #[serde(skip_serializing_if = "Option::is_none")]
202 pub trust_levels: Option<bool>,
203}
204
205#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
206#[serde(rename_all = "camelCase", default)]
207pub struct SkillsSettings {
208 #[serde(skip_serializing_if = "Option::is_none")]
209 pub block_unaudited: Option<bool>,
210 #[serde(skip_serializing_if = "Option::is_none")]
211 pub scan_on_install: Option<bool>,
212 #[serde(skip_serializing_if = "Option::is_none")]
213 pub ioc_check_enabled: Option<bool>,
214}
215
216#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
217#[serde(rename_all = "camelCase", default)]
218pub struct NetworkSettings {
219 #[serde(skip_serializing_if = "Option::is_none")]
220 pub egress_allowlist_enabled: Option<bool>,
221 #[serde(skip_serializing_if = "Option::is_none")]
222 pub egress_allowlist: Option<Vec<String>>,
223}
224
225#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
226#[serde(rename_all = "camelCase", default)]
227pub struct RiskProfileDef {
228 #[serde(skip_serializing_if = "Option::is_none")]
229 pub failure_mode: Option<FailureMode>,
230 #[serde(skip_serializing_if = "Option::is_none")]
231 pub approval_required: Option<bool>,
232 #[serde(skip_serializing_if = "Option::is_none")]
233 pub allowed_tools: Option<Vec<String>>,
234 #[serde(skip_serializing_if = "Option::is_none")]
235 pub blocked_tools: Option<Vec<String>>,
236 #[serde(skip_serializing_if = "Option::is_none")]
237 pub max_cost_per_session: Option<f64>,
238}
239
240#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
241#[serde(rename_all = "camelCase", default)]
242pub struct BehavioralSettings {
243 #[serde(skip_serializing_if = "Option::is_none")]
244 pub baseline_enabled: Option<bool>,
245 #[serde(skip_serializing_if = "Option::is_none")]
246 pub deviation_threshold: Option<f64>,
247 #[serde(skip_serializing_if = "Option::is_none")]
248 pub window_minutes: Option<u64>,
249}
250
251#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
253#[serde(rename_all = "camelCase", default)]
254pub struct OpenClawConfig {
255 #[serde(skip_serializing_if = "Option::is_none")]
256 pub gateway: Option<GatewayConfig>,
257 #[serde(skip_serializing_if = "Option::is_none")]
258 pub exec: Option<ExecConfig>,
259 #[serde(skip_serializing_if = "Option::is_none")]
260 pub sandbox: Option<SandboxConfig>,
261 #[serde(skip_serializing_if = "Option::is_none")]
262 pub tools: Option<ToolsConfig>,
263 #[serde(skip_serializing_if = "Option::is_none")]
264 pub session: Option<SessionConfig>,
265 #[serde(skip_serializing_if = "Option::is_none")]
266 pub logging: Option<LoggingConfig>,
267 #[serde(skip_serializing_if = "Option::is_none")]
268 pub secureops: Option<SecureOpsConfig>,
269}
270
271impl OpenClawConfig {
272 pub fn from_json_or_default(content: &str) -> Self {
278 serde_json::from_str(content).unwrap_or_default()
279 }
280
281 pub fn failure_mode(&self) -> FailureMode {
284 self.secureops
285 .as_ref()
286 .and_then(|s| s.failure_mode)
287 .unwrap_or(FailureMode::BlockAll)
288 }
289
290 pub fn risk_profile(&self) -> RiskProfile {
293 self.secureops
294 .as_ref()
295 .and_then(|s| s.risk_profile)
296 .unwrap_or(RiskProfile::Standard)
297 }
298}
299
300#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
303#[serde(rename_all = "snake_case", default)]
304pub struct DockerServiceConfig {
305 #[serde(skip_serializing_if = "Option::is_none")]
306 pub read_only: Option<bool>,
307 #[serde(skip_serializing_if = "Option::is_none")]
308 pub cap_drop: Option<Vec<String>>,
309 #[serde(skip_serializing_if = "Option::is_none")]
310 pub security_opt: Option<Vec<String>>,
311 #[serde(skip_serializing_if = "Option::is_none")]
312 pub networks: Option<Vec<String>>,
313 #[serde(skip_serializing_if = "Option::is_none")]
314 pub network_mode: Option<String>,
315 #[serde(skip_serializing_if = "Option::is_none")]
316 pub volumes: Option<Vec<String>>,
317 #[serde(skip_serializing_if = "Option::is_none")]
318 pub deploy: Option<DockerDeploy>,
319}
320
321#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
322#[serde(rename_all = "snake_case", default)]
323pub struct DockerDeploy {
324 #[serde(skip_serializing_if = "Option::is_none")]
325 pub resources: Option<DockerResources>,
326}
327
328#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
329#[serde(rename_all = "snake_case", default)]
330pub struct DockerResources {
331 #[serde(skip_serializing_if = "Option::is_none")]
332 pub limits: Option<DockerLimits>,
333}
334
335#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
336#[serde(rename_all = "snake_case", default)]
337pub struct DockerLimits {
338 #[serde(skip_serializing_if = "Option::is_none")]
339 pub memory: Option<String>,
340 #[serde(skip_serializing_if = "Option::is_none")]
341 pub cpus: Option<String>,
342}
343
344#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
345#[serde(rename_all = "snake_case", default)]
346pub struct DockerNetwork {
347 #[serde(skip_serializing_if = "Option::is_none")]
348 pub driver: Option<String>,
349 #[serde(skip_serializing_if = "Option::is_none")]
350 pub internal: Option<bool>,
351}
352
353#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
354#[serde(rename_all = "snake_case", default)]
355pub struct DockerComposeConfig {
356 #[serde(skip_serializing_if = "Option::is_none")]
357 pub services: Option<HashMap<String, DockerServiceConfig>>,
358 #[serde(skip_serializing_if = "Option::is_none")]
359 pub networks: Option<HashMap<String, DockerNetwork>>,
360}