Found while implementing #29, which fixed the equivalent gaps in .dockerignore. Filed separately because the two files have different semantics and must be written to their own — the whole lesson of #29 is that deriving one from the other produces a file that is wrong in a way that looks careful.
Problem
Canonical .gitignore's secrets section is:
.env
.env.*
*.pem
*.key
Measured as reaching a build context in the #29 probes, and equally uncommitted-only-by-luck here:
prod.env / local.env — the <name>.env convention is common and .env.* does not match it (.env.* matches .env.production, not prod.env).
.envrc — direnv's config, a secrets file by convention, matched by nothing above.
*.p12 / *.pfx — certificate bundles that contain private keys.
Extensionless SSH private keys — id_rsa, id_dsa, id_ecdsa, id_ed25519. deploy/secrets/id_rsa.key was caught in the probes only because someone had given it a .key suffix; the default ssh-keygen output has none.
REPO_POLICIES.md states "Never commit secrets. .env files, credentials, API keys, and private keys must be in .gitignore. No exceptions." The current file does not deliver that for any of the shapes above.
.dockerignore needed **/ prefixes because it uses Go filepath.Match. .gitignore must NOT get those prefixes — an unanchored .gitignore pattern already matches at any depth, so prefixing would be both redundant and, for anchored patterns, actively wrong (**/binary would newly match sub/binary where /binary deliberately did not). Verified with git check-ignore during the #29 review.
That asymmetry is exactly why the two changes must not ride together: a single commit touching both invites the next reader to conclude the files are kept in sync.
Definition of done
Canonical .gitignore covers the shapes above, written to gitignore semantics — no **/ prefixes.
Verify with git check-ignore -v against planted files at the repo root and nested at least two directories deep, plus a positive control (an ordinary source file that must remain trackable). Reading the patterns is not verification.
Confirm nothing legitimate becomes untrackable — *.env in particular should be checked against any file a repo might genuinely want committed, e.g. a checked-in example.env or sample.env. If that conflict is real, prefer a negation (!example.env) over dropping the pattern, and record the decision.
Follow-up: consuming repos vendor .gitignore, so this needs the same propagation treatment. It can ride along with the #35 sweep as a separate commit, not merged into the .dockerignore one.
Note
This is parity with the pre-existing state rather than a regression — nothing got worse when #29 landed. But #29 tightened .dockerignore past what .gitignore covers, so the two now disagree about what counts as a secret, and .gitignore is the weaker of the two. A developer's prod.env is currently one git add away from the repository.
Found while implementing #29, which fixed the equivalent gaps in `.dockerignore`. Filed separately because the two files have **different semantics** and must be written to their own — the whole lesson of #29 is that deriving one from the other produces a file that is wrong in a way that looks careful.
## Problem
Canonical `.gitignore`'s secrets section is:
```
.env
.env.*
*.pem
*.key
```
Measured as reaching a build context in the #29 probes, and equally uncommitted-only-by-luck here:
- **`prod.env` / `local.env`** — the `<name>.env` convention is common and `.env.*` does not match it (`.env.*` matches `.env.production`, not `prod.env`).
- **`.envrc`** — direnv's config, a secrets file by convention, matched by nothing above.
- **`*.p12` / `*.pfx`** — certificate bundles that contain private keys.
- **Extensionless SSH private keys** — `id_rsa`, `id_dsa`, `id_ecdsa`, `id_ed25519`. `deploy/secrets/id_rsa.key` was caught in the probes only because someone had given it a `.key` suffix; the default `ssh-keygen` output has none.
`REPO_POLICIES.md` states "Never commit secrets. `.env` files, credentials, API keys, and private keys must be in `.gitignore`. No exceptions." The current file does not deliver that for any of the shapes above.
## Why this is filed rather than fixed alongside #29
`.dockerignore` needed `**/` prefixes because it uses Go `filepath.Match`. **`.gitignore` must NOT get those prefixes** — an unanchored `.gitignore` pattern already matches at any depth, so prefixing would be both redundant and, for anchored patterns, actively wrong (`**/binary` would newly match `sub/binary` where `/binary` deliberately did not). Verified with `git check-ignore` during the #29 review.
That asymmetry is exactly why the two changes must not ride together: a single commit touching both invites the next reader to conclude the files are kept in sync.
## Definition of done
- Canonical `.gitignore` covers the shapes above, written to **gitignore** semantics — no `**/` prefixes.
- Verify with `git check-ignore -v` against planted files at the repo root **and** nested at least two directories deep, plus a positive control (an ordinary source file that must remain trackable). Reading the patterns is not verification.
- Confirm nothing legitimate becomes untrackable — `*.env` in particular should be checked against any file a repo might genuinely want committed, e.g. a checked-in `example.env` or `sample.env`. If that conflict is real, prefer a negation (`!example.env`) over dropping the pattern, and record the decision.
- Follow-up: consuming repos vendor `.gitignore`, so this needs the same propagation treatment. It can ride along with the #35 sweep **as a separate commit**, not merged into the `.dockerignore` one.
## Note
This is parity with the pre-existing state rather than a regression — nothing got worse when #29 landed. But #29 tightened `.dockerignore` past what `.gitignore` covers, so the two now disagree about what counts as a secret, and `.gitignore` is the weaker of the two. A developer's `prod.env` is currently one `git add` away from the repository.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Found while implementing #29, which fixed the equivalent gaps in
.dockerignore. Filed separately because the two files have different semantics and must be written to their own — the whole lesson of #29 is that deriving one from the other produces a file that is wrong in a way that looks careful.Problem
Canonical
.gitignore's secrets section is:Measured as reaching a build context in the #29 probes, and equally uncommitted-only-by-luck here:
prod.env/local.env— the<name>.envconvention is common and.env.*does not match it (.env.*matches.env.production, notprod.env)..envrc— direnv's config, a secrets file by convention, matched by nothing above.*.p12/*.pfx— certificate bundles that contain private keys.id_rsa,id_dsa,id_ecdsa,id_ed25519.deploy/secrets/id_rsa.keywas caught in the probes only because someone had given it a.keysuffix; the defaultssh-keygenoutput has none.REPO_POLICIES.mdstates "Never commit secrets..envfiles, credentials, API keys, and private keys must be in.gitignore. No exceptions." The current file does not deliver that for any of the shapes above.Why this is filed rather than fixed alongside #29
.dockerignoreneeded**/prefixes because it uses Gofilepath.Match..gitignoremust NOT get those prefixes — an unanchored.gitignorepattern already matches at any depth, so prefixing would be both redundant and, for anchored patterns, actively wrong (**/binarywould newly matchsub/binarywhere/binarydeliberately did not). Verified withgit check-ignoreduring the #29 review.That asymmetry is exactly why the two changes must not ride together: a single commit touching both invites the next reader to conclude the files are kept in sync.
Definition of done
.gitignorecovers the shapes above, written to gitignore semantics — no**/prefixes.git check-ignore -vagainst planted files at the repo root and nested at least two directories deep, plus a positive control (an ordinary source file that must remain trackable). Reading the patterns is not verification.*.envin particular should be checked against any file a repo might genuinely want committed, e.g. a checked-inexample.envorsample.env. If that conflict is real, prefer a negation (!example.env) over dropping the pattern, and record the decision..gitignore, so this needs the same propagation treatment. It can ride along with the #35 sweep as a separate commit, not merged into the.dockerignoreone.Note
This is parity with the pre-existing state rather than a regression — nothing got worse when #29 landed. But #29 tightened
.dockerignorepast what.gitignorecovers, so the two now disagree about what counts as a secret, and.gitignoreis the weaker of the two. A developer'sprod.envis currently onegit addaway from the repository.clawbot referenced this issue2026-08-09 18:36:36 +02:00
clawbot referenced this issue2026-08-09 18:44:00 +02:00
clawbot referenced this issue2026-08-09 19:08:20 +02:00
clawbot referenced this issue2026-08-09 19:15:47 +02:00
clawbot referenced this issue2026-08-09 19:22:26 +02:00
clawbot referenced this issue2026-08-09 20:10:32 +02:00
clawbot referenced this issue2026-08-09 20:52:45 +02:00