Should your coding agent be able to read your .env?
Security
No — and by default, most of them can. A coding agent works in your project
directory, your project directory contains a .env, and a .env is
production credentials in plaintext. The agent doesn't need to be malicious
for that to go wrong: it reads the file to understand your config, and now
your Stripe key is in a transcript that gets logged, retained, and — as
three vendors learned in 2026 — sometimes
talked out of the model by whoever can write to its input.
You can block the reads, and you should. But blocking is the shallow half of the fix, so this post covers both halves.
Blocking reads, per tool
Claude Code takes deny rules in .claude/settings.json. This stops the
Read tool from opening the files:
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)"
]
}
}
Cursor uses .cursorignore in the project root — add .env and
.env.* and the files are excluded from indexing and access.
GitHub Copilot offers content exclusions, configured at the repository or organization level rather than in a project file.
Do this today; it costs five minutes. Then look at what it doesn't cover.
Why blocking isn't enough
Agents run shell commands. A deny rule on the file-read tool doesn't
stop cat .env, grep -r "sk-" ., or a test runner that prints its
environment on failure. You can deny shell patterns too, but you're now
enumerating every way a Unix system can read a file, and the agent
legitimately needs the shell.
The agent isn't the only reader. The same file is readable by every npm
postinstall script, every MCP server you've configured, and every
dependency of your dev server. Fencing one reader leaves the value on disk
for all the others.
.env files multiply. .env.local, .env.production, the copy in the
other service's directory, the one a teammate sent over Slack. Ignore rules
are per-file, per-tool, per-machine; the sprawl isn't.
Some agents skip .env — and still need the values. The Claude Agent
SDK doesn't read .env files at all, which protects it from the file and
leaves you finding another way to configure the process it runs.
The setup where there's nothing to block
Delete the file and inject at spawn instead. Secrets live encrypted in
seekrit; seekrit run decrypts them locally and puts them in the child
process's environment, exactly where your code already looks:
seekrit secrets import .env --app storefront --env development
git rm --cached .env
seekrit run -- npm run dev
Now the working directory contains no credentials. The agent can read every
file in the repo — there's nothing sensitive to find. No deny rules to
maintain, nothing for a postinstall script to scoop up, and rotation
happens in one place instead of everywhere the file was copied. Local
overrides still work: seekrit run layers a local .env over the managed
set, so a .env.local with one dev value keeps its job. The dotenv
guide walks the migration.
One habit to pair with it: the values are now in the process environment,
so a printed process.env still leaks. If the agent needs to run your app,
seekrit run -- npm test gives the test process its secrets while the
agent sees only stdout — and seekrit's MCP tools do the same
for arbitrary commands via run_command.
When the agent itself is the threat model
Everything above assumes an editor agent working under your supervision. An
autonomous agent with a shell can run seekrit run -- env like anything
else, so spawn-time injection isn't a boundary against it. For that case
the credential has to stay outside the process entirely — a placeholder in
the environment and a credential broker
substituting the real value at the network edge. The five-level
ladder covers when to make that jump.
The short version
- Add deny rules for
.envin every agent you use — cheap, do it now. - Move the values out of the file:
seekrit secrets import .env, then delete it. This is the fix that doesn't depend on tool-by-tool rules. - Rotate anything that was in the file while agents could read it.
- If the agent runs unsupervised, keys out of the process too — use the proxy.