Skip to Content
Living documentation — last reviewed 2026-05-28
RunbooksCredentials rotation

Credentials rotation

Secrets are stored in Railway/Vercel project env. Some are load-bearing in ways that make naive rotation destructive — read the table before rotating anything.

Quick reference

SecretWhere it livesSafe to rotate?Notes
DATABASE_URL, REDIS_URLRailwayYesRoll API/web after change.
CLERK_SECRET_KEY, CLERK_PUBLISHABLE_KEY, NEXT_PUBLIC_CLERK_PUBLISHABLE_KEYRailway + VercelYes (per-environment)Must be a matched pair from the same Clerk instance.
CLERK_WEBHOOK_SECRETRailwayYesUpdate Clerk Dashboard → Webhooks first, then deploy. Brief window where webhooks fail signature check.
PAYMENT_CREDENTIALS_ENCRYPTION_KEYRailwayDestructive without migrationStored payment provider credentials are AES-256-GCM encrypted with this key. Rotating it makes existing rows unreadable. See payments rotation below.
NATIONAL_ID_ENCRYPTION_KEYRailwayDestructive without migrationSame as above, for Israeli national IDs (Teudat Zehut). Used by leads/members.
INVITE_SECRETRailwayYes, but invalidates outstanding invitesMembership invitation tokens are signed with this. Rotating immediately breaks any unredeemed invites.
QR_CHECKIN_SECRETRailwayYes, but invalidates issued QR codesHMAC-signed check-in QR codes. Outstanding codes stop scanning.
Cardcom / Morning / Rivhit / iCreditRailwayYes (per provider)The relevant org-level credentials are also stored encrypted in payment_provider_configs — those use the payment encryption key above.
PLATFORM_BILLING_*RailwayYesFitKit’s own Cardcom credentials for B2B charging.
R2_*RailwayYesRotate via Cloudflare R2 → Manage API tokens.
VERCEL_TOKENRailwayYesRequired for minisite domain provisioning.
RAILWAY_API_TOKEN, NEON_API_KEY, POSTHOG_API_KEY, VOYAGE_API_KEY, ANTHROPIC_API_KEYRailwayYesSide-channel services. Rotate at provider, paste into Railway.
SENTRY_DSN, NEXT_PUBLIC_SENTRY_DSN, SENTRY_AUTH_TOKENRailway / VercelYesAuth token used at build time for source-map upload.
LINEAR_EMAILRailwayYesJust an email address; not a secret per se.
RESEND_API_KEY, RESEND_FROM_ADDRESSRailwayYesUpdate DKIM/SPF if changing sending domain.

Payment credentials encryption key

Both PAYMENT_CREDENTIALS_ENCRYPTION_KEY and NATIONAL_ID_ENCRYPTION_KEY are AES-256-GCM keys used to encrypt fields at rest in Postgres. Rotating either key naively makes existing data unreadable. Do not change them in production without running a re-encryption migration.

Why they exist

  • Payment provider credentials are stored encrypted in payment_provider_configs.encrypted_* columns (Cardcom terminal numbers, API keys, etc., per org).
  • National IDs are stored encrypted in members / leads to comply with Israeli data protection.

Rotation procedure (when you actually need to)

  1. Plan a maintenance window. Payment provider config writes must be paused for the duration.
  2. Stand up a re-encrypt script (scripts/reencrypt-<field>.ts) that:
    • Loads each row with the old key.
    • Re-encrypts with the new key.
    • Writes back.
    • Reads back and verifies the new key decrypts the row.
  3. Deploy a code change that accepts BOTH keys for decryption (old or new), but writes with the new key.
  4. Run the migration script.
  5. After 100% of rows are confirmed re-encrypted, deploy a follow-up code change that removes the old key.
  6. Remove the old key from Railway env.

This isn’t yet implemented as a real procedure — the script and dual-key support need to be written before the first rotation. If you’re rotating in an emergency (key compromise), the only safe immediate action is to invalidate all stored credentials (force orgs to re-enter their payment provider credentials) and treat outstanding signed payment provider sessions as compromised.

What we’d need to change to make routine rotation easy

  • Store a key_id per row so multiple keys can coexist.
  • Background re-encrypt worker that respects a target key_id.
  • This work is not currently tracked in Linear. Add a ticket if rotation becomes a regular operational need.

Token signing secrets (INVITE_SECRET, QR_CHECKIN_SECRET)

These sign tokens with a finite lifetime (invites: ~7 days; QR check-in: short-lived). Rotating immediately invalidates any token in flight.

Mitigations:

  • Communicate ahead of time if you have a known invite window (e.g., a launch event).
  • Make the rotation a no-op for already-redeemed invites (they’re stored decoded after redemption).
  • For QR check-in, accept brief check-in failures; users re-fetch a QR and continue.

Auditing key usage

There is no per-row audit of “which key encrypted this row” today. Track which key version is active in Railway env history (Railway keeps a change log per project). Until audit logging (FIT-20) lands, key rotation must be manual-tracked by the operator.