Skill: Secrets Management
Rules for storing, injecting, rotating, and protecting secrets in any
project. A secret is anything that grants access: passwords, API keys,
tokens, signing keys, certificates, connection strings.
Reference: engineering-principles.md §2 (Security).
The non-negotiables
- No secret in the repository, ever — not in code, config files,
migrations, tests, docs, commit messages, or CI logs.
.env files are
git-ignored; only .env.example (with placeholder values) is committed.
- Inject at runtime — environment variables or a secrets manager
(cloud KMS/Secrets Manager, Vault). The application fails fast and
clearly at startup when a required secret is missing.
- One secret per purpose — never reuse the JWT signing key as the
database password or share credentials across environments.
- Least privilege — credentials scoped to exactly what the consumer
needs (read-only DB user for reporting, deploy token limited to one repo).
- Rotation is routine, not an emergency — design so a secret can be
swapped without a code change (re-read from env/manager on restart).
Environment layout
.env.example # committed — names + placeholder values + comments
.env # git-ignored — local development values
CI secrets store # GitHub Actions secrets / equivalent — per environment
Production # secrets manager or platform env vars, never files in the image
Rules:
- Document every variable in
.env.example with a one-line comment.
- CI masks secrets in logs — but never
echo them anyway.
- Docker images never contain secrets (no
ENV with real values, no
baked-in files); inject at container start.
Detection — catch leaks before they land
- Enable secret scanning in CI: Semgrep
p/secrets, gitleaks, or the
platform's native scanner (see the infra-ci-cd skill).
- Add a pre-commit hook where the team tolerates it.
- Custom SAST rule for the project's own token formats.
Incident response — a secret leaked
Order matters; follow exactly:
- Rotate first — issue a new secret and deploy it.
- Revoke the leaked one — only after the replacement is live.
- Audit usage — check access logs for the exposure window.
- Then clean history — rewriting git history is cosmetic; the secret
is compromised the moment it was pushed. Never treat removal as the fix.
- Record the incident in
docs/lessons-learned.md.
Application-level secrets
- Password storage: modern memory-hard KDF — Argon2id (preferred) or
bcrypt with adequate cost. Never MD5/SHA-x, never reversible encryption.
- Signing keys: ≥ 256-bit random (
openssl rand -hex 32); see
be-jwt-auth-patterns for token-specific rules.
- Logs: never log secrets or PII; scrub headers like
Authorization
in middleware (see ops-observability).
When the consumer is an AI agent
A credential handed to an autonomous agent or an MCP server leaves the blast
radius this skill assumes: the agent can be steered by content it reads, so the
secret is only as scoped as the agent's least-agency boundary. Apply
sec-agent-security in addition to this skill — scope the token to the
narrowest capability, never place a long-lived secret where injected text can
reach it, and keep a kill switch that revokes without a deploy.
Common mistakes
| Mistake |
Cause |
Solution |
| Secret committed "temporarily" |
Local shortcut |
.env git-ignored from day one; scanner in CI |
| Same secret in dev and prod |
Convenience |
Per-environment values, separate stores |
| Secret removed from git but not rotated |
Treating cleanup as the fix |
Rotate + revoke first, always |
| App starts with missing secret and fails later |
No startup validation |
Validate required vars at boot, fail fast |
| Secrets in CI logs |
Debug echo |
Never print; rely on masked secret stores |
1---2name: sec-secrets-management3description: Use when handling any credential — API keys, database passwords, signing secrets, certificates — or when configuring environments and CI. Storage, injection, rotation, scanning, and incident response for leaked secrets.4---56# Skill: Secrets Management78Rules for storing, injecting, rotating, and protecting secrets in any9project. A secret is anything that grants access: passwords, API keys,10tokens, signing keys, certificates, connection strings.1112Reference: `engineering-principles.md` §2 (Security).1314## The non-negotiables15161. **No secret in the repository, ever** — not in code, config files,17 migrations, tests, docs, commit messages, or CI logs. `.env` files are18 git-ignored; only `.env.example` (with placeholder values) is committed.192. **Inject at runtime** — environment variables or a secrets manager20 (cloud KMS/Secrets Manager, Vault). The application fails fast and21 clearly at startup when a required secret is missing.223. **One secret per purpose** — never reuse the JWT signing key as the23 database password or share credentials across environments.244. **Least privilege** — credentials scoped to exactly what the consumer25 needs (read-only DB user for reporting, deploy token limited to one repo).265. **Rotation is routine, not an emergency** — design so a secret can be27 swapped without a code change (re-read from env/manager on restart).2829## Environment layout3031```32.env.example # committed — names + placeholder values + comments33.env # git-ignored — local development values34CI secrets store # GitHub Actions secrets / equivalent — per environment35Production # secrets manager or platform env vars, never files in the image36```3738Rules:39- Document every variable in `.env.example` with a one-line comment.40- CI masks secrets in logs — but never `echo` them anyway.41- Docker images never contain secrets (no `ENV` with real values, no42 baked-in files); inject at container start.4344## Detection — catch leaks before they land4546- Enable secret scanning in CI: Semgrep `p/secrets`, gitleaks, or the47 platform's native scanner (see the `infra-ci-cd` skill).48- Add a pre-commit hook where the team tolerates it.49- Custom SAST rule for the project's own token formats.5051## Incident response — a secret leaked5253Order matters; follow exactly:54551. **Rotate first** — issue a new secret and deploy it.562. **Revoke the leaked one** — only after the replacement is live.573. **Audit usage** — check access logs for the exposure window.584. **Then clean history** — rewriting git history is cosmetic; the secret59 is compromised the moment it was pushed. Never treat removal as the fix.605. Record the incident in `docs/lessons-learned.md`.6162## Application-level secrets6364- **Password storage:** modern memory-hard KDF — Argon2id (preferred) or65 bcrypt with adequate cost. Never MD5/SHA-x, never reversible encryption.66- **Signing keys:** ≥ 256-bit random (`openssl rand -hex 32`); see67 `be-jwt-auth-patterns` for token-specific rules.68- **Logs:** never log secrets or PII; scrub headers like `Authorization`69 in middleware (see `ops-observability`).7071## When the consumer is an AI agent7273A credential handed to an autonomous agent or an MCP server leaves the blast74radius this skill assumes: the agent can be steered by content it reads, so the75secret is only as scoped as the agent's least-agency boundary. Apply76`sec-agent-security` **in addition** to this skill — scope the token to the77narrowest capability, never place a long-lived secret where injected text can78reach it, and keep a kill switch that revokes without a deploy.7980## Common mistakes8182| Mistake | Cause | Solution |83|---------|-------|----------|84| Secret committed "temporarily" | Local shortcut | `.env` git-ignored from day one; scanner in CI |85| Same secret in dev and prod | Convenience | Per-environment values, separate stores |86| Secret removed from git but not rotated | Treating cleanup as the fix | Rotate + revoke first, always |87| App starts with missing secret and fails later | No startup validation | Validate required vars at boot, fail fast |88| Secrets in CI logs | Debug echo | Never print; rely on masked secret stores |