Who sopsy is for
sopsy does not replace SOPS. It makes SOPS delightful.
Generate
Your keypair is minted in hardware: the Secure Enclave (Touch ID) or a YubiKey (PIN and a touch). The private key never leaves it; your public key is recorded pending in .sopsy.yml.
Vouch & approve
You open a PR; any active member vouches and promotes your public key from pending into .sops.yaml’s recipient list — recording who approved you, and when, in .sopsy.yml.
Re-key secrets
Every encrypted file is re-wrapped for the new recipient set — so your key, and only listed keys, can open them.
Edit & commit
git pull, then your key decrypts into your editor and re-encrypts on save. Commit the ciphertext; plaintext .env stays gitignored.
SOPS stays the encryption engine. age stays the cryptography. Your Secure Enclave or YubiKey holds the private key.
sopsy orchestrates all three with safe defaults, great diagnostics, and a scriptable interface — so the right thing is finally the easy thing.
Why not just use sops?
You already can. sopsy removes the sixteen ways you can get it wrong.
Everything here runs sops + age under the hood. The difference is the friction — the steps you have to remember, the flags you have to know, and the safety rails that aren't there until you build them.
✕Hand-write .sops.yaml creation_rules, craft .gitignore rules, build .env.example, then encrypt by hand.
→One idempotent command writes every file and seeds an encrypted .env.encrypted for you.sopsy init
✕Run age-plugin-se keygen or age-plugin-yubikey --generate, copy the public key, paste it into config without fat-fingering it.
→The key is made in the best allowed store your machine has (Secure Enclave, YubiKey, keychain), printed prominently, and recorded in config automatically.sopsy init
✕Hand-edit .sops.yaml, run sops updatekeys on every file — and just trust on faith that the key really belongs to them.
→The newcomer joins to record a pending request (no access yet); any active member approves — vouching for the key, promoting it, and re-keying every secret in one step.sopsy join → sopsy approve
✕git blame .sops.yaml and hope the commit history tells the story of who let whom in.
→Every member carries requested_at, approved_at, and approved_by — one command prints the whole audit trail as a table.sopsy recipient list
✕Hand-edit .sops.yaml, run updatekeys on each file, and miss that the data key never changed.
→recipient remove re-wraps every file for the remaining members. The data key stays the same, so rotate-data-key re-encrypts under a new one. Then change the secret values: old ciphertext in git history still opens.sopsy recipient remove → sopsy rotate-data-key
✕Generate a new key, add it, remove the old one, re-key every file, and redo your approval by hand.
→One command swaps your key under the same name and approval, and re-wraps every secret. --new-data-key also re-encrypts under a fresh data key.sopsy recipient rotate
✕Pass --input-type/--output-type yourself — .env.encrypted has no extension sops recognizes.
→File type is auto-detected (dotenv / yaml / json / binary); your $EDITOR just opens.sopsy edit
✕Decrypt the whole file into an editor, find the line, retype the value, and hope nothing else changed.
→Asks for the value with hidden input, or reads it from stdin, so it never lands in shell history. unset removes one.sopsy set API_KEY
✕Decrypt to a plaintext .env and remember to delete it, or wire up sops exec-env yourself.
→Decrypts into one command's environment and writes nothing to disk. Several files merge; later ones win.sopsy run -- bin/dev
✕Call sops --encrypt with the right --input-type, then redirect the output yourself and hope the artifact matches a creation rule.
→sopsy encrypt writes a .encrypted artifact, auto-detecting the format — .env, json, yaml, ini — all encrypt values only; anything else as one blob.sopsy encrypt [file] -o file.encrypted
✕sops --decrypt with manual type flags, then redirect — easy to clobber the wrong file or leak plaintext to disk.
→sopsy decrypt prints plaintext to stdout or writes it with -o file.sopsy decrypt file.encrypted [-o file]
✕Dig through docs to learn which formats support partial, values-only encryption versus whole-file.
→sopsy types prints exactly which extensions value-encrypt (.env / .yaml / .json / .ini); everything else is encrypted as a single binary blob.sopsy types
✕Roll your own grep for tracked plaintext and hope it catches every case.
→Seven hygiene invariants, a clear pass/fail checklist, and a non-zero exit on any violation.sopsy check
✕Generate an age key on a laptop, paste the private half around, hand-edit .sops.yaml, and remember to delete the local copies.
→A guided ceremony provisions a portable key, waits while you store one CI secret (SOPS_AGE_KEY), then deletes the local files and re-keys. A Linux runner with only sops installed can decrypt.sopsy recipient ci
✕Dig through $PATH, tool versions, Enclave and YubiKey status by hand.
→A grouped, colorful report you can paste straight into a GitHub issue, including which key store auto would use. sopsy deps installs whatever is missing.sopsy doctor → sopsy deps
✕Nothing reminds you to keep an offline recovery key. Lose the device, lose the secrets.
→One guided ceremony generates a portable emergency key, waits while you store it offline, then deletes the local copy and registers it — and doctor and check nag until it exists.sopsy recipient break-glass
●Net effect: the same battle-tested crypto, minus the foot-guns — every interactive prompt also has a flag, so a human at a terminal and an unattended CI job run the exact same tool.