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