Secret sprawl on developer machines: an audit guide
Ask any team how many secrets live on a typical developer laptop and you’ll
get an estimate that is off by an order of magnitude. The one or two keys in
~/.bashrc are easy to account for. Everything else is not: the AWS
credential in ~/.aws/credentials, the GitHub token in ~/.gitconfig’s
credential helper, a dozen .env files scattered across cloned repos, the
Stripe test key in a scratch file, the Docker config with a registry
password, the npm token in ~/.npmrc. Secrets accumulate on developer
machines the way dust accumulates on a bookshelf — silently, and in every
crack.
Why sprawl is a real problem
Each copy of a secret is an attack surface. Malware on one laptop, a stolen
device, an AI coding agent that reads dotfiles while answering a question, a
curl command that dumps shell history — any of these exposes every secret
that machine holds. Sprawl also breaks rotation: when a key is compromised,
you cannot rotate what you cannot enumerate. The team rotates the key in the
vault, and the copy in ~/scratch/notes.md from 2023 keeps working.
An audit is the first step, and you don’t need fancy tooling to start.
Where secrets actually live
Shell and profile files. ~/.bashrc, ~/.zshrc, ~/.profile, and the
history files (~/.bash_history, ~/.zsh_history) are full of
export TOKEN=... lines and pasted commands. Grep for export .*= and for
known key prefixes.
Dotfiles and tool configs. ~/.gitconfig (credential helpers, sometimes
plaintext tokens), ~/.npmrc, ~/.docker/config.json (registry auth,
base64-encoded), ~/.aws/credentials, ~/.config/gh/hosts.yml (a GitHub
token in plaintext), ~/.netrc, ~/.pgpass. SSH keys with empty passphrases
deserve particular scrutiny.
Project files. .env files everywhere — and .env.example files that
someone filled in with real values. Config files committed to repositories,
editor settings, and VS Code tasks.json entries that embed tokens for build
steps.
Caches and logs. Shell history, editor swap files, ~/.cache/
directories, application logs. These are the ones people forget, and the
ones most likely to be read by tooling.
Running the audit
- Enumerate. Walk the home directory for the files above.
find ~ -name '.env*' -o -name '*.npmrc'gives you a list fast. - Scan with pattern tools. Run a secrets scanner with plain-file support over the whole home directory. High-entropy detection flags long random strings; triage the hits by hand.
- Check the git-history angle. Every cloned repo on the disk carries its own history, including deleted files. Your machine is a museum of everyone’s old secrets.
- Write it down. Keep a per-machine inventory: secret name, location, whether it’s still valid, and a rotation date. You can’t manage what you haven’t listed.
Reducing sprawl at the source
The audit finds the mess; process changes stop it from coming back. Rules that work in practice:
- Centralize. Keep secrets in a password store (the
passtool is the common default) or a vault, and never paste values into files. - Inject, don’t copy. Runtime injection — a tool that reads the store and sets environment variables for a subprocess — means the value exists in memory for the duration of a command, not in a file on disk.
- Give agents a broker. AI coding agents are a new and prolific source of sprawl: they write config files, persist context, and copy values into logs. A credential broker that injects secrets only into the commands they run keeps keys out of their context windows and session files.
That last point is what trustless
is: a zero-dependency, single-binary credential broker CLI that reads your
existing pass or Bitwarden store, injects secrets into subprocess
environments, and sanitizes output, with an append-only audit log of every
injection. Its trustless doctor command checks GPG validity, pass health,
and scans for stray .env files — a reasonable first pass for the audit
step too.
After your next audit, how many of the secrets you found were ones you had forgotten existed — and how many are still in rotation today?