It started at 1am on a Tuesday. We were rotating a Cloudflare API token — routine stuff, the old one was about to expire. We updated the .env in the active project, ran the deploy, everything worked. Then the staging environment for a different project broke. Same token, different .env, still pointing to the old value.
Fixed it. Then a third project broke the next morning.
That is when we ran the command that changed everything:
$ find ~/dev -name ".env" -not -path "*/node_modules/*" -not -path "*/.git/*"
./boundless-learning/.env
./gitpulse/.env
./gitpulse/api/.env
./noxterm/website/.env
./blindspot/.env
./blindspot/api/.env
./112schade/.env
./bitz-snoek/.env
./playnist/.env
...
$ find ~/dev -name ".env" -not -path "*/node_modules/*" -not -path "*/.git/*" | wc -l
47
Forty-seven .env files. On one machine.
47
.env files on one machine
6
duplicated keys across projects
8 months
forgotten with live credentials
The .env file audit that started it all
We spent the next hour opening every single one.
The duplicates. The Cloudflare API token — the one that just broke three projects — appeared in 6 different files. An OpenAI API key was in 8. The same Postmark server token sat in 4 projects, two of which had not been touched in over a year.
The expired ones. A Stripe test key that had been rotated months ago was still sitting in three .env files. It no longer worked, but nobody cleaned it up. If it had been accidentally used in production, the result would have been silent auth failures — mysterious 401s at 2am with no explanation.
Critical
The dangerous ones. A healthcare API project had a .env with a production database connection string. Full admin credentials. The project was archived — untouched for 8 months. But the credentials were still valid. Anyone with access to the machine could have connected to a production database with patient-adjacent data.
The forgotten ones. A side project from early 2024 — a weekend experiment, completely forgotten — still had a live Stripe secret key in its .env. Not a test key. The real one. Connected to a real account with a real credit card.
Forty-seven plaintext files with zero authentication, scattered across the filesystem, containing credentials we could not even remember storing. Some valid, some expired, no way to tell which without checking each one manually.
Migrating from .env files to macOS Keychain
We decided to move everything to the macOS Keychain using NoxKey and delete every .env file on the machine. The whole process took one afternoon.
The workflow for each project:
.env workflow
# Plaintext file, no auth
$ cat .env
DATABASE_URL=postgresql://...
OAUTH_CLIENT_SECRET=abc...
OPENAI_API_KEY=sk-proj-...
# Hope you .gitignored it
# Hope no agent reads it
# Hope you remember to update it
NoxKey workflow
# Step 1: Import the .env file
$ noxkey import noboxdev/gitpulse .env
✓ Imported 4 secrets
# Step 2: Verify everything landed
$ noxkey ls noboxdev/gitpulse/
# Step 3: Peek to confirm
$ noxkey peek noboxdev/gitpulse/OPENAI_API_KEY
sk-proj-...
# Step 4: Test it works
$ eval "$(noxkey get noboxdev/gitpulse/OPENAI_API_KEY)"
# Step 5: Delete the liability
$ rm .env
Import .env → Verify with ls → Peek to confirm → Test with eval → Delete .env
For projects that shared secrets — like the Cloudflare token that lived in 6 places — we stored it once under a shared prefix:
$ noxkey set shared/CLOUDFLARE_API_TOKEN --clipboard
✓ Stored shared/CLOUDFLARE_API_TOKEN
One token. One location. Accessible from any project. When we rotate it next time, we update it once. Not six times. Not three-out-of-six times.
The healthcare API credentials got an extra layer:
$ noxkey strict noboxdev/healthcare-api/DATABASE_URL
✓ Marked as strict — always requires Touch ID
Strict mode means that secret always requires Touch ID, even during a session unlock. No shortcuts for credentials that could expose patient data.
The first week without .env files
The first two days were friction city.
Every time we opened a terminal, muscle memory reached for the .env that was not there anymore. Instead of source .env or letting dotenv auto-load, the workflow was eval "$(noxkey get noboxdev/project/KEY)" with a Touch ID prompt for each secret.
Day two almost broke us. Debugging a webhook integration, restarting the server about fifteen times in an hour. Touch ID fifteen times. It felt excessive.
Tip
Then we discovered session unlock:
$ noxkey unlock noboxdev/blindspot
✓ Session unlocked — Touch ID skipped for noboxdev/blindspot/* until session expires
One Touch ID authentication, then every get under that prefix skips the prompt for the rest of the session. Unlock at the start of a work session and forget about it.
By day four, the new workflow felt natural. By end of week, it was invisible.
How AI agent security became the bigger win
Two weeks after the migration, we were pair-programming with Claude Code on the Blindspot project. The agent needed the Postmark token to test an email integration. Old workflow: it would have read the .env and the raw token would be sitting in the conversation context. Logged. Visible. Potentially leaked in an error message.
Instead, the agent ran noxkey get. NoxKey detected the agent by walking the process tree, encrypted the value with AES-256-CBC, wrote a self-deleting temp script, and returned a source command. The secret reached the shell environment, but the raw value never appeared in the conversation.
We had not even been thinking about AI agent security during the migration. We deleted our .env files because of the duplication and rotation mess. The agent safety was a side effect — and turned out to be the more important benefit. We use AI agents every day now. Every single session would have been reading plaintext secrets if those .env files still existed.
For more on this attack surface, we wrote about six specific ways agents can leak your secrets.
Six months without a single .env file
Key rotation is a non-event. When the Cloudflare token expires, we update it in one place. Every project picks up the new value next time it runs noxkey get. No hunting through directories. No grepping for old values. No "which three of the six copies did we forget to update?"
We know exactly what we have. noxkey ls shows every secret on the machine, organized by project. No more discovering a forgotten Stripe key in an archived repo. If it is in the Keychain, we can see it. If it is not, it does not exist on this machine.
$ noxkey ls
noboxdev/blindspot/POSTMARK_SERVER_TOKEN
noboxdev/blindspot/DATABASE_URL
noboxdev/gitpulse/DATABASE_URL
noboxdev/gitpulse/OAUTH_CLIENT_SECRET
noboxdev/gitpulse/OPENAI_API_KEY
noboxdev/healthcare-api/DATABASE_URL [strict]
shared/CLOUDFLARE_API_TOKEN
shared/CLOUDFLARE_ACCOUNT_ID
...
New projects start clean. No .env.example to copy and fill in. Store secrets once with noxkey set and load them with eval. The project directory has zero credential files. Nothing to accidentally commit, nothing for an agent to read, nothing to forget about when the project gets archived.
The background anxiety about plaintext credentials is gone.
The anxiety is gone. This one was unexpected. We did not realize how much low-grade background worry those plaintext files caused. "Did we .gitignore that correctly?" "Is that old project's .env still sitting there with live keys?" "Did the AI just read our database credentials?" Those questions do not exist anymore. The secrets are in the Keychain, behind Touch ID.
The honest downsides of leaving .env behind
It is macOS only. We work exclusively on macOS, so this is not a limitation for us. If you are on a mixed team, your Linux colleagues need a different solution. The principle is the same — use your OS credential store — but NoxKey will not help them.
Onboarding new team members takes an extra step. Instead of "here is the .env.example, fill in your keys," it is "install NoxKey, then noxkey set each key." More steps the first time. Simpler every time after.
Some tools expect .env files. Docker Compose, certain Node.js frameworks, Vercel's local dev server. For those, we generate a temporary .env from the Keychain, use it, and delete it. Not perfect. But the alternative is 47 plaintext files with no authentication.
Run this command right now
find ~/dev -name ".env" -not -path "*/node_modules/*" -not -path "*/.git/*" | wc -l
Whatever number you see — that is how many plaintext files with zero authentication are on your machine right now, containing credentials that any process, any agent, any accidental git push can expose.
NoxKey is not the only answer. But .env files are the wrong answer. Use your Keychain. Use 1Password CLI. Use something with actual authentication. Stop treating plaintext files as secret storage.
Key Takeaway
47 .env files with zero authentication, scattered across one machine, containing credentials we could not even remember storing. The fix: import them into your OS Keychain with noxkey import, verify with noxkey ls, then delete every .env file. One afternoon of work eliminates plaintext secrets, duplicate key rot, and AI agent exposure — permanently.
If NoxKey is the route you want to take:
brew install no-box-dev/noxkey/noxkey
Free, no account, no cloud, your secrets never leave your machine. The migration took one afternoon. Six months later, there is no going back.
Frequently asked questions
*How many .env files does the average developer have?*
We found 47 across one machine — spanning active projects, archived repos, and forgotten prototypes. Many contained duplicate or expired keys. Running noxkey scan . from your home directory will find yours.
*What replaces .env files?*
The macOS Keychain. It's hardware-encrypted, protected by Touch ID, and already on your Mac. NoxKey provides a developer CLI on top: noxkey import myorg .env migrates your secrets, then eval "$(noxkey get myorg/KEY)" loads them into your shell.
*Is it safe to delete .env files after migrating?*
Yes, once you've verified the import with noxkey ls myorg/ and confirmed each key with noxkey peek myorg/KEY (shows first 8 characters). We recommend keeping a backup for 48 hours, then deleting permanently.
*Does this work with Docker and CI/CD?*
NoxKey is for local development. For Docker and CI/CD, use your provider's secret management (GitHub Actions secrets, AWS Secrets Manager, etc.). NoxKey replaces the plaintext .env files on your development machine.
NoxKey is free and open source. brew install no-box-dev/noxkey/noxkey — GitHub | Website
Top comments (0)