Keep secrets out of the repository, permanently
Never commit a secret, and scan every push for one — a leaked key is leaked forever.
Git does not forget. A key committed and then deleted in the next commit is still in the history, still in every clone, and still in every fork — so the only real remediation is rotating the credential, and rotating under pressure is when mistakes compound. Scanning is the one control that catches this before the push rather than after the incident.
What goes wrong: A live key in a public commit, found by a scanner that was not yours, minutes after the push.
What agents change: An agent writing a config file will cheerfully inline the key it can see in your environment, because that makes the example work.
You are violating it when
- `.env` appears in `git ls-files`.
- No secret scanner runs in CI or as a pre-commit hook.
- A key is 'removed' by a follow-up commit rather than rotated.
The usual objection: That deleting the file fixes it. The commit is the artifact, not the file, and it survives.
Treat credentials as toxic to source control, including API keys, tokens, private keys, passwords, signing material, and realistic examples copied into config. The safe pattern is to keep code and configuration references in the repository, while the actual values live in a secret manager, deployment environment, or local developer environment that is not committed.
The mechanism is about time and replication. A pushed commit is copied into remotes, clones, caches, and sometimes forks; removing the line later only creates a newer commit that no longer shows the value. The older commit can still be read, so prevention needs to happen before or during push, and remediation after exposure means revoking and rotating the credential, not just editing history.
The misconception is that deleting the file fixes it. That treats the current tree as the artifact, but Git stores the commit graph. Once a credential appears in a commit that others may have fetched, control of that value has been lost, even if the default branch looks clean afterward.
With an AI coding agent, accidental exposure becomes easier because the agent may see environment variables, shell output, sample .env files, or pasted credentials and then make the code “work” by embedding the value directly. Secret scanning and pre-commit or push-time gates turn that from a review-time hope into an automated stop.
Install it
npx klay practices add secrets-never-in-the-repo.github/workflows/klay-secret-scan.ymlcreate# Klay practice: secrets-never-in-the-repo # https://klaylearn.com/practices/secrets-never-in-the-repo # # Scans the repository for committed credentials on every push and pull request. # # This FAILS the build on a finding, unlike the PR-size workflow. The asymmetry # is deliberate: an oversized diff is a judgement call, and a live key in git # history is not — it is already unrecoverable by the time this runs, and the # only remaining question is whether you find out now or from someone else. # # Note what this cannot do: a secret already in your history stays in every # clone and every fork. Deleting the file does not remove it. Rotate the # credential; treat this workflow as the thing that tells you to. name: Secret scan.gitignoreappend-block# Local environment. `.env.example` is committed; nothing else here is. .env .env.* !.env.example *.pem *.key *.p12 *.pfx .envrc secrets.json service-account*.json credentials.json .aws/credentials
The previews are the first lines of each file; the command writes them in full. Existing files are never overwritten.
How you know it stuck
npx klay practices audit reports these checks for this practice:
secret-scanning-configuredno-committed-secretsenv-not-tracked
Where this comes from
- Push protection - GitHub DocsFirst-party control for blocking supported secrets before they enter hosted repository history.
- Removing sensitive data from a repository - GitHub DocsExplains why history, forks, and clones make rotation necessary after exposure.
- Secrets Management - OWASP Cheat Sheet SeriesSecurity-authority guidance placing detection and handling in the development workflow.
- gitleaks/gitleaksConcrete scanner that can inspect full Git history and run in hooks or CI.
- pre-commit/pre-commitAutomation layer that makes local checks routine instead of relying on memory.
Questions
- It was a test key.
- Rotate it anyway. The cost of rotating a test key is ten minutes; the cost of being wrong about which key it was is unbounded.