0009 — Secrets hygiene: server-side generation, names-only in git¶
Status: accepted (2026-07-23)
Context¶
The repository's initial import (40d8e62) committed a real
aim-backend/.env. The later cleanup (1120727) deleted the file, but a
deletion does not unpublish it: the full contents remain readable in git
history to anyone with a clone. The old scripts/ deploy helpers also
took a server password from the environment. The rebuilt platform needs a
posture where a repository leak cannot leak production credentials again.
Decision¶
- Names only in git. Committed env files are names-only templates
(
aim-backend/.env.exampleand equivalents); every real.envpattern is gitignored. Documentation and workflows reference variable names, never values. - Secrets are generated on the server.
deploy/gen-env.shruns on the VPS and writes/opt/aim-store/deploy/.env,/opt/aim-store/deploy/portal.env.production, and/opt/aim-store/monitoring/.env(chmod 600), generating random values (DB passwords,JWT_SECRET,NEXTAUTH_SECRET, webhook secrets) and leaving provider credentials as fill-in placeholders. Deploy workflows copy configs but never env files. - CI holds only small-blast-radius credentials: an SSH deploy key and a
read:packagesGHCR pull token (ADR 0005). - Everything in the exposed
.envis treated as compromised and must be rotated before the new stack goes live: database passwords,JWT_SECRET, Firebase service-account key, Entity Sport token, SMS/SMTP credentials, and any gateway keys it contained.
Consequences¶
- A repository clone or leak yields variable names, not credentials.
- The history itself is not rewritten — the repo has active clones and a rewrite would invalidate every checkout for cosmetic benefit; rotation, not erasure, is what actually revokes the exposed values.
- Rotation is an operator task and is tracked as such; until it is done the historical exposure stands.
- New components must ship a names-only template in the same PR that adds the component.