Sets
Everything in hush is a set: some keys, a name you chose, an optional
description, and an optional note on when to use it. dev, prod,
Personal fal, Acme Production — all sets. There is no second concept to
learn.
A set lives in one of two places:
- Your library —
~/.hush/vaults/<name>, yours alone, never in any repo. A key you use across projects lives here once, so rotating it is one edit. - This project —
.hush/vault.json, committed, shared with your team.
A project uses sets. Its own default set is always used, as the floor;
everything else layers on top in the order you added it, later wins.
Your library is a catalog, not a floor. Nothing in it reaches a folder
until that folder asks: hush use <set>, or tell your agent which ones you want
and it adds them. Its default set is your catch-all (hush add K=v --library
with no --to lands there); use it in a folder with
hush use default --library. Add more from the library any time.
.hush/envs.json records only the names — a teammate who clones the repo
gets “this project uses a set called acme-production” and supplies their own.
Because that list travels with the repository, it does not reach your library
on its own. The first time a project you did not link yourself names one of
your library sets, hush asks before using it (on the terminal, or in a dialog),
once per project on this machine; until then the set is left out and hush says
so. hush use <set> and the app’s “Use here” count as confirming, and
hush use --confirm confirms the sets a cloned project already lists. The
confirmation is kept in ~/.hush, where no repository can write it.
hush add .env.production --as "Acme Production" --library \ --description "Live Stripe + Convex" --when "deploys only"hush add DATABASE_URL=postgres://… --to "Acme Production" # one value into a sethush add fal --as "Personal fal" --library # a known service: asks for FAL_KEY, hidden
hush use acme-production # this project uses ithush use # what this project uses, in orderhush use --not acme-productionA set you make from inside a project is used by that project automatically
(--no-use to opt out), so hush add .env --as Dev followed by hush npm run dev just works.
$ hush ls
YOUR LIBRARY (global) ● Acme Production (acme-production) 12 key(s) Live Stripe + Convex when: deploys only Personal fal (personal-fal) 1 key(s)
THIS PROJECT ● default (default) 2 key(s)
● = used by this project.hush ls <set> lists one set’s key names — never values.
Everything already in one pile? That is where everyone starts. Make the sets you want and move keys across — the value is re-encrypted under its new name, so it is a real move rather than a relabelling:
hush env move STRIPE_SECRET_KEY DATABASE_URL --to "Acme Production"Renaming works properly. hush env rename acme-production "Acme Prod EU"
re-seals every value under acme-prod-eu and updates any project using the old
name. Nothing is left pointing at a name that no longer exists.
Values are cryptographically bound to their set: a staging ciphertext cannot
be moved into the prod slot, even by someone editing the JSON by hand.