tatara_engine/secrets/
mod.rs1pub mod env;
5pub mod sops;
6
7use anyhow::Result;
8use async_trait::async_trait;
9use serde::{Deserialize, Serialize};
10use tatara_core::domain::secret::{SecretProvider, SecretRef};
11use tracing::{debug, info};
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct SecretValue {
16 pub value: String,
17 pub version: String,
18}
19
20#[async_trait]
22pub trait SecretFetcher: Send + Sync {
23 async fn fetch(&self, key: &str) -> Result<SecretValue>;
25
26 async fn needs_rotation(&self, key: &str, current_version: &str) -> Result<bool> {
28 let current = self.fetch(key).await?;
29 Ok(current.version != current_version)
30 }
31}
32
33pub struct SecretResolver {
35 env_fetcher: env::EnvSecretFetcher,
36 sops_fetcher: sops::SopsSecretFetcher,
37}
38
39impl SecretResolver {
40 pub fn new() -> Self {
41 Self {
42 env_fetcher: env::EnvSecretFetcher,
43 sops_fetcher: sops::SopsSecretFetcher,
44 }
45 }
46
47 pub async fn resolve(&self, refs: &[SecretRef]) -> Result<ResolvedSecrets> {
49 let mut env_vars = std::collections::HashMap::new();
50 let mut files = Vec::new();
51
52 for secret_ref in refs {
53 let fetcher: &dyn SecretFetcher = match secret_ref.provider {
54 SecretProvider::Env => &self.env_fetcher,
55 SecretProvider::Sops => &self.sops_fetcher,
56 SecretProvider::Akeyless => {
57 anyhow::bail!("Akeyless provider not yet configured");
59 }
60 };
61
62 let value = fetcher.fetch(&secret_ref.key).await?;
63 debug!(name = %secret_ref.name, provider = ?secret_ref.provider, "resolved secret");
64
65 if let Some(env_var) = &secret_ref.env_var {
66 env_vars.insert(env_var.clone(), value.value.clone());
67 }
68
69 if let Some(mount_path) = &secret_ref.mount_path {
70 files.push(SecretFile {
71 path: mount_path.clone(),
72 content: value.value.clone(),
73 });
74 }
75 }
76
77 info!(
78 env_count = env_vars.len(),
79 file_count = files.len(),
80 "secrets resolved"
81 );
82 Ok(ResolvedSecrets { env_vars, files })
83 }
84}
85
86impl Default for SecretResolver {
87 fn default() -> Self {
88 Self::new()
89 }
90}
91
92#[derive(Debug, Default)]
94pub struct ResolvedSecrets {
95 pub env_vars: std::collections::HashMap<String, String>,
97 pub files: Vec<SecretFile>,
99}
100
101#[derive(Debug)]
103pub struct SecretFile {
104 pub path: String,
105 pub content: String,
106}