Skip to main content

deepstrike_sdk/runtime/
credential_vault.rs

1use async_trait::async_trait;
2use std::collections::HashMap;
3use std::sync::Arc;
4use tokio::sync::RwLock;
5
6/// Read-only credential abstraction. Credentials never enter model context or session log.
7#[async_trait]
8pub trait CredentialVault: Send + Sync {
9    async fn get(&self, key: &str) -> Option<String>;
10}
11
12/// Reads credentials from environment variables.
13pub struct EnvCredentialVault;
14
15#[async_trait]
16impl CredentialVault for EnvCredentialVault {
17    async fn get(&self, key: &str) -> Option<String> {
18        std::env::var(key).ok()
19    }
20}
21
22/// In-memory credential store, useful for tests and embedded deployments.
23pub struct InMemoryCredentialVault {
24    store: Arc<RwLock<HashMap<String, String>>>,
25}
26
27impl Default for InMemoryCredentialVault {
28    fn default() -> Self {
29        Self {
30            store: Arc::new(RwLock::new(HashMap::new())),
31        }
32    }
33}
34
35impl InMemoryCredentialVault {
36    pub fn new() -> Self {
37        Self::default()
38    }
39
40    pub async fn set(&self, key: impl Into<String>, value: impl Into<String>) {
41        self.store.write().await.insert(key.into(), value.into());
42    }
43}
44
45#[async_trait]
46impl CredentialVault for InMemoryCredentialVault {
47    async fn get(&self, key: &str) -> Option<String> {
48        self.store.read().await.get(key).cloned()
49    }
50}
51
52/// Tries each vault in order, returning the first non-`None` result.
53pub struct ChainedCredentialVault {
54    vaults: Vec<Box<dyn CredentialVault>>,
55}
56
57impl ChainedCredentialVault {
58    pub fn new(vaults: Vec<Box<dyn CredentialVault>>) -> Self {
59        Self { vaults }
60    }
61}
62
63#[async_trait]
64impl CredentialVault for ChainedCredentialVault {
65    async fn get(&self, key: &str) -> Option<String> {
66        for vault in &self.vaults {
67            if let Some(v) = vault.get(key).await {
68                return Some(v);
69            }
70        }
71        None
72    }
73}