Skip to main content

AgeVaultProvider

Struct AgeVaultProvider 

Source
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

Source

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());
Source

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"),
)?;
Source

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?;
Source

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()?;
Source

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?;
Source

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()?;
Source

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()?;
}
Source

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}");
}
Source

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"),
}
Source

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
FileContentsUnix mode
<dir>/vault-key.txtage identity (private + public key comment)0600
<dir>/secrets.ageage-encrypted empty JSON object {}default

Refuses to overwrite a pre-existing vault at dir — see AgeVaultProvider::init_vault_at for the underlying guard and a force escape hatch.

§Errors

Returns AgeVaultError::VaultAlreadyExists if vault-key.txt or secrets.age already exists under dir, or 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.
Source

pub fn init_vault_at( key_path: &Path, vault_path: &Path, force: bool, ) -> Result<(), AgeVaultError>

Generates a fresh age keypair and an empty encrypted vault at explicit key_path and vault_path locations, mirroring AgeVaultProvider::load’s explicit-path signature.

Unlike AgeVaultProvider::init_vault (which always derives the standard vault-key.txt/secrets.age filenames from a directory), this accepts arbitrary target paths — the correct entry point when the caller has resolved --vault-key/--vault-path CLI overrides that may not follow the default directory/filename convention.

§Overwrite guard

If either key_path or vault_path already exists and force is false, the vault is left untouched and AgeVaultError::VaultAlreadyExists is returned — a partial prior state (only one of the two files present) is treated the same as a full prior vault, since it is itself evidence of an earlier init attempt worth protecting. Pass force: true to regenerate the keypair and overwrite both files unconditionally.

The existence check and the subsequent write are not wrapped in a single filesystem lock, so a racing concurrent call between the check and the write could still both pass the guard; this is a best-effort, not a hard mutual-exclusion guarantee.

§Errors

Returns AgeVaultError::VaultAlreadyExists when a vault already exists and force is false, or AgeVaultError on key/vault write failure or encryption failure.

§Examples
use std::path::Path;
use zeph_vault::AgeVaultProvider;

AgeVaultProvider::init_vault_at(
    Path::new("/etc/zeph/vault-key.txt"),
    Path::new("/etc/zeph/secrets.age"),
    false,
)?;

Trait Implementations§

Source§

impl Debug for AgeVaultProvider

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl VaultProvider for AgeVaultProvider

Source§

fn get_secret( &self, key: &str, ) -> Pin<Box<dyn Future<Output = Result<Option<String>, VaultError>> + Send + '_>>

Retrieve a secret by key. Read more
Source§

fn list_keys(&self) -> Vec<String>

Return all known secret keys. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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 more
Source§

impl<T> IntoRequest<T> for T

Source§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
Source§

impl<L> LayerExt<L> for L

Source§

fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>
where L: Layer<S>,

Applies the layer to a service and wraps it in Layered.
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more