1pub use zeph_config::{
11 AcpConfig, AcpLspConfig, AcpTransport, AgentConfig, CandleConfig, CandleInlineConfig,
12 CascadeClassifierMode, CascadeConfig, ClassifiersConfig, CompressionConfig,
13 CompressionStrategy, Config, ConfigError, CostConfig, DaemonConfig, DebugConfig, DetectorMode,
14 DiscordConfig, DocumentConfig, DumpFormat, ExperimentConfig, ExperimentSchedule, FocusConfig,
15 GatewayConfig, GenerationParams, GraphConfig, HookDef, HookMatcher, HookType, IndexConfig,
16 LearningConfig, LlmConfig, LlmRoutingStrategy, LogRotation, LoggingConfig, MAX_TOKENS_CAP,
17 McpConfig, McpOAuthConfig, McpServerConfig, McpTrustLevel, MemoryConfig, MemoryScope,
18 NoteLinkingConfig, OAuthTokenStorage, OrchestrationConfig, PermissionMode, ProviderEntry,
19 ProviderKind, ProviderName, PruningStrategy, RateLimitConfig, ResolvedSecrets, RouterConfig,
20 RouterStrategyConfig, ScheduledTaskConfig, ScheduledTaskKind, SchedulerConfig, SecurityConfig,
21 SemanticConfig, SessionsConfig, SidequestConfig, SkillFilter, SkillPromptMode, SkillsConfig,
22 SlackConfig, StoreRoutingConfig, StoreRoutingStrategy, SttConfig, SubAgentConfig,
23 SubAgentLifecycleHooks, SubagentHooks, TaskSupervisorConfig, TelegramConfig, TimeoutConfig,
24 ToolDiscoveryConfig, ToolDiscoveryStrategyConfig, ToolFilterConfig, ToolPolicy, TraceConfig,
25 TrustConfig, TuiConfig, VaultConfig, VectorBackend,
26};
27
28pub use zeph_config::{
29 AutoDreamConfig, CategoryConfig, ContextStrategy, DigestConfig, MagicDocsConfig,
30 MicrocompactConfig, PersonaConfig, TrajectoryConfig, TreeConfig,
31};
32pub use zeph_config::{DiagnosticSeverity, DiagnosticsConfig, HoverConfig, LspConfig};
33pub use zeph_config::{TelemetryBackend, TelemetryConfig};
34
35pub use zeph_config::{
36 ContentIsolationConfig, CustomPiiPattern, ExfiltrationGuardConfig, MemoryWriteValidationConfig,
37 PiiFilterConfig, QuarantineConfig,
38};
39pub use zeph_config::{GuardrailAction, GuardrailConfig, GuardrailFailStrategy};
40
41pub use zeph_config::A2aServerConfig;
42pub use zeph_config::ChannelSkillsConfig;
43pub use zeph_config::{FileChangedConfig, HooksConfig};
44
45pub use zeph_config::{
46 DEFAULT_DEBUG_DIR, DEFAULT_LOG_FILE, DEFAULT_SKILLS_DIR, DEFAULT_SQLITE_PATH,
47 default_debug_dir, default_log_file_path, default_skills_dir, default_sqlite_path,
48 is_legacy_default_debug_dir, is_legacy_default_log_file, is_legacy_default_skills_path,
49 is_legacy_default_sqlite_path,
50};
51
52pub use zeph_config::providers::{default_stt_language, default_stt_provider, validate_pool};
53
54pub mod migrate {
55 pub use zeph_config::migrate::*;
56}
57
58use crate::vault::{Secret, VaultProvider};
59
60pub trait SecretResolver {
65 fn resolve_secrets(
71 &mut self,
72 vault: &dyn VaultProvider,
73 ) -> impl std::future::Future<Output = Result<(), ConfigError>> + Send;
74}
75
76impl SecretResolver for Config {
77 async fn resolve_secrets(&mut self, vault: &dyn VaultProvider) -> Result<(), ConfigError> {
78 if let Some(val) = vault.get_secret("ZEPH_CLAUDE_API_KEY").await? {
79 self.secrets.claude_api_key = Some(Secret::new(val));
80 }
81 if let Some(val) = vault.get_secret("ZEPH_OPENAI_API_KEY").await? {
82 self.secrets.openai_api_key = Some(Secret::new(val));
83 }
84 if let Some(val) = vault.get_secret("ZEPH_GEMINI_API_KEY").await? {
85 self.secrets.gemini_api_key = Some(Secret::new(val));
86 }
87 if let Some(val) = vault.get_secret("ZEPH_TELEGRAM_TOKEN").await?
88 && let Some(tg) = self.telegram.as_mut()
89 {
90 tg.token = Some(val);
91 }
92 if let Some(val) = vault.get_secret("ZEPH_A2A_AUTH_TOKEN").await? {
93 self.a2a.auth_token = Some(val);
94 }
95 for entry in &self.llm.providers {
96 if entry.provider_type == crate::config::ProviderKind::Compatible
97 && let Some(ref name) = entry.name
98 {
99 let env_key = format!("ZEPH_COMPATIBLE_{}_API_KEY", name.to_uppercase());
100 if let Some(val) = vault.get_secret(&env_key).await? {
101 self.secrets
102 .compatible_api_keys
103 .insert(name.clone(), Secret::new(val));
104 }
105 }
106 }
107 if let Some(val) = vault.get_secret("ZEPH_HF_TOKEN").await? {
108 self.classifiers.hf_token = Some(val.clone());
109 if let Some(candle) = self.llm.candle.as_mut() {
110 candle.hf_token = Some(val);
111 }
112 }
113 if let Some(val) = vault.get_secret("ZEPH_GATEWAY_TOKEN").await? {
114 self.gateway.auth_token = Some(val);
115 }
116 if let Some(val) = vault.get_secret("ZEPH_DATABASE_URL").await? {
117 self.memory.database_url = Some(val);
118 }
119 if let Some(val) = vault.get_secret("ZEPH_DISCORD_TOKEN").await?
120 && let Some(dc) = self.discord.as_mut()
121 {
122 dc.token = Some(val);
123 }
124 if let Some(val) = vault.get_secret("ZEPH_DISCORD_APP_ID").await?
125 && let Some(dc) = self.discord.as_mut()
126 {
127 dc.application_id = Some(val);
128 }
129 if let Some(val) = vault.get_secret("ZEPH_SLACK_BOT_TOKEN").await?
130 && let Some(sl) = self.slack.as_mut()
131 {
132 sl.bot_token = Some(val);
133 }
134 if let Some(val) = vault.get_secret("ZEPH_SLACK_SIGNING_SECRET").await?
135 && let Some(sl) = self.slack.as_mut()
136 {
137 sl.signing_secret = Some(val);
138 }
139 for key in vault.list_keys() {
140 if let Some(custom_name) = key.strip_prefix("ZEPH_SECRET_")
141 && !custom_name.is_empty()
142 && let Some(val) = vault.get_secret(&key).await?
143 {
144 let normalized = custom_name.to_lowercase().replace('-', "_");
148 self.secrets.custom.insert(normalized, Secret::new(val));
149 }
150 }
151 Ok(())
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[tokio::test]
160 #[cfg(any(test, feature = "mock"))]
161 async fn resolve_secrets_with_mock_vault() {
162 use crate::vault::MockVaultProvider;
163
164 let vault = MockVaultProvider::new()
165 .with_secret("ZEPH_CLAUDE_API_KEY", "sk-test-123")
166 .with_secret("ZEPH_TELEGRAM_TOKEN", "tg-token-456");
167
168 let mut config = Config::load(std::path::Path::new("/nonexistent/config.toml")).unwrap();
169 config.resolve_secrets(&vault).await.unwrap();
170
171 assert_eq!(
172 config.secrets.claude_api_key.as_ref().unwrap().expose(),
173 "sk-test-123"
174 );
175 if let Some(tg) = config.telegram {
176 assert_eq!(tg.token.as_deref(), Some("tg-token-456"));
177 }
178 }
179}