Trigger: creating, using, rotating or revoking any credential. Owner: Jonny Allum. Policy anchors: POL-01, POL-04.
1. Where secrets live (and nowhere else)
- Password manager — the master copy of every credential, key and the secrets register (§3). Emergency access configured (POL-07 §4).
- VM environment — production runtime values only.
- `apps/shell/.env.local` — local dev values; gitignored;
.env.exampledocuments shape without values.
Never: in the repo, in git remotes, in prompts to AI tools, in tickets, chat, screenshots or docs. The kanban's standing task applies: env files sectioned personal vs client, per provider, local only.
2. The secret inventory (shape, not values)
NEXT_PUBLIC_SUPABASE_* (public by design) · SUPABASE_SERVICE_ROLE_KEY (the most dangerous — server-only, bypasses RLS) · ANTHROPIC_API_KEY · RESEND_API_KEY · STRIPE_SECRET_KEY + STRIPE_WEBHOOK_SECRET + STRIPE_PRICE_* · BIZOS_CRON_SECRET · JAIOS X-API-Key · SSH keys · registrar/DNS · app-store credentials.
3. The register
For each secret, the password manager records: what it unlocks, where it is deployed (VM / local / CI), created date, last rotated, and blast radius if leaked. Monthly (with SOP-06): register matches reality — no keys in providers' dashboards that the register doesn't know.
4. Rotation
| When | What |
|---|---|
| Immediately | On any suspicion of exposure — revoke first (SOP-05 §1), rotate, then investigate |
| Immediately | When any collaborator/contractor with access departs |
| Annually (July) | SUPABASE_SERVICE_ROLE_KEY, Stripe keys, BIZOS_CRON_SECRET, SSH keys — rotate deliberately so rotation is rehearsed, not exotic |
| On provider advice | Provider-initiated rotations, same day |
Rotation order: create new → deploy to VM env → restart → verify → revoke old. Never revoke first outside an incident.
5. Guard rails
- Restricted keys wherever the provider supports scoping (Stripe restricted keys; Supabase publishable vs service split).
- GitHub secret scanning + push protection enabled on all repos.
- Any secret that ever touched a commit — even force-pushed away — is treated as leaked and rotated (history is forever on a public forge).