Securing OpenRouter keys with Direnv and GPG

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.

  1. Create the secret file: In your project, open a new file named .env-secrets.gpg by typing C-x C-f .env-secrets.gpg.
  2. 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=""
  1. 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.

  1. Create the .envrc file: Create a file named .envrc in your project root with the following content. The 2>/dev/null || true part 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)
  1. 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.


Posted

in

by

Tags: