ADR 0004 — Public repo secret hygiene
- Status: Accepted (policy), with open violations tracked in known_issues.md
- Date: 2026-04-16
Context
github.com/callmehetch/h5h is a public repository. Anyone on the internet can read every file and every commit. This is a deliberate choice (portfolio visibility, community contributions), but it shifts the burden of secret hygiene from "git-gated" to "never type a secret in a tracked file".
Decision
The following rules are binding for anything that lands on main:
- No real secrets in any committed file. Real secrets live only in
code/docker/.env(git-ignored) or in GitHub Actions secrets. - Every secret has a placeholder in
.env.examplewith a prefix likechangeme_.generate-secrets.shreplaces a subset automatically. - Homepage widgets must read API keys via
{{HOMEPAGE_VAR_*}}Mustache placeholders — never inline the literal key. - ACME / service emails are env vars, not hardcoded constants.
- Let's Encrypt private key (
data/traefik/acme/acme.json) is never committed —.gitignorehandles this. - Git history is public too. Rotating a credential by editing the current file does not remove it from history. Rotation in the upstream system (Cloudflare, CrowdSec, Google, etc.) is mandatory.
Checklist before every push
- [ ]
git status— no.env, noacme.json, no*.key/*.pem. - [ ]
git diff --cached— no values that look like tokens (40+ chars, base64-ish). - [ ] If a
HOMEPAGE_VAR_*placeholder changed, does.env.examplehave the new var?
Reasoning
- Public visibility is an invariant, not a bug. Assume every commit is indexed by scanners within minutes.
.gitignoreonly prevents adding. It does not prevent leaking something that was already committed.- Rotation cost << compromise cost. Rotating a CrowdSec key takes 30 seconds; cleaning up a compromised instance takes days.
Known violations
See known_issues.md. At time of writing:
- §1 — CrowdSec admin password committed in
docker/homepage/services.yaml:122. - §2 — ACME email hardcoded in
docker/traefik/traefik.yml:68. - §3 — Admin email hardcoded in
docker/scripts/setup_rbac.py.
These are tracked rather than fixed in the April 2026 docs-only pass. The rotation + env-var-ization is a follow-up PR.
Automation (see Phase 2 of the docs plan)
- CI job
docs-checkin.github/workflows/ci.ymlruns a lightweight regex scan ondocker/**/*.yamlfor long base64-ish values not wrapped in Mustache placeholders. - The pull-request template forces an env-var sanity check.
- Dependabot keeps GitHub Actions current.