Aleph
Concepts

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

  1. Per-entry encryption — Each secret has its own derived key and random nonce/salt
  2. Zeroize on drop — Sensitive data is cleared from memory when no longer needed
  3. 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 detected
  • LeakDecision::Suspicious — Possible leak, log warning
  • LeakDecision::Leaked — Confirmed leak, block output

Integration: The RuntimeSecurityGuard runs leak detection on all tool outputs before sending to the LLM.


Safety Properties

  • Lock recoveryunwrap_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 loggedserde_json errors are debug-logged, not silently swallowed
  • ZeroizeSecretString clears memory on drop

Code Location

  • src/secrets/mod.rs — Module entry point
  • src/secrets/vault.rs — Encrypted file storage
  • src/secrets/crypto.rs — HKDF + AES-256-GCM
  • src/secrets/types.rsSecretString, DecryptedSecret
  • src/secrets/provider/ — Provider implementations (1Password, env)
  • src/secrets/injection.rs — Template placeholder resolution
  • src/secrets/leak_detector.rs — Output scanning
  • src/secrets/web3_signer.rs — Ethereum signing

See Also

On this page