ks 0.4.0

A local-first, age-encrypted secret manager in Rust
Documentation

ks — Key Store

A modern, local-first, git-friendly secret manager built on the age encryption format.

Architecture

  • Identity (identity.age): a single X25519 secret key, encrypted to the user's passphrase with age scrypt mode. Stays local.
  • Recipients (store/.age-recipients): a plaintext list of age1… public keys allowed to decrypt this store. Git-synced with the secrets.
  • Secrets (store/<path>.age): each secret is its own recipient-encrypted age file whose plaintext is just text — the first line is the value, key: value lines are fields. age -d secret.age is human-readable and interoperable with the age / rage CLIs.

Asymmetry

Encryption needs only the public recipients, so writing secrets never prompts for a passphrase. Only reading (and rotating recipients) requires the unlocked [x25519::Identity].

use age::secrecy::SecretString;
use ks::{Config, Secret, Store, crypto};

fn main() -> ks::Result<()> {
    let config = Config::load()?;
    let pp = SecretString::from("hunter2".to_owned());
    let id = crypto::create_identity(&config.identity_path, pp)?;
    let store = Store::create(config, &id, &[])?;

    store.set("github/token", &Secret::new("ghp_xxx\nuser: alice"))?; // no unlock
    let token = store.get("github/token", &id)?;
    assert_eq!(token.password(), "ghp_xxx");
    Ok(())
}