1#[cfg(test)]
7#[path = "env_registry_tests.rs"]
8mod tests;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum EnvClassification {
12 KeepEnv,
13 ComposeEnv,
14 TrustedOperatorBootstrap,
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum RuntimePlacement {
19 HostOnly,
20 ContainerRequired,
21 ComposeInterpolation,
22 Both,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum LegacyBehavior {
27 Canonical,
28 WarnEnvOverride,
29 Advanced,
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub struct EnvKeySpec {
34 pub key: &'static str,
35 pub classification: EnvClassification,
36 pub placement: RuntimePlacement,
37 pub toml_destination: Option<&'static str>,
38 pub legacy_behavior: LegacyBehavior,
39 pub secret: bool,
40 pub plugin_option: Option<&'static str>,
41}
42
43pub const fn spec(
44 key: &'static str,
45 classification: EnvClassification,
46 placement: RuntimePlacement,
47 toml_destination: Option<&'static str>,
48 legacy_behavior: LegacyBehavior,
49 secret: bool,
50 plugin_option: Option<&'static str>,
51) -> EnvKeySpec {
52 EnvKeySpec {
53 key,
54 classification,
55 placement,
56 toml_destination,
57 legacy_behavior,
58 secret,
59 plugin_option,
60 }
61}
62
63pub const ENV_KEY_SPECS: &[EnvKeySpec] = &[
64 spec(
65 "SOMA_API_URL",
66 EnvClassification::KeepEnv,
67 RuntimePlacement::HostOnly,
68 Some("soma.api_url"),
69 LegacyBehavior::Canonical,
70 false,
71 Some("CLAUDE_PLUGIN_OPTION_SOMA_API_URL"),
72 ),
73 spec(
74 "SOMA_API_KEY",
75 EnvClassification::KeepEnv,
76 RuntimePlacement::HostOnly,
77 Some("soma.api_key"),
78 LegacyBehavior::Canonical,
79 true,
80 Some("CLAUDE_PLUGIN_OPTION_SOMA_API_KEY"),
81 ),
82 spec(
83 "SOMA_MCP_TOKEN",
84 EnvClassification::TrustedOperatorBootstrap,
85 RuntimePlacement::Both,
86 Some("mcp.api_token"),
87 LegacyBehavior::Canonical,
88 true,
89 Some("CLAUDE_PLUGIN_OPTION_API_TOKEN"),
90 ),
91 spec(
92 "SOMA_SERVER_URL",
93 EnvClassification::KeepEnv,
94 RuntimePlacement::HostOnly,
95 None,
96 LegacyBehavior::WarnEnvOverride,
97 false,
98 Some("CLAUDE_PLUGIN_OPTION_SERVER_URL"),
99 ),
100 spec(
101 "SOMA_MCP_AUTH_MODE",
102 EnvClassification::KeepEnv,
103 RuntimePlacement::Both,
104 Some("mcp.auth.mode"),
105 LegacyBehavior::Canonical,
106 false,
107 Some("CLAUDE_PLUGIN_OPTION_AUTH_MODE"),
108 ),
109 spec(
110 "SOMA_MCP_NO_AUTH",
111 EnvClassification::TrustedOperatorBootstrap,
112 RuntimePlacement::HostOnly,
113 Some("mcp.no_auth"),
114 LegacyBehavior::Advanced,
115 false,
116 Some("CLAUDE_PLUGIN_OPTION_NO_AUTH"),
117 ),
118 spec(
119 "SOMA_NOAUTH",
120 EnvClassification::TrustedOperatorBootstrap,
121 RuntimePlacement::Both,
122 Some("mcp.trusted_gateway"),
123 LegacyBehavior::Advanced,
124 false,
125 None,
126 ),
127 spec(
128 "SOMA_MCP_PUBLIC_URL",
129 EnvClassification::KeepEnv,
130 RuntimePlacement::Both,
131 Some("mcp.auth.public_url"),
132 LegacyBehavior::Canonical,
133 false,
134 Some("CLAUDE_PLUGIN_OPTION_PUBLIC_URL"),
135 ),
136 spec(
137 "SOMA_MCP_GOOGLE_CLIENT_ID",
138 EnvClassification::TrustedOperatorBootstrap,
139 RuntimePlacement::Both,
140 Some("mcp.auth.google_client_id"),
141 LegacyBehavior::Canonical,
142 true,
143 Some("CLAUDE_PLUGIN_OPTION_GOOGLE_CLIENT_ID"),
144 ),
145 spec(
146 "SOMA_MCP_GOOGLE_CLIENT_SECRET",
147 EnvClassification::TrustedOperatorBootstrap,
148 RuntimePlacement::Both,
149 Some("mcp.auth.google_client_secret"),
150 LegacyBehavior::Canonical,
151 true,
152 Some("CLAUDE_PLUGIN_OPTION_GOOGLE_CLIENT_SECRET"),
153 ),
154 spec(
155 "SOMA_MCP_GOOGLE_CALLBACK_PATH",
156 EnvClassification::TrustedOperatorBootstrap,
157 RuntimePlacement::Both,
158 Some("mcp.auth.google_callback_path"),
159 LegacyBehavior::Canonical,
160 false,
161 None,
162 ),
163 spec(
164 "SOMA_MCP_GOOGLE_SCOPES",
165 EnvClassification::TrustedOperatorBootstrap,
166 RuntimePlacement::Both,
167 Some("mcp.auth.google_scopes"),
168 LegacyBehavior::Canonical,
169 false,
170 None,
171 ),
172 spec(
173 "SOMA_MCP_AUTHELIA_ISSUER_URL",
174 EnvClassification::TrustedOperatorBootstrap,
175 RuntimePlacement::Both,
176 Some("mcp.auth.authelia_issuer_url"),
177 LegacyBehavior::Canonical,
178 false,
179 None,
180 ),
181 spec(
182 "SOMA_MCP_AUTHELIA_CLIENT_ID",
183 EnvClassification::TrustedOperatorBootstrap,
184 RuntimePlacement::Both,
185 Some("mcp.auth.authelia_client_id"),
186 LegacyBehavior::Canonical,
187 true,
188 None,
189 ),
190 spec(
191 "SOMA_MCP_AUTHELIA_CLIENT_SECRET",
192 EnvClassification::TrustedOperatorBootstrap,
193 RuntimePlacement::Both,
194 Some("mcp.auth.authelia_client_secret"),
195 LegacyBehavior::Canonical,
196 true,
197 None,
198 ),
199 spec(
200 "SOMA_MCP_AUTHELIA_CALLBACK_PATH",
201 EnvClassification::TrustedOperatorBootstrap,
202 RuntimePlacement::Both,
203 Some("mcp.auth.authelia_callback_path"),
204 LegacyBehavior::Canonical,
205 false,
206 None,
207 ),
208 spec(
209 "SOMA_MCP_AUTHELIA_SCOPES",
210 EnvClassification::TrustedOperatorBootstrap,
211 RuntimePlacement::Both,
212 Some("mcp.auth.authelia_scopes"),
213 LegacyBehavior::Canonical,
214 false,
215 None,
216 ),
217 spec(
218 "SOMA_MCP_GITHUB_CLIENT_ID",
219 EnvClassification::TrustedOperatorBootstrap,
220 RuntimePlacement::Both,
221 Some("mcp.auth.github_client_id"),
222 LegacyBehavior::Canonical,
223 true,
224 None,
225 ),
226 spec(
227 "SOMA_MCP_GITHUB_CLIENT_SECRET",
228 EnvClassification::TrustedOperatorBootstrap,
229 RuntimePlacement::Both,
230 Some("mcp.auth.github_client_secret"),
231 LegacyBehavior::Canonical,
232 true,
233 None,
234 ),
235 spec(
236 "SOMA_MCP_GITHUB_CALLBACK_PATH",
237 EnvClassification::TrustedOperatorBootstrap,
238 RuntimePlacement::Both,
239 Some("mcp.auth.github_callback_path"),
240 LegacyBehavior::Canonical,
241 false,
242 None,
243 ),
244 spec(
245 "SOMA_MCP_GITHUB_SCOPES",
246 EnvClassification::TrustedOperatorBootstrap,
247 RuntimePlacement::Both,
248 Some("mcp.auth.github_scopes"),
249 LegacyBehavior::Canonical,
250 false,
251 None,
252 ),
253 spec(
254 "SOMA_MCP_AUTH_DEFAULT_PROVIDER",
255 EnvClassification::TrustedOperatorBootstrap,
256 RuntimePlacement::Both,
257 Some("mcp.auth.default_provider"),
258 LegacyBehavior::Canonical,
259 false,
260 None,
261 ),
262 spec(
263 "SOMA_MCP_AUTH_ADMIN_EMAIL",
264 EnvClassification::TrustedOperatorBootstrap,
265 RuntimePlacement::Both,
266 Some("mcp.auth.admin_email"),
267 LegacyBehavior::Canonical,
268 false,
269 Some("CLAUDE_PLUGIN_OPTION_AUTH_ADMIN_EMAIL"),
270 ),
271 spec(
272 "SOMA_MCP_HOST",
273 EnvClassification::ComposeEnv,
274 RuntimePlacement::Both,
275 Some("mcp.host"),
276 LegacyBehavior::Canonical,
277 false,
278 None,
279 ),
280 spec(
281 "SOMA_MCP_SERVER_NAME",
282 EnvClassification::ComposeEnv,
283 RuntimePlacement::Both,
284 Some("mcp.server_name"),
285 LegacyBehavior::Canonical,
286 false,
287 None,
288 ),
289 spec(
290 "SOMA_MCP_PORT",
291 EnvClassification::ComposeEnv,
292 RuntimePlacement::Both,
293 Some("mcp.port"),
294 LegacyBehavior::Canonical,
295 false,
296 None,
297 ),
298 spec(
299 "SOMA_MCP_ALLOWED_HOSTS",
300 EnvClassification::ComposeEnv,
301 RuntimePlacement::Both,
302 Some("mcp.allowed_hosts"),
303 LegacyBehavior::Canonical,
304 false,
305 None,
306 ),
307 spec(
308 "SOMA_MCP_ALLOWED_ORIGINS",
309 EnvClassification::ComposeEnv,
310 RuntimePlacement::Both,
311 Some("mcp.allowed_origins"),
312 LegacyBehavior::Canonical,
313 false,
314 None,
315 ),
316 spec(
317 "SOMA_MCP_TRACE_HEADERS",
318 EnvClassification::KeepEnv,
319 RuntimePlacement::Both,
320 Some("mcp.trace_headers"),
321 LegacyBehavior::Canonical,
322 false,
323 Some("CLAUDE_PLUGIN_OPTION_TRACE_HEADERS"),
324 ),
325 spec(
326 "SOMA_MCP_STATIC_TOKEN_WRITE",
327 EnvClassification::TrustedOperatorBootstrap,
328 RuntimePlacement::Both,
329 Some("mcp.static_token_write"),
330 LegacyBehavior::Advanced,
331 false,
332 None,
333 ),
334 spec(
335 "SOMA_MCP_AUTH_BOOTSTRAP_SECRET",
336 EnvClassification::TrustedOperatorBootstrap,
337 RuntimePlacement::Both,
338 Some("mcp.auth.bootstrap_secret"),
339 LegacyBehavior::Canonical,
340 true,
341 None,
342 ),
343 spec(
344 "SOMA_MCP_AUTH_SQLITE_PATH",
345 EnvClassification::TrustedOperatorBootstrap,
346 RuntimePlacement::Both,
347 Some("mcp.auth.sqlite_path"),
348 LegacyBehavior::Canonical,
349 false,
350 None,
351 ),
352 spec(
353 "SOMA_MCP_AUTH_KEY_PATH",
354 EnvClassification::TrustedOperatorBootstrap,
355 RuntimePlacement::Both,
356 Some("mcp.auth.key_path"),
357 LegacyBehavior::Canonical,
358 false,
359 None,
360 ),
361 spec(
362 "SOMA_MCP_AUTH_ACCESS_TOKEN_TTL_SECS",
363 EnvClassification::TrustedOperatorBootstrap,
364 RuntimePlacement::Both,
365 Some("mcp.auth.access_token_ttl_secs"),
366 LegacyBehavior::Canonical,
367 false,
368 None,
369 ),
370 spec(
371 "SOMA_MCP_AUTH_REFRESH_TOKEN_TTL_SECS",
372 EnvClassification::TrustedOperatorBootstrap,
373 RuntimePlacement::Both,
374 Some("mcp.auth.refresh_token_ttl_secs"),
375 LegacyBehavior::Canonical,
376 false,
377 None,
378 ),
379 spec(
380 "SOMA_MCP_AUTH_CODE_TTL_SECS",
381 EnvClassification::TrustedOperatorBootstrap,
382 RuntimePlacement::Both,
383 Some("mcp.auth.auth_code_ttl_secs"),
384 LegacyBehavior::Canonical,
385 false,
386 None,
387 ),
388 spec(
389 "SOMA_MCP_AUTH_REGISTER_REQUESTS_PER_MINUTE",
390 EnvClassification::TrustedOperatorBootstrap,
391 RuntimePlacement::Both,
392 Some("mcp.auth.register_rpm"),
393 LegacyBehavior::Canonical,
394 false,
395 None,
396 ),
397 spec(
398 "SOMA_MCP_AUTH_AUTHORIZE_REQUESTS_PER_MINUTE",
399 EnvClassification::TrustedOperatorBootstrap,
400 RuntimePlacement::Both,
401 Some("mcp.auth.authorize_rpm"),
402 LegacyBehavior::Canonical,
403 false,
404 None,
405 ),
406 spec(
407 "SOMA_MCP_AUTH_MAX_PENDING_OAUTH_STATES",
408 EnvClassification::TrustedOperatorBootstrap,
409 RuntimePlacement::Both,
410 Some("mcp.auth.max_pending_oauth_states"),
411 LegacyBehavior::Canonical,
412 false,
413 None,
414 ),
415 spec(
416 "SOMA_MCP_AUTH_ALLOWED_REDIRECT_URIS",
417 EnvClassification::TrustedOperatorBootstrap,
418 RuntimePlacement::Both,
419 Some("mcp.auth.allowed_client_redirect_uris"),
420 LegacyBehavior::Canonical,
421 false,
422 None,
423 ),
424 spec(
425 "SOMA_MCP_TOKEN_ENCRYPTION_KEY",
426 EnvClassification::TrustedOperatorBootstrap,
427 RuntimePlacement::Both,
428 Some("mcp.auth.token_encryption_key"),
429 LegacyBehavior::Canonical,
430 true,
431 None,
432 ),
433];
434
435pub fn all_specs() -> &'static [EnvKeySpec] {
436 ENV_KEY_SPECS
437}
438
439pub fn spec_for(key: &str) -> Option<&'static EnvKeySpec> {
440 ENV_KEY_SPECS.iter().find(|spec| spec.key == key)
441}
442
443pub fn plugin_option_mappings() -> impl Iterator<Item = (&'static str, &'static str)> {
444 ENV_KEY_SPECS
445 .iter()
446 .filter_map(|spec| spec.plugin_option.map(|option| (option, spec.key)))
447}