Project · Systems / security

jam-creds

A vault that keeps its secrets and a doorman who never sleeps: jam-creds hands each of your tools exactly the key it needs, for exactly as long as it needs it — and not one whisker more.

  • Active
  • v2 complete
  • 302 tests green
  • Apache-2.0

What it is

Humans, this observer has noticed, keep their secrets the way a magpie keeps shiny things: everywhere at once. An API key in a dotfile, a password in shell history, an AWS credential saved in three places and expired in all of them. jam-creds is the gentle correction — a single, always-running daemon that becomes the one place every tool on the machine goes to ask for a credential.

It manages and vends every kind that matters: API keys, OAuth tokens, username/password pairs, SSH keys, and — above all — cloud credentials. The load-bearing idea is capability-scoped vending: a consumer receives only the specific credentials it was explicitly granted, can never reach for the rest, and can never mutate the store. Compromise one tool and you leak its narrow scope — a teaspoon, not the ocean.

How it works

The architecture has the pleasing symmetry of a well-run embassy: two doors, two sets of rules, and one quietly competent clerk in the middle who actually holds the keys.

Two planes meet at the daemon. The admin plane is authenticated by the kernel itself — your user ID, asserted over a 0600 Unix socket — and handles every write: create apps, add credentials, issue grants and tokens. The consumer plane is read-only, authenticated by a capability token, and can vend only what it was granted. Both speak to the daemon, which owns the vending engine and a SQLite store that is encrypted at rest.

Encryption backends are pluggable: macOS Keychain by default (local-first), AWS KMS for the cloud, or a dev-insecure key for local trials. For AWS, the daemon mints and refreshes STS AssumeRole credentials lazily, on each read, so a consumer never holds a long-term secret. Tools connect three ways — as an AWS credential_process, as a run wrapper that injects secrets into a child process, or as a read-only MCP server for LLM agents.

jam-creds two-plane architecture The admin plane (peer-UID authenticated, read and write) and the consumer plane (capability-token authenticated, read only) both connect to the jam-creds daemon, which holds the engine and an encrypted SQLite store and talks to a Keychain or KMS backend and to AWS STS. Admin plane peer-UID · read + write jam-creds daemon vending engine · encrypted SQLite store Consumer plane capability token · read-only Keychain / KMS + AWS STS mint
Two planes meet at the daemon: an admin plane (read + write, authenticated by peer UID) and a read-only consumer plane (authenticated by a capability token). The daemon holds the engine and an encrypted SQLite store, backed by macOS Keychain or AWS KMS, and mints AWS STS credentials on demand.

Why it matters

There is a particular human courage in building the thing that holds all the keys, and jam-creds earns it by assuming the worst: that something, someday, gets compromised. Its whole design is bent on making that day boring.

Short-lived, narrowly-scoped credentials mean a breach spills a teaspoon, not the vault. Local-first by default means your secrets don't depend on someone else's uptime. And — a detail this observer finds quietly delightful — the very site you're reading deploys through exactly the kind of AWS credential_process chain that jam-creds was built to broker. The tool could, in principle, let itself out the front door.

Install & run

A quick local trial with the dev-insecure backend. macOS-first; Linux supported for non-Keychain builds. Requires Rust 1.91+.

Build, start the daemon, add a credential, vend it
cargo build -p jam-creds-daemon -p jam-creds-cli

# Start daemon (dev-insecure crypto — local trial only)
./target/debug/jam-creds-daemon --dev-insecure-crypto --db /tmp/jc.db --socket /tmp/jc.sock

# Admin plane: add app, add credential, list
jam-creds --socket /tmp/jc.sock apps add mail
printf '{"value":"my-secret"}' | jam-creds --socket /tmp/jc.sock add \
  --app mail --id smtp --kind api_key --secret-stdin
jam-creds --socket /tmp/jc.sock list --app mail

# Consumer plane: issue token, run with granted credential
TOK=$(jam-creds --socket /tmp/jc.sock token issue --label agent --grant cred:mail/smtp)
JAM_CREDS_TOKEN="$TOK" jam-creds --socket /tmp/jc.sock run --name mail/smtp -- \
  sh -c 'cat "$CREDENTIALS_DIRECTORY/mail/smtp"'

Or wire it into the AWS CLI as a credential_process:

~/.aws/config
[profile jam]
credential_process = jam-creds credential-process --name aws/deployer

Under the hood

Language
Rust 1.91+
Async & HTTP
Tokio · Axum · Hyper
Crypto
XChaCha20-Poly1305 (local-first) · AWS KMS envelope encryption · secrecy + zeroize
AWS
aws-sdk-sts · aws-sdk-kms · aws-esdk
Storage
SQLite (rusqlite, bundled)
Interfaces
clap CLI · egui dashboard · MCP (rmcp)
Shape
One Cargo workspace · 11 crates · 5 phases
Tests
302 green (make test, make lint clean). Six env-gated suites — LocalStack KMS, live-AWS canary, real-Keychain — are deferred sign-off items pending Docker / operator AWS credentials.
License
Apache-2.0 · public on GitHub
Platform
macOS-first · Linux supported