DEV Community

Wes
Wes

Posted on

Secrets, Agents, and .env Files

Your .env file has your database credentials in it. Your Stripe key. Your AWS secret. Maybe a JWT signing key you generated at 2am and never rotated.

Your AI agent can see all of it.

When you give an agentic coding tool access to your project directory, it can read every file in that directory. That includes .env, .env.local, .env.production, and whatever other secrets files you've got sitting in the root of your project. The agent doesn't know those are sensitive. It just sees files. And if you ask it to "clean up the project structure" or "fix the config," there's nothing stopping it from including those files in a commit.

One git add . and your secrets are in version history. Even if you delete the file in the next commit, they're still there. Permanently. Unless you rewrite history, and if you don't know how to do that, you probably won't.

This is preventable. Let's prevent it.

Layer 1: .gitignore

This is the bare minimum. If you don't have a .gitignore that covers your secrets files, stop reading and go add one.

# .gitignore
.env
.env.*
.env.local
.env.production
.env.staging
Enter fullscreen mode Exit fullscreen mode

This tells git to ignore these files entirely. They won't show up in git status, won't get staged by git add ., and won't end up in commits.

Two caveats. First, .gitignore only works on files that aren't already tracked. If your .env file was committed at some point in the past, adding it to .gitignore won't remove it from history. You need to run git rm --cached .env and then commit that removal. Second, .gitignore is a suggestion to git, not a security boundary. Someone (or some agent) can still force-add an ignored file with git add -f .env. It's a speed bump, not a wall.

Layer 2: pre-commit secret scanning

Speed bumps are good, but you want an actual wall. That's where pre-commit hooks come in.

There are several tools that do this. git-secrets from AWS, detect-secrets from Yelp, gitleaks, and trufflehog are the most common. They all do roughly the same thing: scan staged changes for patterns that look like secrets and block the commit if they find any.

Here's a quick setup with gitleaks as a pre-commit hook:

# Install gitleaks (macOS)
brew install gitleaks

# Or grab the binary from GitHub releases for other platforms
Enter fullscreen mode Exit fullscreen mode

If you're using pre-commit (the framework):

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.21.2
    hooks:
      - id: gitleaks
Enter fullscreen mode Exit fullscreen mode

Then run:

pre-commit install
Enter fullscreen mode Exit fullscreen mode

Now every commit gets scanned before it's created. If the agent stages a file with something that looks like AKIAIOSFODNN7EXAMPLE in it, the commit fails. The secret never enters version history.

This matters more with agentic tools than with manual development. When you're typing code yourself, you generally know when you're touching a secrets file. An agent doesn't have that awareness. It's just completing the task you gave it. The pre-commit hook catches what the agent doesn't think about.

Layer 3: stop putting secrets in files

This is the longer-term fix. If your secrets aren't in files in your project directory, your agent can't commit them.

Environment variable managers like direnv, dotenvx, or platform-specific solutions like Vercel's environment variables, Netlify's env settings, or AWS Parameter Store all keep secrets out of your repo entirely. The values exist in your shell environment or your deployment platform, not in a file that git can touch.

For local development, direnv is the lightest lift:

# .envrc (this file IS in .gitignore)
export DATABASE_URL="postgres://..."
export STRIPE_KEY="sk_test_..."
Enter fullscreen mode Exit fullscreen mode

direnv loads these into your shell when you cd into the project and unloads them when you leave. Your application reads them from the environment the same way it would read from a .env file. The difference is that .envrc is loaded by your shell, not parsed by your application, and you've got it in .gitignore where it belongs.

The real win here is that your agent never sees the actual values. It sees process.env.STRIPE_KEY in your code, not the key itself.

A note on what your agent can access beyond files

Secrets in files are the obvious risk. But agents interact with more than your filesystem.

If you're using MCP servers to give your agent access to GitHub, Slack, databases, or other services, take a hard look at what tools are actually exposed. The GitHub MCP server, for example, exposes a delete_repository tool. It's right there in the tool list. Your agent probably doesn't need the ability to delete repos. It definitely doesn't need it by default.

Most MCP servers ship with everything enabled. That's the wrong default for agentic use. You want a deny-by-default posture: the agent gets access to the specific tools it needs and nothing else. We'll dig deeper into MCP server policies and tool scoping in a future article, but for now, audit what your agent has access to. You might be surprised.

The checklist

Your secrets protection should have these layers:

  1. .gitignore covering all secrets files. Bare minimum. Non-negotiable.
  2. Pre-commit scanning with gitleaks, detect-secrets, or similar. Catches what .gitignore misses.
  3. GitHub secret scanning and push protection enabled. Server-side safety net.
  4. Secrets in environment managers, not in files. Removes the risk at the source.
  5. Audit your agent's tool access. Least privilege applies to AI tools the same way it applies to everything else.

None of these are hard to set up. All of them are hard to retrofit after a leak.


This is Part 2 of the **Guardrails* series. Previously: Stop Letting Agents Push to Main. Next up: Why Your Agent Should Never Have Access to Production Data.*

Top comments (0)