granith(1)
Secrets you decrypt locally, rotate from your shell, audit on the same wire.
A zero-knowledge config vault and a small Go CLI. The server stores ciphertext; your CLI does the rest.
~ · zsh · granith run
$ go install github.com/granith/granith/cmd/granith@latest
$ export GRANITH_SERVER=https://api.granith.dev
$ export GRANITH_TOKEN=grnth_kQ8…7P.4xVm…Yz
$ granith run -- node server.js
▸ fetched bundle · 3 secrets · 142 bytes · etag d4f1…
▸ decrypted in 11ms · running command
listening on :3000 what just happened
The CLI fetched a ciphertext bundle, unwrapped the project key locally with the token, decrypted in process memory, and exec'd your command with the values as env vars. The server returned bytes; it never held a key that could read them.
subsequent polls cost nothing
$ curl -sI -H 'if-none-match: "d4f1…"' \
-H "authorization: bearer $GRANITH_TOKEN" \
https://api.granith.dev/api/v1/bundle
HTTP/2 304 audit
One row per bundle fetch. No secret names, no values, no labels. Enough to spot a stolen token; not enough to leak which secret your process touches when.
{
"ts": "2026-05-15T09:42:11Z",
"actor_id": "tok_d8e2…",
"action": "bundle.fetch",
"ip": "203.0.113.42",
"user_agent": "granith-go/0.5.0"
} getting in
Invite-only, beta, single-user.
The internal security review is still open. We'd rather have a small honest beta than a big dishonest launch.