pub struct AgeVaultProvider { /* private fields */ }Expand description
Age-encrypted vault backend.
Secrets are stored as a JSON object ({"KEY": "value", ...}) encrypted with an x25519
keypair using the age format. The in-memory secret values
are held in zeroize::Zeroizing buffers.
§File layout
<dir>/vault-key.txt # age identity (private key), Unix mode 0600
<dir>/secrets.age # age-encrypted JSON object§Initialising a new vault
Use AgeVaultProvider::init_vault to generate a fresh keypair and create an empty vault:
use std::path::Path;
use zeph_vault::AgeVaultProvider;
AgeVaultProvider::init_vault(Path::new("/etc/zeph"))?;
// Produces:
// /etc/zeph/vault-key.txt (mode 0600)
// /etc/zeph/secrets.age (empty encrypted vault)§Atomic writes
save writes to a .age.tmp sibling file first, then renames it
atomically, so a crash during write never leaves the vault in a corrupted state.
Implementations§
Source§impl AgeVaultProvider
impl AgeVaultProvider
Sourcepub fn new(
key_path: &Path,
vault_path: &Path,
) -> Result<AgeVaultProvider, AgeVaultError>
pub fn new( key_path: &Path, vault_path: &Path, ) -> Result<AgeVaultProvider, AgeVaultError>
Decrypt an age-encrypted JSON secrets file.
This is an alias for load provided for ergonomic construction.
§Arguments
key_path— path to the age identity (private key) file. Lines starting with#and blank lines are ignored; the first non-comment line is parsed as the identity.vault_path— path to the age-encrypted JSON file.
§Errors
Returns AgeVaultError on key/vault read failure, parse error, or decryption failure.
§Examples
use std::path::Path;
use zeph_vault::AgeVaultProvider;
let vault = AgeVaultProvider::new(
Path::new("/etc/zeph/vault-key.txt"),
Path::new("/etc/zeph/secrets.age"),
)?;
println!("{} secrets loaded", vault.list_keys().len());Sourcepub fn load(
key_path: &Path,
vault_path: &Path,
) -> Result<AgeVaultProvider, AgeVaultError>
pub fn load( key_path: &Path, vault_path: &Path, ) -> Result<AgeVaultProvider, AgeVaultError>
Load vault from disk, storing paths for subsequent write operations.
Reads and decrypts the vault, then retains both paths so that
save can re-encrypt and persist changes without requiring callers to
pass paths again.
This method performs blocking I/O on the calling thread. Use load_async
when calling from an async context to avoid stalling the tokio executor.
§Errors
Returns AgeVaultError on key/vault read failure, parse error, or decryption failure.
§Examples
use std::path::Path;
use zeph_vault::AgeVaultProvider;
let vault = AgeVaultProvider::load(
Path::new("/etc/zeph/vault-key.txt"),
Path::new("/etc/zeph/secrets.age"),
)?;Sourcepub async fn load_async(
key_path: &Path,
vault_path: &Path,
) -> Result<AgeVaultProvider, AgeVaultError>
pub async fn load_async( key_path: &Path, vault_path: &Path, ) -> Result<AgeVaultProvider, AgeVaultError>
Async variant of load — offloads blocking I/O to a spawn_blocking thread.
Use this when calling from an async context to avoid stalling the tokio executor.
§Errors
Returns AgeVaultError on key/vault read failure, parse error, decryption failure, or
if the blocking task panics.
§Examples
use std::path::Path;
use zeph_vault::AgeVaultProvider;
let vault = AgeVaultProvider::load_async(
Path::new("/etc/zeph/vault-key.txt"),
Path::new("/etc/zeph/secrets.age"),
).await?;Sourcepub fn save(&self) -> Result<(), AgeVaultError>
pub fn save(&self) -> Result<(), AgeVaultError>
Serialize and re-encrypt secrets to vault file using atomic write (temp + rename).
Re-reads and re-parses the key file on each call. For CLI one-shot use this is acceptable; if used in a long-lived context consider caching the parsed identity.
This method performs blocking I/O on the calling thread. Use save_async
when calling from an async context to avoid stalling the tokio executor.
§Errors
Returns AgeVaultError on encryption or write failure.
§Examples
use std::path::Path;
use zeph_vault::AgeVaultProvider;
let mut vault = AgeVaultProvider::load(
Path::new("/etc/zeph/vault-key.txt"),
Path::new("/etc/zeph/secrets.age"),
)?;
vault.set_secret_mut("MY_TOKEN".into(), "tok_abc123".into(), false)?;
vault.save()?;Sourcepub async fn save_async(&self) -> Result<(), AgeVaultError>
pub async fn save_async(&self) -> Result<(), AgeVaultError>
Async variant of save — offloads blocking I/O to a spawn_blocking thread.
Use this when calling from an async context to avoid stalling the tokio executor.
§Errors
Returns AgeVaultError on encryption or write failure, or if the blocking task panics.
§Examples
use std::path::Path;
use zeph_vault::AgeVaultProvider;
let mut vault = AgeVaultProvider::load(
Path::new("/etc/zeph/vault-key.txt"),
Path::new("/etc/zeph/secrets.age"),
)?;
vault.set_secret_mut("MY_TOKEN".into(), "tok_abc123".into(), false)?;
vault.save_async().await?;Sourcepub fn set_secret_mut(
&mut self,
key: String,
value: String,
overwrite: bool,
) -> Result<(), AgeVaultError>
pub fn set_secret_mut( &mut self, key: String, value: String, overwrite: bool, ) -> Result<(), AgeVaultError>
Insert or update a secret in the in-memory map.
Refuses to replace an existing key unless overwrite is true, so that callers cannot
silently destroy a previously-stored secret by accident — see #5955 (and the sibling
incident #5874, which hit the same gap in the zeph init durable-execution wizard before
this guard existed at the vault layer). Callers that intend an unconditional update (e.g.
OAuth token refresh) pass overwrite: true explicitly.
Call save afterwards to persist the change to disk.
§Errors
Returns AgeVaultError::AlreadyExists if key is already present and overwrite is
false. The in-memory map is left untouched in that case.
§Examples
use std::path::Path;
use zeph_vault::AgeVaultProvider;
let mut vault = AgeVaultProvider::load(
Path::new("/etc/zeph/vault-key.txt"),
Path::new("/etc/zeph/secrets.age"),
)?;
vault.set_secret_mut("API_KEY".into(), "sk-...".into(), false)?;
vault.save()?;Sourcepub fn remove_secret_mut(&mut self, key: &str) -> bool
pub fn remove_secret_mut(&mut self, key: &str) -> bool
Remove a secret from the in-memory map.
Returns true if the key existed and was removed, false if it was not present.
Call save afterwards to persist the removal to disk.
§Examples
use std::path::Path;
use zeph_vault::AgeVaultProvider;
let mut vault = AgeVaultProvider::load(
Path::new("/etc/zeph/vault-key.txt"),
Path::new("/etc/zeph/secrets.age"),
)?;
let removed = vault.remove_secret_mut("OLD_KEY");
if removed {
vault.save()?;
}Sourcepub fn list_keys(&self) -> Vec<&str>
pub fn list_keys(&self) -> Vec<&str>
Return sorted list of secret keys (no values exposed).
Keys are returned in ascending lexicographic order. Secret values are never included.
§Examples
use std::path::Path;
use zeph_vault::AgeVaultProvider;
let vault = AgeVaultProvider::load(
Path::new("/etc/zeph/vault-key.txt"),
Path::new("/etc/zeph/secrets.age"),
)?;
for key in vault.list_keys() {
println!("{key}");
}Sourcepub fn get(&self, key: &str) -> Option<&str>
pub fn get(&self, key: &str) -> Option<&str>
Look up a secret value by key, returning None if not present.
Returns a borrowed &str tied to the lifetime of the vault. For async use across await
points, use VaultProvider::get_secret instead, which returns an owned String.
§Examples
use std::path::Path;
use zeph_vault::AgeVaultProvider;
let vault = AgeVaultProvider::load(
Path::new("/etc/zeph/vault-key.txt"),
Path::new("/etc/zeph/secrets.age"),
)?;
match vault.get("ZEPH_OPENAI_API_KEY") {
Some(key) => println!("key length: {}", key.len()),
None => println!("key not configured"),
}Sourcepub fn init_vault(dir: &Path) -> Result<(), AgeVaultError>
pub fn init_vault(dir: &Path) -> Result<(), AgeVaultError>
Generate a new x25519 keypair, write the key file (mode 0600), and create an empty encrypted vault.
Creates dir and all missing parent directories before writing files. Existing files
are not checked — calling this on an already-initialised directory will overwrite both
the key and the vault, making the old key irrecoverable.
§Output files
| File | Contents | Unix mode |
|---|---|---|
<dir>/vault-key.txt | age identity (private + public key comment) | 0600 |
<dir>/secrets.age | age-encrypted empty JSON object {} | default |
§Errors
Returns AgeVaultError on key/vault write failure or encryption failure.
§Examples
use std::path::Path;
use zeph_vault::AgeVaultProvider;
AgeVaultProvider::init_vault(Path::new("/etc/zeph"))?;
// /etc/zeph/vault-key.txt and /etc/zeph/secrets.age are now ready.Trait Implementations§
Source§impl Debug for AgeVaultProvider
impl Debug for AgeVaultProvider
Auto Trait Implementations§
impl Freeze for AgeVaultProvider
impl RefUnwindSafe for AgeVaultProvider
impl Send for AgeVaultProvider
impl Sync for AgeVaultProvider
impl Unpin for AgeVaultProvider
impl UnsafeUnpin for AgeVaultProvider
impl UnwindSafe for AgeVaultProvider
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request