If you’re using Claude Code and wondering where to store your API keys, database passwords, and other secrets — this is the page you need. The short answer: not in .env. Here’s the right approach and how to set it up.
Disclosure: We built OpaqueVault to solve this problem. This guide uses OpaqueVault as the specific solution.
The .env problem is covered in depth in Why your AI coding assistant is a secret leak waiting to happen. Short version: Claude Code is a filesystem agent running with your full user permissions. During debugging and development, it reads .env files — often without asking, as a natural part of understanding your project. When that happens, secrets enter its context window and can persist in conversation history. There is no documented per-message redaction of API keys or credentials before they reach the model.
OpaqueVault encrypts secret data on your machine before sending it to its remote API. A local daemon performs secret operations, with an MCP bridge connecting it to your AI client. See the quickstart for current setup and network requirements.
The right approach: MCP vault injection
An MCP vault interface lets Claude request a command by secret name. OpaqueVault’s daemon decrypts selected credentials locally and injects them into the child process. Claude receives exit code and run metadata.
Since v0.16.0, vault_run omits stdout and stderr from MCP responses. Output goes to the daemon terminal or agent log. The authorized subprocess can still write or transmit its credentials, and other tools may read local files; protect those separate paths.
OpaqueVault is built specifically for this pattern. Here’s how to set it up.
Step-by-step: storing secrets for Claude Code
1. Install OpaqueVault
brew install opaquevault-brew/tap/ovOn Linux or without Homebrew:
curl -fsSL https://get.opaquevault.com | shIf you prefer to verify before executing, download and inspect the script first:
curl -fsSL https://get.opaquevault.com -o install.sh# Review install.sh, then:sh install.shOr download a pre-built binary and verify it manually — Release signing walks the exact releases.opaquevault.com download URLs, the minisign public key, and the full verification path. The short version, once you have the binary, checksums file, and signature for your version:
minisign -Vm ov_<version>_checksums.txt -P <public key> # authenticate the checksumssha256sum -c ov_<version>_checksums.txt # verify the binary against them2. Initialize your vault
ov auth loginYou’ll enter your email and master password. The master password derives your Key Encryption Key (KEK) via Argon2id — the KEK never leaves your machine. Each secret gets its own randomly generated Data Encryption Key (DEK), encrypted under your KEK. The server stores only the encrypted DEK and encrypted secret value — it has no way to decrypt either. See the security model for full details including memory handling and session behavior.
3. Store your secrets
Create your app and store each secret:
# Create your appov app create my-appov app use my-app
# Databaseov secret set DATABASE_URL
# Stripe (secret key only — publishable keys are safe to put in source code)ov secret set STRIPE_SECRET_KEY
# OpenAI (if your app calls it directly)ov secret set OPENAI_API_KEY
# Any other serviceov secret set SENDGRID_API_KEYEach command prompts for the value. The value is encrypted client-side before anything is sent to the server.
4. Configure OpaqueVault as an MCP server
Add OpaqueVault to your Claude Code MCP config (~/.claude.json or a project-level .mcp.json):
{ "mcpServers": { "opaquevault": { "command": "ov", "args": ["mcp", "serve"] } }}Restart Claude Code after saving.
5. Set project context with .ov.yaml
In your project root, create .ov.yaml:
app: my-appenv: developmentThis tells OpaqueVault which app/environment to use when Claude calls vault tools in this directory. You don’t have to specify it on every call.
6. Start the MCP server
Start and unlock ov agent in a trusted terminal before connecting your MCP client. The bridge does not prompt interactively for your master password. If the bridge started before the daemon was running, restart the MCP client after starting the daemon. See troubleshooting and the security model for session and key lifecycle details.
7. Add OpaqueVault context to your CLAUDE.md
The more reliable way to direct Claude’s behavior isn’t per-session instructions — it’s your project’s CLAUDE.md, which Claude reads at every session start. Add:
## Secret managementThis project uses OpaqueVault for secrets.- Use vault_run to run commands that need credentials- Use vault_list_secrets to see what's available- Never read .env files for credentials — use vault_run insteadThese project instructions guide the agent toward vault_run across sessions. They are not an access-control boundary: restrict other tools and review behavior rather than relying on instructions alone.
8. What happens if the vault isn’t unlocked
All MCP tools require a running, unlocked ov agent. If the daemon was absent when the MCP bridge started, start and unlock it in a trusted terminal, then restart the MCP client. If the daemon’s session has locked or expired, the tools return SESSION_LOCKED; follow the troubleshooting instructions to restore it.
Restarting ov mcp serve alone does not unlock the daemon. The vault refuses the operation, but the client may respond by trying other tools. Do not supply credential values through chat as a workaround.
Your .env file is now either empty or contains only non-sensitive values like:
# .env — safe to have Claude read thisNODE_ENV=developmentLOG_LEVEL=debugPORT=3000# No secrets hereOrganizing secrets across environments
Secrets are scoped by app and environment. You set the active context with ov app use and ov env use, and all commands target it. Each environment has its own set of flat-named secrets:
my-app / development: DATABASE_URL, STRIPE_SECRET_KEY, OPENAI_API_KEYmy-app / staging: DATABASE_URL, STRIPE_SECRET_KEY, OPENAI_API_KEYmy-app / production: DATABASE_URL, STRIPE_SECRET_KEY, OPENAI_API_KEYWhen you switch from development to staging work, update .ov.yaml:
app: my-appenv: stagingvault_run now injects staging secrets automatically.
Listing and auditing what you’ve stored
# List all secrets (names only, no values)ov secret list
# Output:# DATABASE_URL# STRIPE_SECRET_KEY# OPENAI_API_KEY# SENDGRID_API_KEYClaude can also call vault_list_secrets to see what’s available. This returns names only — never values.
What about sharing secrets with teammates?
OpaqueVault is currently a single-developer tool. Each developer has their own vault encrypted with their own master password. The .ov.yaml config lives in version control so your team shares the app context automatically — but each developer provisions their own secret values.
That’s a real limitation for team workflows. For now, use your existing secure channel for the initial distribution of values: 1Password shared vaults, your organization’s secrets manager, or direct secure transfer. Team vaults with shared key management are on the roadmap.
Migrating from .env
If you have an existing .env file, use ov secret import — it handles quoted values, export prefixes, comments, and multi-line values correctly:
# Preview what will be imported (no writes)ov secret import .env --dry-run
# Importov secret import .env
# Verify what landedov secret listAfter import, remove the secret values from .env and leave only non-sensitive configuration (ports, log levels, feature flags).
Summary
| Don’t do this | Do this instead |
|---|---|
Store API keys in .env |
Store with ov secret set NAME |
| Let Claude read your secrets | Configure OpaqueVault as MCP server |
| Run authenticated commands directly | Use vault_run for subprocess injection |
| Secrets scattered across files | Centralized vault scoped by app and environment |
OpaqueVault is an end-to-end encrypted MCP secret manager for Claude Code and other AI coding agents — secrets are encrypted on your machine before leaving it, and the server stores only ciphertext it cannot decrypt. Get started free →
Related: How to keep API keys secure with Claude Code · MCP Secret Manager — How OpaqueVault Works
Corrections (April 2026):
- Verified MCP configuration paths are correct for Claude Code (
~/.claude.jsonuser-level,.mcp.jsonproject-level). - Added SHA-256 checksum verification command for install step.
Updated October 23, 2026: aligned OpaqueVault’s daemon and MCP response descriptions with the current tool contract. Local commands and output sinks remain part of the trust boundary.