Last year, a developer at a YC startup pushed a .env file to a public GitHub repo. It contained their Stripe live key, an OpenAI API key, and a production database URL. Automated scanners picked it up in under 30 seconds. By the time GitHub's secret scanning kicked in and revoked the tokens it recognized, someone had already racked up $14,000 in OpenAI API charges.
This isn't rare. GitGuardian's 2024 State of Secrets Sprawl report found 12.8 million new secrets exposed in public GitHub repositories in a single year. That's a 28% increase from the year before. And those are just the public repos — the private ones are worse, because nobody's scanning them.
Your .env file is a liability. Let me explain why, and what to do about it.
The .env file was never a security mechanism
The dotenv pattern came out of the Ruby community in 2012. It solved a real problem: developers were hardcoding database passwords and API keys directly into source files. Moving them to a separate file that you .gitignore was a genuine improvement.
But here's what a typical .env looks like on a developer's machine right now:
OPENAI_API_KEY=sk-proj-abc123...
STRIPE_SECRET_KEY=sk_live_...
DATABASE_URL=postgresql://admin:[email protected]:5432/main
CLOUDFLARE_API_TOKEN=v4x...
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI...
Five production credentials. Plaintext. No authentication to read them. No encryption at rest. No audit trail. Any process running as your user — a rogue npm postinstall script, a compromised VS Code extension, a careless docker build — can read that file without you knowing.
The problems compound fast
A single .env file is manageable. Nobody has a single .env file.
I counted mine. I had 47. Some in active projects, some in archived repos I'd forgotten about, some in Docker contexts that got copied into images. The same Cloudflare API token appeared in six different files. When I rotated it, I updated three of them and missed the other three for weeks.
There's no expiry tracking. No way to know which secrets are still valid. No audit trail of which process read which key. When a breach happens, your incident response starts with "wait, where do we even have this key?" and ends with grep -r across your entire home directory.
AI agents turned a bad practice into a critical vulnerability
The .env pattern assumed that only your application would read the file. That assumption shattered in 2025.
AI coding agents — Claude Code, Cursor, GitHub Copilot, Codex — routinely read project files to build context. Your .env is a project file. When an agent reads it, every secret in that file enters the model's context window. From there, values can appear in generated code, debug output, error messages, or conversation logs.
The agent isn't being malicious. It's trying to help. "Here's the error — your DATABASE_URL returns connection refused" — and now your production database hostname and credentials are in the chat history. Shared with your team. Logged by the provider. Maybe used as training data.
I wrote more about specific leak vectors in Six Ways AI Agents Leak Your Secrets. The short version: if a secret is in a file that an agent can read, assume it will be read.
What the alternative looks like
The macOS Keychain is an encrypted credential store that's been on every Mac for over 20 years. It's backed by the Secure Enclave, protected by Touch ID, and used by Safari, SSH, and every Apple-signed application. It's not a startup's custom vault — it's infrastructure that Apple maintains and audits.
NoxKey is a thin layer on top of the Keychain, purpose-built for developer secrets. Here's the workflow:
# Plaintext file anyone can read
$ cat .env
STRIPE_SECRET_KEY=sk_live_51Hx...
# Encrypted in Keychain, Touch ID on every access
$ noxkey set myorg/project/STRIPE_KEY --clipboard
✓ Stored myorg/project/STRIPE_KEY
$ eval "$(noxkey get myorg/project/STRIPE_KEY)"
# → Touch ID prompt → secret loaded into shell
# → raw value never written to disk
$ echo $STRIPE_KEY
sk_live_51Hx...
One secret, one location, accessible from any project directory. No files on disk. Touch ID every time. When an AI agent calls noxkey get, it detects the agent's process tree and returns an encrypted handoff instead of the raw value — the secret reaches the environment but never enters the conversation context.
The migration takes less time than reading this post
# Import your existing .env file
$ noxkey import myorg/project .env
✓ Imported 5 secrets
# Verify everything landed
$ noxkey ls myorg/project/
myorg/project/STRIPE_SECRET_KEY
myorg/project/OPENAI_API_KEY
myorg/project/DATABASE_URL
myorg/project/CLOUDFLARE_API_TOKEN
myorg/project/AWS_SECRET_ACCESS_KEY
# Peek at a value to confirm (shows first 8 chars)
$ noxkey peek myorg/project/STRIPE_SECRET_KEY
sk_live_...
# Now delete the liability
$ rm .env
Do that for every project. I did 47 in one afternoon. The find command that started it:
$ find ~/dev -name ".env" -not -path "*/node_modules/*" -not -path "*/.git/*" | wc -l
47
I wrote about the full process in Why I Deleted Every .env File on My Machine.
What about CI/CD?
This approach is for local development — your machine, your secrets, your Touch ID. CI/CD systems have their own secrets management: GitHub Actions secrets, Cloudflare environment variables, AWS Parameter Store, Vault. Those are purpose-built for that environment and they're fine.
The problem is the gap between "secrets managed in CI" and "secrets on your laptop." That gap is currently filled by plaintext files with no authentication, no encryption, and no access control. It should be filled by your operating system's credential store.
For a deeper look at what the macOS Keychain offers developers beyond just storage, read macOS Keychain for Developers.
The honest tradeoffs
This is macOS only. If your team is on Linux or Windows, NoxKey isn't the answer for them (though the principle still holds — use your OS credential store, not plaintext files).
There's friction. Touch ID on every access means you're authenticating more than you used to. Session unlock (noxkey unlock myorg/project) reduces this to one authentication per batch, but it's still more than the zero authentication that .env offers. That friction is the point — it means something is actually guarding access.
And you'll need to update your workflow. Instead of copying .env.example and filling in values, you'll run noxkey import once and then use eval commands. It's different. It took me about a day to stop reaching for .env instinctively.
Stop reaching for .env
12.8 million secrets exposed in public repos last year. AI agents reading every file in your project directory. Supply chain attacks targeting plaintext credentials. The .env pattern was a good idea in 2012. It's a liability in 2026.
Your operating system has an encrypted, hardware-backed credential store with biometric authentication. Use it.
.env file has no encryption, no authentication, and no access control. Every AI agent, every rogue script, and every accidental git push can read it. Move your secrets to the macOS Keychain — one location, Touch ID on every access, and AI agents never see the raw values. The migration takes minutes per project.
brew install no-box-dev/noxkey/noxkey
Free. No account. No cloud. Your secrets stay on your machine.