Why .env files leak
A .env file is plain text on disk. Every program running as you can read it, and so can every backup, sync folder and AI coding tool that indexes your project. Forget one line in .gitignore and it's committed; share your screen while it's open and it's on the recording. An export line in ~/.zshrc has the same problems, and also hands the key to every program you start from that shell.
The built-in way: the macOS Keychain
macOS ships a security command that stores secrets in your login keychain, encrypted on disk.
# store once (put -w last so you are prompted for the value and it stays out of shell history) $ security add-generic-password -a "$USER" -s OPENAI_API_KEY -w # pass it to one command $ OPENAI_API_KEY="$(security find-generic-password -a "$USER" -s OPENAI_API_KEY -w)" npm run dev
That gets the key off disk in plain text. What it doesn't do: while you're logged in, the keychain is unlocked and any program running as you can run that same security command and get the key without a prompt. Nothing records who read it, and you wire it up by hand for every project.
With CopyBuffer: Touch ID for every request
- Put the key in the vault. Select the key anywhere, for example a line like
OPENAI_API_KEY=sk-…in a web console or an old.env, and press ⌃⌘C. CopyBuffer opens a new vault entry with the variable name filled in. Check it and press ⌘S. Or edit any vault entry (⌘E) and type a name under Terminal environment variable (letters, digits and underscores, not starting with a digit). - Install the command. Settings ▸ Terminal ▸ Install links
~/.local/bin/cbvaultto the command inside the app. If~/.local/binisn't on your PATH, addexport PATH="$HOME/.local/bin:$PATH"to~/.zshrc. - Run your command through it. Touch ID asks once, showing which process wants which names, then the values go to that command and its child processes only.
# inject into one command only (safest) $ cbvault run OPENAI_API_KEY STRIPE_KEY -- npm run dev # export into the current shell / print one value / list names $ eval "$(cbvault export OPENAI_API_KEY)" $ cbvault get OPENAI_API_KEY $ cbvault list # one line in ~/.zshrc adds cbload / cbunload $ eval "$(cbvault shell-init)" $ cbload OPENAI_API_KEY Loaded: OPENAI_API_KEY $ cbunload OPENAI_API_KEY
Most dotenv loaders don't overwrite variables that are already set, so you can delete the key from .env and keep the rest of the file.
What protects the key
- Touch ID per request (or your Mac login password). The same names from the same terminal aren't asked again for 5 minutes, and locking the vault cancels that at once.
- Only the real cbvault. The app accepts requests only over a socket readable by you (
agent.sock, 0600), and checks through the kernel audit token that the caller runs as the same user and is acbvaultsigned with the app's own certificate. Copied or modified binaries are refused. - An access log. The requesting process path, working folder and your decision are written to the vault's encrypted Access log.
- Screen recording aware. While the screen is being recorded or shared, a locked vault won't open, and
cbvault getorcbvault exportwithout a pipe are refused because they would print the key on screen.cbvault runandeval "$(cbvault export …)"still work.
.env file | Keychain + security | cbvault run | |
|---|---|---|---|
| On disk | Plain text | Encrypted | Encrypted vault |
| Asks before handing out | Never | Not while logged in | Touch ID |
| Who receives it | Anything that reads the file | The command you wrap | The named command and its children |
| Log of who asked | None | None | Process, folder, time |
Limits
- A value loaded into a shell with
exportorcbloadis visible to every program you start from that shell. Prefercbvault run. - The program you run still receives the real key and could log or leak it.
- CopyBuffer has to be running, and the vault lives on this Mac only. There is no team sharing and no sync.
- It is a tool for your Mac, not for CI or servers. Use your CI's secret store there.
FAQ
Does cbvault work in CI or on a server?
No. It needs the CopyBuffer app and Touch ID on your Mac. Use your CI provider's secret store for pipelines.
Can I keep using tools that expect a .env file?
Yes. Remove only the secret lines from .env and start the tool with cbvault run NAME -- your-command. Most dotenv loaders keep variables that are already set in the environment.
What happens if I run cbvault while sharing my screen?
cbvault run and eval "$(cbvault export …)" work as usual. cbvault get and cbvault export without a pipe are refused, because they would print the key where the recording can see it.