lightshuttle_secrets/source/mod.rs
1//! Secret source trait and built-in implementations.
2
3mod env_file;
4
5pub use env_file::EnvFileSource;
6
7use std::collections::HashMap;
8
9use crate::error::SecretError;
10
11/// A source of key-value secret pairs.
12///
13/// Implementations may read from a file, the system environment, a remote vault,
14/// or any other backing store. Each call to [`load`] returns a fresh snapshot.
15/// Sources are expected to be cheap to call repeatedly; consumers may invoke
16/// [`load`] multiple times without penalty.
17///
18/// This trait is used by higher layers (e.g. `lightshuttle-manifest`) to populate
19/// interpolation contexts. Built-in implementations include [`EnvFileSource`].
20///
21/// # Implementing a custom source
22///
23/// ```
24/// use lightshuttle_secrets::SecretSource;
25/// use std::collections::HashMap;
26/// use std::sync::Arc;
27///
28/// struct MyVaultSource {
29/// url: String,
30/// }
31///
32/// impl SecretSource for MyVaultSource {
33/// fn load(&self) -> Result<HashMap<String, String>, lightshuttle_secrets::SecretError> {
34/// // Fetch from a remote vault (hypothetical)
35/// Ok([("API_TOKEN".to_string(), "vault-secret".to_string())].into())
36/// }
37///
38/// fn source_name(&self) -> &str {
39/// "MyVault"
40/// }
41/// }
42/// ```
43///
44/// [`load`]: SecretSource::load
45/// [`EnvFileSource`]: crate::EnvFileSource
46pub trait SecretSource: Send + Sync {
47 /// Load all secrets from this source.
48 ///
49 /// Returns a map of variable names to their string values. The map may be empty
50 /// if the source contains no entries. Errors (via [`SecretError`]) indicate that
51 /// the source exists but is invalid or inaccessible.
52 ///
53 /// Callers may invoke this method multiple times and expect idempotent results
54 /// (assuming the source does not change between calls).
55 fn load(&self) -> Result<HashMap<String, String>, SecretError>;
56
57 /// Human-readable name used in error messages and diagnostics.
58 ///
59 /// For example: `.env`, `vault://prod`, `environment`, or a file path.
60 /// This name should be short and suitable for logging.
61 fn source_name(&self) -> &str;
62}