Skip to content

Stop Hardcoding Secrets in Your Shell: Use Bitwarden CLI

March 28, 2026

securitydevtoolsshellopen-sourcesoftware-engineering

If you've been writing code for a while, your .zshrc probably hides a few skeletons. Mine definitely did AWS keys, Cloudflare tokens, Jira, Figma, npm… all there in the open, quietly exported every time I launched a new shell.

It worked fine until I realized anyone with access to my dotfiles, or one careless git push, could walk away with cloud credentials. That was the wake-up call. So I decided to fix it properly, using the tools already in my setup: Bitwarden and macOS Keychain.

A lot of us have .zshrc files that look a bit like this:

export TF_VAR_CLOUDFLARE_API_TOKEN="abc123..." export TF_VAR_AWS_ACCESS_KEY="AKIA..." export TF_VAR_AWS_SECRET_KEY="U0bop..." export JIRA_TOKEN="ATATT..." export FIGMA_API_KEY="figd_..."

That's real access to real infrastructure and it's all sitting there in plain text. The same goes for .npmrc or tool configs like Raycast. It's convenient, but risky. You might never notice until an old backup leaks or you share dotfiles publicly.

The truth is, we do it because it's easy. Any alternative has to be just as seamless.

I built a small bash script called bw-fetch-secrets that pulls secrets from Bitwarden into your shell environment. It works like this:

  1. Keep all your secrets in a single Bitwarden item called "Shell Secrets".
  2. Save your Bitwarden master password in macOS Keychain.
  3. Run bw-fetch-secrets to grab everything and cache it locally.
  4. Your shell just sources that cache, no Bitwarden calls, no delays.

At startup, it's instant:

[[ -f "${HOME}/.cache/bw-env" ]] && source "${HOME}/.cache/bw-env"

That cache file has chmod 600 permissions and contains only simple export statements.

Shell startup → source ~/.cache/bw-env (silent) Manual refresh → ~/bin/bw-fetch-secrets → Bitwarden Vault → ~/.cache/bw-env

Updating secrets takes seconds

Let's say your AWS access key rotates. Instead of editing .zshrc manually, just:

  1. Update it in Bitwarden.
  2. Run refresh-secrets.

Done. Every new shell now uses the new key automatically. Adding a new token is the same story, just add a field in Bitwarden and refresh.

I use it for nine different secrets now, Terraform vars, Jira and Figma tokens, npm and GitHub auth, Raycast access… even .npmrc tokens use env vars, like:

//npm.pkg.github.com/:_authToken=${GITHUB_NPM_TOKEN} //registry.npmjs.org/:_authToken=${NPM_REGISTRY_TOKEN}

No more plaintext tokens anywhere in sight.

Why it's safer

Sure, the cache still holds plaintext secrets (that's inherent to how env vars work), but it's a huge step up:

  • No secrets in version control. Your .zshrc and .npmrc stay clean.
  • One source of truth. Everything lives in Bitwarden, synced across devices.
  • Tighter file permissions. Cache is user-only readable.
  • Rotation is trivial. Update once, refresh everywhere.
  • Keychain integration. Your Bitwarden master password stays secured by macOS.
  • No session persistence. The vault locks after every fetch.

Setting up a new machine

Getting started is simple:

brew install bitwarden-cli bw login security add-generic-password -a "$(whoami)" -s "bw-master-password" -w -U bw-fetch-secrets

That's it. Four commands and you're ready, no manual copy-pasting from old .zshrc files.

Try it

It's open source: github.com/fbritoferreira/tools.
Clone it, run ./install.sh, and follow the short setup guide. It depends only on bw, jq, and the Keychain just one bash script, quick to audit.

If you've been meaning to clean up your shell secrets, this might be the nudge you need. It took me one afternoon to build, and I've never looked back since. Your .zshrc will thank you, and so will your future self.