Agent config files are the new .env: secrets in Claude settings and opencode.json

For a decade, the canonical secret leak was the .env file: plaintext keys in a project directory, accidentally committed, synced to a dotfile repo, or pasted into a support thread. We built ignore rules, scanners, and git history rewrites to deal with it.

The AI coding agent ecosystem has quietly recreated the same problem with a new filename. Claude Code, OpenCode, and similar agents now encourage you to put API keys directly into their configuration files — and those files are plaintext, version-controlled, and shared more casually than .env ever was.

The config formats that now hold secrets

The patterns differ per agent, but they share a shape:

  • Claude Code reads a settings.json with an "env" block, and project-scoped .claude/settings.json files that live inside the repo itself. The quick-start path for many integrations is literally “add your key to the env block.”
  • OpenCode uses opencode.json, which has an env section and per-agent environment blocks. Example configs in docs and blog posts show real-looking keys.
  • The generic .env is still auto-loaded by most agents, so the old problem did not go away — it gained new siblings.

None of these files are encrypted, and all are read by the agent — so the keys inside end up in its context window, session transcripts, and anything it echoes back.

Why these are worse than .env

.env had conventions on its side: it was a known filename with a known .gitignore entry, and scanners had rules for it. Agent config files are newer, fewer ignore rules exist for them, and they are committed eagerly — opencode.json is meant to be shared with teammates so everyone’s agent behaves the same way. The same file gets synced to dotfile repos, pasted into gists for troubleshooting, and attached to GitHub issues. The file is machine-readable and human-pasteable, which is a bad combination.

Finding the leaks

Before fixing anything, you need to know where the values are. A few practical sweeps:

  • Run a scan for env-style files. trustless doctor includes an .env scan that walks your projects and reports files that parse as KEY=VALUE — the same shape used by agent config env blocks.
  • grep -r your config directories for api[_-]?key, sk-, and provider key prefixes (ANTHROPIC_, OPENAI_, GITHUB_).
  • Check git history: git log -S "sk-" --all across your repos, plus GitHub’s secret scanning results for anything that was pushed.
  • Search your dotfile repo and any shared configs, not just project trees.

What to do instead

The fix is to stop storing values and start storing names:

  1. Import what you have into your existing password store. trustless setup scans directories for env-style files, parses them, imports the entries, and backs up the originals before touching them.
  2. Remove the plaintext values from settings.json, opencode.json, and .env. The agent config references secrets by name only.
  3. Inject at runtime. trustless run -s OPENAI_API_KEY -- opencode or an equivalent wrapper resolves the value from pass or Bitwarden and injects it into the subprocess environment. The agent gets the value it needs; no file on disk contains it.
  4. Rotate anything that was committed. If a value ever reached git, a gist, or a transcript, treat it as public — rotation is the only step that actually unsends it.

Config files are the new .env, but the fix is the one that worked before: values in the store, names in the files, injection at the process boundary. The agent still works; the plaintext is gone.

trustless implements exactly this import-and-inject workflow — one static binary, existing pass or Bitwarden backends, no migration. The command reference lives at /docs/commands/.

When was the last time you grepped your dotfiles and agent configs for a key that should not be there?