Skip to main content

zeph_config/
env.rs

1// SPDX-FileCopyrightText: 2026 Andrei G <bug-ops>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use std::num::NonZeroUsize;
5
6use crate::ProviderName;
7use crate::providers::{ProviderEntry, SttConfig, default_stt_language};
8use crate::root::Config;
9
10impl Config {
11    pub fn apply_env_overrides(&mut self) {
12        self.apply_env_overrides_core();
13        self.apply_env_overrides_security();
14    }
15
16    fn apply_env_overrides_core(&mut self) {
17        self.apply_env_overrides_core_1();
18        self.apply_env_overrides_core_1b();
19        self.apply_env_overrides_core_2();
20        self.apply_env_overrides_core_2b();
21    }
22
23    fn apply_env_overrides_core_1(&mut self) {
24        let has_provider_env = std::env::var("ZEPH_LLM_PROVIDER").is_ok()
25            || std::env::var("ZEPH_LLM_BASE_URL").is_ok()
26            || std::env::var("ZEPH_LLM_MODEL").is_ok();
27        if has_provider_env && self.llm.providers.is_empty() {
28            self.llm.providers.push(ProviderEntry::default());
29        }
30
31        if let Ok(v) = std::env::var("ZEPH_LLM_PROVIDER")
32            && let Some(entry) = self.llm.providers.first_mut()
33        {
34            if let Ok(kind) = serde_json::from_value(serde_json::Value::String(v.clone())) {
35                entry.provider_type = kind;
36            } else {
37                tracing::warn!("ignoring invalid ZEPH_LLM_PROVIDER value: {v}");
38            }
39        }
40        if let Ok(v) = std::env::var("ZEPH_LLM_BASE_URL")
41            && let Some(entry) = self.llm.providers.first_mut()
42        {
43            entry.base_url = Some(v);
44        }
45        if let Ok(v) = std::env::var("ZEPH_LLM_MODEL")
46            && let Some(entry) = self.llm.providers.first_mut()
47        {
48            entry.model = Some(v);
49        }
50        if let Ok(v) = std::env::var("ZEPH_LLM_EMBEDDING_MODEL") {
51            self.llm.embedding_model = v;
52        }
53        if let Ok(v) = std::env::var("ZEPH_SQLITE_PATH") {
54            self.memory.sqlite_path = v;
55        }
56        if let Ok(v) = std::env::var("ZEPH_QDRANT_URL") {
57            self.memory.qdrant_url = v;
58        }
59        if let Ok(v) = std::env::var("ZEPH_MEMORY_SEMANTIC_ENABLED")
60            && let Ok(enabled) = v.parse::<bool>()
61        {
62            self.memory.semantic.enabled = enabled;
63        }
64        if let Ok(v) = std::env::var("ZEPH_MEMORY_RECALL_LIMIT")
65            && let Ok(limit) = v.parse::<usize>()
66        {
67            self.memory.semantic.recall_limit = limit;
68        }
69        if let Ok(v) = std::env::var("ZEPH_MEMORY_SUMMARIZATION_THRESHOLD")
70            && let Ok(threshold) = v.parse::<usize>()
71        {
72            self.memory.summarization_threshold = threshold;
73        }
74        if let Ok(v) = std::env::var("ZEPH_MEMORY_CONTEXT_BUDGET_TOKENS")
75            && let Ok(tokens) = v.parse::<usize>()
76        {
77            self.memory.context_budget_tokens = tokens;
78        }
79        if let Ok(v) = std::env::var("ZEPH_MEMORY_COMPACTION_THRESHOLD")
80            && let Ok(threshold) = v.parse::<f32>()
81        {
82            self.memory.hard_compaction_threshold = threshold;
83        }
84        if let Ok(v) = std::env::var("ZEPH_MEMORY_SOFT_COMPACTION_THRESHOLD")
85            && let Ok(threshold) = v.parse::<f32>()
86        {
87            self.memory.soft_compaction_threshold = threshold;
88        }
89        if let Ok(v) = std::env::var("ZEPH_MEMORY_COMPACTION_PRESERVE_TAIL")
90            && let Ok(tail) = v.parse::<usize>()
91        {
92            self.memory.compaction_preserve_tail = tail;
93        }
94        if let Ok(v) = std::env::var("ZEPH_MEMORY_AUTO_BUDGET")
95            && let Ok(enabled) = v.parse::<bool>()
96        {
97            self.memory.auto_budget = enabled;
98        }
99        if let Ok(v) = std::env::var("ZEPH_MEMORY_PRUNE_PROTECT_TOKENS")
100            && let Ok(tokens) = v.parse::<usize>()
101        {
102            self.memory.prune_protect_tokens = tokens;
103        }
104        if let Ok(v) = std::env::var("ZEPH_MEMORY_VECTOR_BACKEND") {
105            match v.to_lowercase().as_str() {
106                "sqlite" => {
107                    self.memory.vector_backend = crate::memory::VectorBackend::Sqlite;
108                }
109                "qdrant" => {
110                    self.memory.vector_backend = crate::memory::VectorBackend::Qdrant;
111                }
112                _ => {}
113            }
114        }
115    }
116
117    fn apply_env_overrides_core_1b(&mut self) {
118        if let Ok(v) = std::env::var("ZEPH_SAFE_MODE")
119            && let Ok(enabled) = v.parse::<bool>()
120        {
121            self.cli.safe_mode = enabled;
122        }
123        if let Ok(v) = std::env::var("ZEPH_SKILLS_MAX_ACTIVE")
124            && let Ok(n) = v.parse::<usize>()
125            && let Some(nz) = NonZeroUsize::new(n)
126        {
127            self.skills.max_active_skills = nz;
128        }
129        if let Ok(v) = std::env::var("ZEPH_SKILLS_LEARNING_ENABLED")
130            && let Ok(enabled) = v.parse::<bool>()
131        {
132            self.skills.learning.enabled = enabled;
133        }
134        if let Ok(v) = std::env::var("ZEPH_SKILLS_LEARNING_AUTO_ACTIVATE")
135            && let Ok(auto_activate) = v.parse::<bool>()
136        {
137            self.skills.learning.auto_activate = auto_activate;
138        }
139        if let Ok(v) = std::env::var("ZEPH_SKILLS_PROMPT_MODE") {
140            match v.to_lowercase().as_str() {
141                "full" => self.skills.prompt_mode = crate::features::SkillPromptMode::Full,
142                "compact" => self.skills.prompt_mode = crate::features::SkillPromptMode::Compact,
143                "auto" => self.skills.prompt_mode = crate::features::SkillPromptMode::Auto,
144                _ => {}
145            }
146        }
147        if let Ok(v) = std::env::var("ZEPH_MEMORY_SEMANTIC_TEMPORAL_DECAY_ENABLED")
148            && let Ok(enabled) = v.parse::<bool>()
149        {
150            self.memory.semantic.temporal_decay_enabled = enabled;
151        }
152        if let Ok(v) = std::env::var("ZEPH_MEMORY_SEMANTIC_TEMPORAL_DECAY_HALF_LIFE_DAYS")
153            && let Ok(days) = v.parse::<u32>()
154        {
155            self.memory.semantic.temporal_decay_half_life_days = days;
156        }
157        if let Ok(v) = std::env::var("ZEPH_MEMORY_SEMANTIC_MMR_ENABLED")
158            && let Ok(enabled) = v.parse::<bool>()
159        {
160            self.memory.semantic.mmr_enabled = enabled;
161        }
162        if let Ok(v) = std::env::var("ZEPH_MEMORY_SEMANTIC_MMR_LAMBDA")
163            && let Ok(lambda) = v.parse::<f32>()
164        {
165            self.memory.semantic.mmr_lambda = lambda;
166        }
167        if let Ok(v) = std::env::var("ZEPH_MEMORY_TOKEN_SAFETY_MARGIN")
168            && let Ok(margin) = v.parse::<f32>()
169        {
170            self.memory.token_safety_margin = margin.clamp(0.1, 10.0);
171        }
172        if let Ok(v) = std::env::var("ZEPH_TOOLS_SUMMARIZE_OUTPUT")
173            && let Ok(enabled) = v.parse::<bool>()
174        {
175            self.tools.summarize_output = enabled;
176        }
177        if let Ok(v) = std::env::var("ZEPH_TOOLS_SHELL_ALLOWED_COMMANDS") {
178            self.tools.shell.allowed_commands = v
179                .split(',')
180                .map(|s| s.trim().to_owned())
181                .filter(|s| !s.is_empty())
182                .collect();
183        }
184        if let Ok(v) = std::env::var("ZEPH_TOOLS_TIMEOUT")
185            && let Ok(secs) = v.parse::<u64>()
186        {
187            self.tools.shell.timeout = secs;
188        }
189        if let Ok(v) = std::env::var("ZEPH_TOOLS_SCRAPE_TIMEOUT")
190            && let Ok(secs) = v.parse::<u64>()
191        {
192            self.tools.scrape.timeout = secs;
193        }
194        if let Ok(v) = std::env::var("ZEPH_TOOLS_SCRAPE_MAX_BODY")
195            && let Ok(bytes) = v.parse::<usize>()
196        {
197            self.tools.scrape.max_body_bytes = bytes;
198        }
199    }
200
201    fn apply_env_overrides_core_2(&mut self) {
202        if let Ok(v) = std::env::var("ZEPH_INDEX_ENABLED")
203            && let Ok(enabled) = v.parse::<bool>()
204        {
205            self.index.enabled = enabled;
206        }
207        if let Ok(v) = std::env::var("ZEPH_INDEX_MAX_CHUNKS")
208            && let Ok(n) = v.parse::<usize>()
209        {
210            self.index.max_chunks = n;
211        }
212        if let Ok(v) = std::env::var("ZEPH_INDEX_SCORE_THRESHOLD")
213            && let Ok(t) = v.parse::<f32>()
214        {
215            self.index.score_threshold = t.clamp(0.0, 1.0);
216        }
217        if let Ok(v) = std::env::var("ZEPH_INDEX_BUDGET_RATIO")
218            && let Ok(r) = v.parse::<f32>()
219        {
220            self.index.budget_ratio = r.clamp(0.0, 1.0);
221        }
222        if let Ok(v) = std::env::var("ZEPH_INDEX_REPO_MAP_TOKENS")
223            && let Ok(n) = v.parse::<usize>()
224        {
225            self.index.repo_map_tokens = n;
226        }
227        if let Ok(v) = std::env::var("ZEPH_STT_PROVIDER") {
228            let stt = self.llm.stt.get_or_insert_with(|| SttConfig {
229                provider: ProviderName::default(),
230                language: default_stt_language(),
231            });
232            stt.provider = ProviderName::new(v);
233        }
234        if let Ok(v) = std::env::var("ZEPH_STT_LANGUAGE") {
235            let stt = self.llm.stt.get_or_insert_with(|| SttConfig {
236                provider: ProviderName::default(),
237                language: default_stt_language(),
238            });
239            stt.language = v;
240        }
241        if std::env::var("ZEPH_STT_MODEL").is_ok() {
242            tracing::warn!(
243                "env var ZEPH_STT_MODEL is no longer supported; set `stt_model` on the \
244                 [[llm.providers]] entry instead"
245            );
246        }
247        if std::env::var("ZEPH_STT_BASE_URL").is_ok() {
248            tracing::warn!(
249                "env var ZEPH_STT_BASE_URL is no longer supported; set `base_url` on the \
250                 [[llm.providers]] entry instead"
251            );
252        }
253    }
254
255    fn apply_env_overrides_core_2b(&mut self) {
256        if let Ok(v) = std::env::var("ZEPH_AUTO_UPDATE_CHECK")
257            && let Ok(enabled) = v.parse::<bool>()
258        {
259            self.agent.auto_update_check = enabled;
260        }
261        if let Ok(v) = std::env::var("ZEPH_A2A_ENABLED")
262            && let Ok(enabled) = v.parse::<bool>()
263        {
264            self.a2a.enabled = enabled;
265        }
266        if let Ok(v) = std::env::var("ZEPH_A2A_HOST") {
267            self.a2a.host = v;
268        }
269        if let Ok(v) = std::env::var("ZEPH_A2A_PORT")
270            && let Ok(port) = v.parse::<u16>()
271        {
272            self.a2a.port = port;
273        }
274        if let Ok(v) = std::env::var("ZEPH_A2A_PUBLIC_URL") {
275            self.a2a.public_url = v;
276        }
277        if let Ok(v) = std::env::var("ZEPH_A2A_RATE_LIMIT")
278            && let Ok(rate) = v.parse::<u32>()
279        {
280            self.a2a.rate_limit = rate;
281        }
282        if let Ok(v) = std::env::var("ZEPH_MEMORY_AUTOSAVE_ASSISTANT")
283            && let Ok(enabled) = v.parse::<bool>()
284        {
285            self.memory.autosave_assistant = enabled;
286        }
287        if let Ok(v) = std::env::var("ZEPH_MEMORY_AUTOSAVE_MIN_LENGTH")
288            && let Ok(len) = v.parse::<usize>()
289        {
290            self.memory.autosave_min_length = len;
291        }
292        if let Ok(v) = std::env::var("ZEPH_LLM_RESPONSE_CACHE_ENABLED")
293            && let Ok(enabled) = v.parse::<bool>()
294        {
295            self.llm.response_cache_enabled = enabled;
296        }
297        if let Ok(v) = std::env::var("ZEPH_LLM_RESPONSE_CACHE_TTL_SECS")
298            && let Ok(secs) = v.parse::<u64>()
299        {
300            self.llm.response_cache_ttl_secs = secs;
301        }
302        if let Ok(v) = std::env::var("ZEPH_LLM_SEMANTIC_CACHE_ENABLED")
303            && let Ok(enabled) = v.parse::<bool>()
304        {
305            self.llm.semantic_cache_enabled = enabled;
306        }
307        if let Ok(v) = std::env::var("ZEPH_LLM_SEMANTIC_CACHE_THRESHOLD")
308            && let Ok(threshold) = v.parse::<f32>()
309        {
310            self.llm.semantic_cache_threshold = threshold;
311        }
312        if let Ok(v) = std::env::var("ZEPH_LLM_SEMANTIC_CACHE_MAX_CANDIDATES")
313            && let Ok(max) = v.parse::<u32>()
314        {
315            self.llm.semantic_cache_max_candidates = max;
316        }
317        if let Ok(v) = std::env::var("ZEPH_ACP_ENABLED")
318            && let Ok(enabled) = v.parse::<bool>()
319        {
320            self.acp.enabled = enabled;
321        }
322        if let Ok(v) = std::env::var("ZEPH_ACP_AGENT_NAME") {
323            self.acp.agent_name = v;
324        }
325        if let Ok(v) = std::env::var("ZEPH_ACP_AGENT_VERSION") {
326            self.acp.agent_version = v;
327        }
328        if let Ok(v) = std::env::var("ZEPH_ACP_MAX_SESSIONS")
329            && let Ok(n) = v.parse::<usize>()
330        {
331            self.acp.max_sessions = n;
332        }
333        if let Ok(v) = std::env::var("ZEPH_ACP_SESSION_IDLE_TIMEOUT_SECS")
334            && let Ok(secs) = v.parse::<u64>()
335        {
336            self.acp.session_idle_timeout_secs = secs;
337        }
338        if let Ok(v) = std::env::var("ZEPH_ACP_PERMISSION_FILE") {
339            self.acp.permission_file = Some(std::path::PathBuf::from(v));
340        }
341        if let Ok(v) = std::env::var("ZEPH_ACP_AUTH_TOKEN") {
342            self.acp.auth_token = Some(v);
343        }
344        if let Ok(v) = std::env::var("ZEPH_ACP_DISCOVERY_ENABLED")
345            && let Ok(enabled) = v.parse::<bool>()
346        {
347            self.acp.discovery_enabled = enabled;
348        }
349        if let Ok(v) = std::env::var("ZEPH_LOG_FILE") {
350            self.logging.file = v;
351        }
352        if let Ok(v) = std::env::var("ZEPH_LOG_LEVEL") {
353            self.logging.level = v;
354        }
355    }
356
357    fn apply_env_overrides_security(&mut self) {
358        if let Ok(v) = std::env::var("ZEPH_TOOLS_SHELL_ALLOWED_PATHS") {
359            self.tools.shell.allowed_paths = v
360                .split(',')
361                .map(|s| s.trim().to_owned())
362                .filter(|s| !s.is_empty())
363                .collect();
364        }
365        if let Ok(v) = std::env::var("ZEPH_TOOLS_SHELL_ALLOW_NETWORK")
366            && let Ok(allow) = v.parse::<bool>()
367        {
368            self.tools.shell.allow_network = allow;
369        }
370        if let Ok(v) = std::env::var("ZEPH_TOOLS_AUDIT_ENABLED")
371            && let Ok(enabled) = v.parse::<bool>()
372        {
373            self.tools.audit.enabled = enabled;
374        }
375        if let Ok(v) = std::env::var("ZEPH_TOOLS_AUDIT_DESTINATION") {
376            self.tools.audit.destination = match v.as_str() {
377                "stdout" => crate::AuditDestination::Stdout,
378                "stderr" => crate::AuditDestination::Stderr,
379                path => crate::AuditDestination::File(std::path::PathBuf::from(path)),
380            };
381        }
382        if let Ok(v) = std::env::var("ZEPH_SECURITY_REDACT_SECRETS")
383            && let Ok(redact) = v.parse::<bool>()
384        {
385            self.security.redact_secrets = redact;
386        }
387        if let Ok(v) = std::env::var("ZEPH_TIMEOUT_LLM")
388            && let Ok(secs) = v.parse::<u64>()
389        {
390            self.timeouts.llm_seconds = secs;
391        }
392        if let Ok(v) = std::env::var("ZEPH_TIMEOUT_LLM_REQUEST")
393            && let Ok(secs) = v.parse::<u64>()
394        {
395            self.timeouts.llm_request_timeout_secs = secs;
396        }
397        if let Ok(v) = std::env::var("ZEPH_TIMEOUT_EMBEDDING")
398            && let Ok(secs) = v.parse::<u64>()
399        {
400            self.timeouts.embedding_seconds = secs;
401        }
402        if let Ok(v) = std::env::var("ZEPH_TIMEOUT_A2A")
403            && let Ok(secs) = v.parse::<u64>()
404        {
405            self.timeouts.a2a_seconds = secs;
406        }
407        if let Ok(v) = std::env::var("ZEPH_A2A_MAX_BODY_SIZE")
408            && let Ok(size) = v.parse::<usize>()
409        {
410            self.a2a.max_body_size = size;
411        }
412        if let Ok(v) = std::env::var("ZEPH_A2A_CLIENT_REQUIRE_TLS")
413            && let Ok(require) = v.parse::<bool>()
414        {
415            self.a2a_client.require_tls = require;
416        }
417        if let Ok(v) = std::env::var("ZEPH_A2A_CLIENT_SSRF_PROTECTION")
418            && let Ok(ssrf) = v.parse::<bool>()
419        {
420            self.a2a_client.ssrf_protection = ssrf;
421        }
422        if let Ok(v) = std::env::var("ZEPH_A2A_CARD_TRUST_POLICY") {
423            match v.to_ascii_lowercase().as_str() {
424                "ignore" => {
425                    self.a2a_client.card_trust_policy = crate::channels::CardTrustPolicy::Ignore;
426                }
427                "prefer" => {
428                    self.a2a_client.card_trust_policy = crate::channels::CardTrustPolicy::Prefer;
429                }
430                "require" => {
431                    self.a2a_client.card_trust_policy = crate::channels::CardTrustPolicy::Require;
432                }
433                _ => {}
434            }
435        }
436    }
437}