rs-secret-store

Secure in-memory secret storage with automatic zeroization, expiry, and redacted display
Installation
[dependencies]
philiprehberger-secret-store = "0.4.0"
Usage
use philiprehberger_secret_store::Secret;
use std::time::Duration;
// Wrap a secret value — automatically zeroized on drop
let api_key = Secret::new("sk-abc123".to_string());
// Or use From trait
let api_key: Secret<String> = Secret::from("sk-abc123");
// Access the value through a closure
api_key.expose(|key| {
println!("Key: {key}");
});
// Debug and Display never reveal the value
println!("{:?}", api_key); // Secret(****)
println!("{}", api_key); // ****
// TTL-based expiry
let token = Secret::with_ttl("temp-token".to_string(), Duration::from_secs(3600));
assert!(!token.is_expired());
// Safe access that returns None if expired
let result = token.expose_or(|t| t.clone());
// Rotation check
if token.needs_rotation(Duration::from_secs(86400)) {
// Token is older than 24 hours
}
SecretString from environment
use philiprehberger_secret_store::SecretString;
// Load from env var and immediately remove it from the environment
if let Some(secret) = SecretString::from_env("API_KEY") {
secret.expose(|key| {
// use key
});
}
// Or require the env var
let secret = SecretString::from_env_required("DATABASE_URL").expect("DATABASE_URL must be set");
SecretStore
use philiprehberger_secret_store::SecretStore;
use std::time::Duration;
let mut store = SecretStore::new();
// Insert secrets
store.insert("api_key", "sk-abc123");
store.insert_with_ttl("session_token", "tok-xyz", Duration::from_secs(3600));
// Retrieve and expose
if let Some(key) = store.expose("api_key") {
println!("Key: {key}");
}
// Debug shows keys but never values
println!("{:?}", store); // SecretStore { api_key: ****, session_token: **** }
// Clean up expired secrets
store.remove_expired();
API
Secret<T>
| Method | Description |
|---|
Secret::new(value) | Wrap a value as a secret |
Secret::with_ttl(value, ttl) | Wrap with a time-to-live |
.expose(f) | Access value via closure (panics if expired) |
.expose_or(f) | Access value via closure (returns None if expired) |
.try_expose(f) | Access value via closure (returns Err(Expired) if expired) |
.is_expired() | Check if the secret has expired |
.age() | Duration since creation |
.needs_rotation(max_age) | True if age exceeds max_age |
.clear() | Manually zeroize value without dropping |
From<String> | Convert String into SecretString |
From<&str> | Convert &str into SecretString |
From<Vec<u8>> | Convert Vec<u8> into SecretBytes |
SecretString
| Method | Description |
|---|
SecretString::from_env(key) | Load from env var, remove var |
SecretString::from_env_required(key) | Load from env var or return error |
SecretStore
| Method | Description |
|---|
SecretStore::new() | Create an empty store |
.insert(key, value) | Add a secret |
.insert_with_ttl(key, value, ttl) | Add a secret with TTL |
.insert_secret(key, secret) | Insert a pre-built SecretString |
.get(key) | Get a reference to a SecretString |
.expose(key) | Get + expose + clone the string |
.remove(key) | Remove and zeroize a secret |
.clear() | Remove and zeroize all secrets |
.remove_expired() | Remove all expired secrets |
.keys() | Iterate over key names |
.iter() | Iterate over (key, &SecretString) pairs |
.len() | Number of secrets |
.is_empty() | True if store is empty |
.contains_key(key) | Check if a key exists |
SecretError
| Variant | Description |
|---|
Expired | Secret has expired |
EnvVarNotFound(String) | Environment variable not found |
Development
cargo test
cargo clippy -- -D warnings
Support
If you find this project useful:
⭐ Star the repo
🐛 Report issues
💡 Suggest features
❤️ Sponsor development
🌐 All Open Source Projects
💻 GitHub Profile
🔗 LinkedIn Profile
License
MIT