I really enjoy using Claude Code, though I’m not a fan of the common practice of exporting API keys directly in a global shell configuration file like .zshrc. It feels insecure to have secrets floating around, and it increases the risk of accidentally committing them to version control.
A better approach is to keep keys encrypted and only load them into the environment when you are working in a specific project’s directory. This post outlines a straightforward, secure, and directory-aware workflow using GPG and direnv.
The OpenRouter Context
For this example, I’m using OpenRouter to access Claude Code, which offers benefits like provider failover and budget controls. According to their integration guide, the claude CLI requires the following environment variables:
export ANTHROPIC_BASE_URL="https://openrouter.ai/api"
export ANTHROPIC_AUTH_TOKEN="$OPENROUTER_API_KEY"
export ANTHROPIC_API_KEY="" # Must be explicitly empty
Our goal is to manage the $OPENROUTER_API_KEY secret securely.
Create and Encrypt the Secrets File
Emacs’s built-in EasyPG integration (epa-mode) makes handling GPG-encrypted files transparent.
- Create the secret file: In your project, open a new file named
.env-secrets.gpgby typingC-x C-f .env-secrets.gpg. - Add all required variables: Add the complete configuration, including your secret key.
export OPENROUTER_API_KEY='sk-or-v1-...'
export ANTHROPIC_BASE_URL="https://openrouter.ai/api"
export ANTHROPIC_AUTH_TOKEN="$OPENROUTER_API_KEY"
export ANTHROPIC_API_KEY=""
- Save the file: When you save (
C-x C-s), Emacs will prompt you to select your GPG public key for encryption.
Your secrets are now encrypted on disk.
Configure direnv to Load Secrets
Next, we use direnv to automatically load the secrets.
- Create the
.envrcfile: Create a file named.envrcin your project root with the following content. The2>/dev/null || truepart makes the command more robust by preventing errors if the secrets file doesn’t exist.
# .envrc
eval $(gpg -dq .env-secrets.gpg 2>/dev/null || true)
- Allow direnv: In your terminal, run
direnv allow.
Now, whenever you cd into this directory, direnv will use GPG to decrypt .env-secrets.gpg in memory and load the variables into your environment. When you cd out, the variables are cleared.
Prevent Committing Secrets
Explicitly ignore the encrypted secrets file in your project’s .gitignore to prevent it from ever being committed.
echo ".env-secrets.gpg" >> .gitignore
Use Claude Code
With the environment variables automatically loaded by direnv, you can now run the client:
claude
A Better Workflow
This setup gives me peace of mind. My secrets are never stored in plaintext, they never pollute my global shell, and they can’t be accidentally committed to Git.
When I enter a project directory, the keys are there for me. When I leave, they’re gone. It’s a simple, secure, and automatic workflow that gets out of the way and lets me focus on the actual work.