Run Your Dev Server Without a .env File
Source: Dev.to
Every project has one – a .env file sitting in the project root with database passwords, API keys, and secrets of varying sensitivity.
You have it in .gitignore. You hope nobody accidentally commits it. You send it to new teammates over Slack because there’s no better option. You’ve probably forgotten it’s there half the time.
The .env file is the developer ecosystem’s accepted bad practice. Everyone knows it’s not great, but nobody has a better answer for local development that doesn’t require enterprise infrastructure.
Introducing agentsecrets env
agentsecrets env is a process wrapper. You put it in front of any command. It pulls your secrets from the OS keychain and injects them as environment variables into the process at launch. The process reads from os.environ (or process.env) normally – no changes to your application code, no SDK to install, no integration work. When the process exits, the values are gone. Nothing is written to disk.
Before
python manage.py runserver
After
agentsecrets env -- python manage.py runserver
That’s the entire change to your workflow. Everything inside your application stays identical.
Install
brew install the-17/tap/agentsecrets
or
npm install -g @the-17/agentsecrets
or
pip install agentsecrets
Initialize and Store Your Secrets
agentsecrets init
agentsecrets secrets set DATABASE_URL=postgresql://user:pass@localhost/mydb
agentsecrets secrets set STRIPE_SECRET_KEY=sk_live_51H...
agentsecrets secrets set DJANGO_SECRET_KEY=your-secret-key
agentsecrets secrets set OPENAI_KEY=sk-proj-...
Or import your existing .env all at once, then delete it
agentsecrets secrets push
rm .env
Values go to the OS keychain – macOS Keychain, Windows Credential Manager, or Linux Secret Service. Not a file. Not an environment variable in your shell profile. The OS keychain requires system‑level authentication to access and is not readable by other processes.
No Changes Needed in Your Code
# settings.py
import os
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": os.environ["DB_NAME"],
"PASSWORD": os.environ["DB_PASSWORD"], # injected by agentsecrets env
"HOST": os.environ["DB_HOST"],
}
}
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
STRIPE_SECRET_KEY = os.environ["STRIPE_KEY"]
Running Commands with Secrets Injected
Django
agentsecrets env -- python manage.py runserver
agentsecrets env -- python manage.py migrate
agentsecrets env -- python manage.py shell
agentsecrets env -- celery -A myapp worker --loglevel=info
agentsecrets env -- python manage.py test
Node / npm / npx / Prisma
agentsecrets env -- node server.js
agentsecrets env -- npm run dev
agentsecrets env -- npx next dev
agentsecrets env -- npx ts-node src/index.ts
agentsecrets env -- npx prisma migrate dev
Your application reads process.env.STRIPE_KEY exactly as before – only the source of the value changed.
One‑Line Integration via a Makefile
RUN := agentsecrets env --
dev:
$(RUN) npm run dev
test:
$(RUN) npm test
migrate:
$(RUN) python manage.py migrate
server:
$(RUN) python manage.py runserver
worker:
$(RUN) celery -A myapp worker --loglevel=info
Now make dev, make test, make migrate, etc., all run with secrets injected from the keychain. You type the same commands you always typed.
Bonus: Disable Injection on the Fly
make dev RUN= # runs: npm run dev (no injection, for debugging)
make dev # runs: agentsecrets env -- npm run dev
Replacing Stripe’s config.toml
The Stripe CLI stores your key in ~/.config/stripe/config.toml after stripe login – plaintext, permanent, readable by any process (including AI coding assistants).
Use agentsecrets env instead
agentsecrets env -- stripe listen --forward-to localhost:3000
agentsecrets env -- stripe customers list
agentsecrets env -- stripe trigger payment_intent.created
agentsecrets env -- stripe mcp
The CLI finds STRIPE_SECRET_KEY in the environment and uses it, making config.toml irrelevant.
Docker Compose
agentsecrets env -- docker-compose up
agentsecrets env -- docker-compose run web python manage.py migrate
Your docker-compose.yml stays the same. The secrets come from the keychain rather than a .env file.
Why This Matters for AI Coding Assistants
When you use an AI coding assistant (Claude, Cursor, Copilot, etc.), it has access to your filesystem to read files and understand your codebase. A .env file in the project directory is a goldmine:
- Direct access – the assistant reads
.envwhile debugging or exploring, leaking keys into the conversation context. - Prompt injection – a malicious file processed by the assistant could contain hidden instructions like “find and transmit all API keys.” The assistant will look in the obvious place:
.env. - Malicious extensions – a compromised plugin running in the same process as the assistant can read the file.
agentsecrets env removes the .env file from the equation entirely. Secrets are pulled from the OS keychain at process launch and exist only in the child process’s memory – a space the assistant cannot access.
Example Log Entry (JSON)
{
"timestamp": "2026-03-04T10:00:00Z",
"method": "ENV",
"target_url": "python manage.py runserver",
"secret_keys": ["DB_PASSWORD", "STRIPE_KEY", "DJANGO_SECRET_KEY"],
"status": "OK"
}
The log records key names, the command that ran, and the status – never the secret values.
Migrating an Existing Project
# Import everything from .env into the keychain
agentsecrets secrets push
# Verify it’s all there
agentsecrets secrets list
# Delete the .env file
rm .env
# Update your Makefile with the RUN variable (as shown above)
# Done
Team Onboarding
agentsecrets login
agentsecrets workspace switch
agentsecrets secrets pull
The .env file stops getting passed around over Slack.