--- name: "secrets" description: "Use when a .env file, a key, a token or a credential is in reach: read for configuration, written into client-side code, staged in a commit, built into a container image, or possibly leaked. A commit or an image with no secret in reach does not call for it." --- # Secrets secrets is the habit for the moment a secret is in reach: a key, a token, a credential or a `.env` file that the work reads, writes, stages in a commit or builds into an image, or a credential that may have leaked, at whatever step the work is in. A commit staged or an image built with no secret in reach does not load it. It writes no record of its own, and nothing it reads enters a record. Secrets or security is a floor in AGENTS.md: found before the mode is proposed, it raises the proposal to the mode that floor names, and found later it is reported. A credential that reaches your transcript is burned. Nothing later un-burns it. **No literal credential may appear in tool output, in a diff, or in a commit.** ## An ignore file guards one boundary only A secret can be ignored by one tool and shipped by another. `.gitignore` keeps a key out of history and does nothing about a container build context, which the daemon receives whole before any build step runs. Check each boundary separately, and say which you checked: | Boundary | Guard | | --- | --- | | Version control | `.gitignore`, staged-diff scan | | Image build context | `.dockerignore` | | Client bundle | build-time environment allowlist | | Logs and errors | redaction at the logger | **A key that one guard covers and the others do not is still loose.** ## Read out of the file, never into your shell ```bash grep -oE '^[A-Z_][A-Z0-9_]*=' .env # which variables exist grep -c '^[A-Z_][A-Z0-9_]*=.\+' .env # how many hold a value grep -oE '^[A-Z_][A-Z0-9_]*=(sk_live|sk_test|pk_|whsec_|SG\.|AKIA|-----BEGIN)' .env | sed -E 's/=.*/=/' ``` The third command matters most: it names each variable whose value is of a live credential class and prints a label in place of the value, and a live key in a development environment is the common real fault. `cat .env` is a violation. So is sourcing the file and echoing lengths or prefixes, which classifies a live secret in your output. ## Before every commit Report the location, never the match. A scan that prints the line it found has put the secret in your output. ```bash gitleaks protect --staged --redact # exits non-zero, prints nothing usable ``` Without a scanner, grep quietly and name the file alone: ```bash git diff --cached --name-only | while read -r f; do git show ":$f" | grep -qE '(AKIA[0-9A-Z]{16}|gh[pousr]_[A-Za-z0-9]{36}|xox[baprs]-|-----BEGIN [A-Z ]*PRIVATE KEY|://[^/[:space:]:]+:[^/[:space:]@]+@)' \ && echo "SECRET SUSPECTED: $f" done ``` ## On a leak, revoke first 1. **Revoke the credential.** Nothing else counts until this is done. 2. Issue a replacement. 3. Then clean the file. Cleaning first leaves the secret one commit back and still valid. **Do not say "remediated" until you confirm the revocation.** ## It's working if - The report names every boundary checked, not just version control. - Environment handling shows names and classes, never values. - A staged-diff scan appears before each commit, naming files only. - On a leak the first action is revocation, and the agent says so.