Secret Management
Encrypted vault for API keys and tokens using AES-256-GCM with per-entry HKDF key derivation, plus secret injection and leak detection.
The secrets module provides encrypted storage for sensitive credentials (API keys, tokens, passwords). It uses AES-256-GCM with per-entry HKDF-SHA256 key derivation, supports multiple secret providers (1Password, environment variables), and detects accidental secret leaks in tool outputs.
Design Philosophy
- Per-entry encryption — Each secret has its own derived key and random nonce/salt
- Zeroize on drop — Sensitive data is cleared from memory when no longer needed
- Leak detection — Tool outputs are scanned for accidental secret exposure
Core Components
SecretVault
File-based encrypted storage:
pub struct SecretVault {
data: VaultData,
path: PathBuf,
}
impl SecretVault {
pub fn open(path: impl Into<PathBuf>) -> Result<Self> { /* ... */ }
pub fn get(&self, name: &str) -> Option<&EncryptedEntry> { /* ... */ }
pub fn store(
&mut self,
name: &str,
entry: EncryptedEntry,
) -> Result<()> { /* ... */ }
}Location: ~/.aleph/secrets.vault
Atomic writes: Vault file is written to a temp location and atomically renamed to prevent corruption.
Encryption
// Per-entry key derivation via HKDF-SHA256
let key = hkdf_derive(master_key, salt, info);
// AES-256-GCM encryption with random nonce
let ciphertext = aes_256_gcm_encrypt(plaintext, key, nonce);Master key: Protected by SecretString (zeroizes on drop).
DecryptedSecret
pub struct DecryptedSecret {
pub value: SecretString,
pub metadata: EntryMetadata,
}SecretString implements:
Zeroize— Clears memory on drop- Redacted
Debug/Display— Prints[REDACTED]instead of the value
Secret Providers
Secrets can be sourced from multiple providers:
1Password Integration
pub struct OnePasswordProvider {
vault: String,
item: String,
}Uses the 1Password CLI (op) to retrieve secrets. Health check validates authentication without exposing credentials in error messages.
Environment Variables
pub struct EnvProvider {
prefix: String,
}Reads secrets from environment variables with a configurable prefix.
Web3 Signer
pub struct EvmSigner {
resolver: Box<dyn SecretResolver>,
}Signs Ethereum transactions using securely stored private keys.
Secret Injection
Templates can reference secrets via placeholder syntax:
pub fn render_with_secrets(
template: &str,
resolver: &dyn AsyncSecretResolver,
) -> Result<String>Placeholder syntax: {{ secret:name }}
The resolver fetches the named secret from the vault and substitutes it into the template.
Leak Detection
Scans tool outputs for accidental secret exposure:
pub struct LeakDetector {
patterns: Vec<Regex>,
}
impl LeakDetector {
pub fn check(&self,
output: &str,
) -> LeakDecision { /* ... */ }
}Decision:
LeakDecision::Clean— No secrets detectedLeakDecision::Suspicious— Possible leak, log warningLeakDecision::Leaked— Confirmed leak, block output
Integration: The RuntimeSecurityGuard runs leak detection on all tool outputs before sending to the LLM.
Safety Properties
- Lock recovery —
unwrap_or_else(|e| e.into_inner())for all Mutex locks - No stderr leakage — Error messages use fixed safe text, raw stderr logged via
tracing::debug - JSON parse failures logged —
serde_jsonerrors are debug-logged, not silently swallowed - Zeroize —
SecretStringclears memory on drop
Code Location
src/secrets/mod.rs— Module entry pointsrc/secrets/vault.rs— Encrypted file storagesrc/secrets/crypto.rs— HKDF + AES-256-GCMsrc/secrets/types.rs—SecretString,DecryptedSecretsrc/secrets/provider/— Provider implementations (1Password, env)src/secrets/injection.rs— Template placeholder resolutionsrc/secrets/leak_detector.rs— Output scanningsrc/secrets/web3_signer.rs— Ethereum signing
See Also
- Security Primitives — Runtime guard integration
- PII Protection — Privacy filtering