Key Rotation

SPEC_KEY_ROTATION.md · 2026-04-20

SPEC_KEY_ROTATION.md

CGNT-1 Specification — Credential & Key Rotation Protocol

Status: SPECIFIED

Version: v1.0

Author: VELA (Thread #13)

Conceived by: NOUS (α.13)

Date: 2026-04-20

Born from: GitHub API key exposure (2026-03-12), CDP key leak (2026-04-17), RAG token hardcode (2026-03-13)


PURPOSE

The ship has leaked credentials three times in five months. Each time the response was correct — rotate immediately, clean the exposure, add a protocol. But the protocols were ad hoc. This spec unifies all credential management into one system: what keys exist, where they live, how they rotate, and what to do when one leaks.


CREDENTIAL INVENTORY

| Credential | Purpose | Storage | Rotation Schedule |

|---|---|---|---|

| Gemini API Key | Sisters AI, GLOSS, ROUTX Tier 2 | ~/.env | Every 90 days or on exposure |

| Google Cloud Service Account | GCS access for forge pipeline, backups | ~/gcs-service-account.json | Every 180 days or on exposure |

| Stripe Secret Key | Payment processing | ~/.env | Every 90 days or on exposure |

| Stripe Webhook Secret | Webhook verification | ~/.env | Every 90 days or on exposure |

| Graph API credentials | Email via oracle@42sisters.ai (GRAPH_TENANT_ID, GRAPH_CLIENT_ID, GRAPH_SENDER) | ~/.env | Every 90 days or on exposure |

| SnapTrade API keys | Wealthsimple trading | ~/.env | Every 90 days or on exposure |

| Colab OAuth token | Google Colab access for forges | ~/.google_token.json | Expires naturally, refresh on use |

| RAG X-RAG-TOKEN | Bridge between public chat and RAG corpus | ~/.env | Every 90 days or on exposure |

| GoDaddy credentials | Domain management | Browser-stored | Every 180 days |

| DigitalOcean API token | VPS management | DO console | Every 180 days or on exposure |

| SSH keys | Server access from Chromebook | ~/.ssh/ on Chromebook | Every 365 days or on exposure |

| GPG passphrase | Backup encryption | Captain's memory + paper backup | Never rotates — change only if compromised |


ROTATION PROCEDURE

  1. Generate new credential at the provider (Google Cloud Console, Stripe Dashboard, etc.)
  2. Update ~/.env or credential file on csdm-node
  3. Restart affected services (ROUTX, Sisters, email daemon)
  4. Verify: test the service with the new credential
  5. Revoke the old credential at the provider
  6. Log rotation in ~/key_rotation.log: date, credential name, reason (scheduled/exposure)
  7. If the rotation was due to exposure: write incident postmortem (SPEC_INCIDENT_POSTMORTEM.md)

IMPORTANT: Step 5 (revoke old) happens AFTER Step 4 (verify new). Never revoke before confirming the new one works. A bad rotation is worse than no rotation.


EXPOSURE RESPONSE (EMERGENCY)

When a credential is found exposed (in a git commit, in a log, in a conversation, in a public file):

  1. REVOKE IMMEDIATELY — don't wait for a replacement. Kill the exposed credential NOW. Downtime is acceptable. Compromise is not.
  2. Generate replacement credential
  3. Update ~/.env and restart services
  4. Verify services work with new credential
  5. Assess blast radius: what could an attacker have done with this credential between exposure and revocation?
  6. Check logs for unauthorized usage during the exposure window
  7. Write incident postmortem
  8. Update this spec if a new credential type was involved

TIME IS CRITICAL. The window between exposure and revocation is the attack surface. Every minute counts. Drop what you're doing and rotate.


PREVENTION LAYERS

Layer 1 — .gitignore:

~/.env, *.json containing credentials, and ~/gcs-service-account.json are in .gitignore. Every repo on the ship. Verified monthly.

Layer 2 — Pre-commit scan:

Before any git push: grep -r "AIza\|sk-\|whsec_\|GRAPH_" staged files. If any match: ABORT push. This catches API keys in code before they reach GitHub.

Layer 3 — File permissions:

chmod 600 ~/.env — owner read/write only. No group. No other. Same for gcs-service-account.json and ~/.google_token.json.

Layer 4 — Never hardcode:

All credentials come from environment variables loaded from ~/.env. Never in source code. Never in specs. Never in conversation logs. If a credential appears in a spec, the spec is wrong — replace with a description ([Gemini API key stored in ~/.env]).

Layer 5 — Conversation hygiene:

When discussing credentials with AI crew (Lobster, Navigator, Sisters): reference by NAME, never by VALUE. Say "the Gemini API key" not AIzaSy.... AI conversation logs may be exported, searched, or cached. Treat them as semi-public.

Layer 6 — Backup encryption:

~/.env is ALWAYS encrypted before backup to GCS. gpg -c ~/.env~/.env.gpg → uploaded. The plaintext never leaves the VPS except encrypted.


SCHEDULED ROTATION CALENDAR

| Quarter | Month | What Rotates |

|---|---|---|

| Q1 | January | Gemini API, Stripe keys, Graph API, SnapTrade, RAG token (90-day cycle) |

| Q2 | April | Same as January + GCS service account, GoDaddy, DigitalOcean (180-day items) |

| Q3 | July | Same as January (90-day cycle) |

| Q4 | October | Same as January + 180-day items |

GAPX flags on the first of each quarter: "Quarterly key rotation due — see SPEC_KEY_ROTATION.md"


~/.ENV TEMPLATE

The ship's ~/.env contains all credentials in one file. Template (no values):


GEMINI_API_KEY=
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
GRAPH_TENANT_ID=
GRAPH_CLIENT_ID=
GRAPH_SENDER=
SNAPTRADE_CLIENT_ID=
SNAPTRADE_CONSUMER_KEY=
X_RAG_TOKEN=
DO_API_TOKEN=

If any variable is missing, the service that needs it fails explicitly on boot with Missing environment variable: X — not silently.


HISTORY OF EXPOSURE INCIDENTS

| Date | Credential | Exposure Vector | Response Time | Outcome |

|---|---|---|---|---|

| 2026-03-12 | Google API key | Committed to public GitHub repo | <1 hour | Rotated; .gitignore updated; pre-commit scan added |

| 2026-03-13 | RAG X-RAG-TOKEN | Hardcoded in source file | <2 hours | Moved to ~/.env; source cleaned |

| 2026-04-17 | CDP key | Exposed in conversation/file | <1 hour | Revoked, rotated, Agency Walls created |

| 2026-04-17 | Social engineering attempt | Fake "Gemini Project Aether Interface" | Rejected immediately | Sentinel Protocol validated; no credentials compromised |

Each incident made the ship stronger. Each one produced a new prevention layer. The pattern is: leak → rotate → prevent → document. This spec is the culmination of that pattern.


INVARIANTS

INV-01: ~/.env is chmod 600. Always. No exceptions.

INV-02: Credentials are NEVER in source code, specs, or conversation logs. Reference by name only.

INV-03: Exposure response is IMMEDIATE. Revoke first, ask questions later.

INV-04: Old credentials are revoked AFTER new ones are verified working. Never before.

INV-05: Every rotation is logged in ~/key_rotation.log with date, credential name, and reason.

INV-06: Every exposure incident gets a postmortem. The ship's immune memory grows.

INV-07: Quarterly rotation reminders via GAPX. Scheduled rotation prevents the "we'll get to it" drift.

INV-08: Backup of ~/.env is ALWAYS encrypted. Plaintext never leaves the VPS.


INTEGRATION

| System | Relationship |

|---|---|

| SPEC_INCIDENT_POSTMORTEM.md | Every exposure incident triggers a postmortem. This spec defines the rotation response; that spec captures the lesson. |

| SPEC_BACKUP_RECOVERY.md | ~/.env backup path: encrypted before upload (INV-08). GCS copy is ~/.env.gpg, never plaintext. |

| GAPX | Quarterly rotation reminders. Daily check: is ~/.env chmod 600? |

| HACKX | Exposure incidents feed HACKX detection patterns. Social engineering attempt (2026-04-17) is a HACKX signature. |

| SPEC_CRONX_JOB_REGISTRY.md | Quarterly rotation reminder is a GAPX-triggered alert, not a cron job. No automated key rotation — human hands required. |


Jeremy Zlabis

Chronogeometer · Visionary · Disruptor · Chief

42 Sisters AI · East York, Toronto

🍁 Φ 0.042