Skip to content

Secret Rotation

Rotation Schedule

SecretRotation FrequencyImpact of Rotation
JWT_SECRETEvery 90 daysAll active sessions invalidated (or none with dual-secret)
APP_ENCRYPTION_KEYAnnuallyRequires data re-encryption migration
MFA_ENCRYPTION_KEYAnnuallyRequires re-encryption migration; users locked out if skipped
ENROLLMENT_KEY_PEPPERAnnuallyAll unexpired enrollment keys invalidated
MFA_RECOVERY_CODE_PEPPERAnnuallyAll existing MFA recovery codes invalidated
AGENT_ENROLLMENT_SECRETEvery 90 daysOnly affects new enrollments
SESSION_SECRETEvery 90 daysAll active sessions invalidated
POSTGRES_PASSWORDEvery 90 daysRequires coordinated DB + app restart
REDIS_URL credentialsEvery 90 daysBrief reconnection
S3_ACCESS_KEY / S3_SECRET_KEYEvery 90 daysNone
CLOUDFLARE_API_TOKENEvery 90 daysNone (existing certs unaffected)
TURN_SECRETEvery 90 daysActive remote sessions may drop
METRICS_SCRAPE_TOKENEvery 180 daysBrief gap in metrics collection
RESEND_API_KEY / SMTP passwordPer provider policyBrief notification gap
Twilio credentialsPer provider policyBrief SMS notification gap
ANTHROPIC_API_KEYPer provider policyAI assistant unavailable
User API keysUser responsibilityImmediate

Rotating JWT Secret

  1. Generate a new secret:

    Terminal window
    openssl rand -base64 64
  2. Update .env.prod with the new JWT_SECRET

  3. Restart the API:

    Terminal window
    docker compose -f docker/docker-compose.prod.yml restart api
  4. All existing JWTs become invalid — users will need to log in again

Rotating App Encryption Key

  1. Generate a new key:

    Terminal window
    openssl rand -hex 32
  2. Record the old key securely — you need it for the re-encryption migration

  3. Run the re-encryption migration:

    Terminal window
    OLD_ENCRYPTION_KEY=<old-key> \
    APP_ENCRYPTION_KEY=<new-key> \
    npx tsx scripts/re-encrypt-secrets.ts
  4. Verify decryption of several records with the new key

  5. Update APP_ENCRYPTION_KEY in .env.prod to the new key

  6. Restart the API:

    Terminal window
    docker compose -f docker/docker-compose.prod.yml restart api

Rotating MFA Encryption Key

  1. Generate a new key:

    Terminal window
    openssl rand -hex 32
  2. Run a re-encryption migration targeting MFA secret columns (same approach as APP_ENCRYPTION_KEY)

  3. Update MFA_ENCRYPTION_KEY in .env.prod

  4. Restart the API:

    Terminal window
    docker compose -f docker/docker-compose.prod.yml restart api

Rotating Enrollment Key Pepper

  1. Generate a new pepper:

    Terminal window
    openssl rand -hex 32
  2. Update ENROLLMENT_KEY_PEPPER in .env.prod

  3. Restart the API:

    Terminal window
    docker compose -f docker/docker-compose.prod.yml restart api
  4. Regenerate enrollment keys and redistribute to anyone deploying new agents

Rotating MFA Recovery Code Pepper

  1. Generate a new pepper:

    Terminal window
    openssl rand -hex 32
  2. Update MFA_RECOVERY_CODE_PEPPER in .env.prod

  3. Restart the API:

    Terminal window
    docker compose -f docker/docker-compose.prod.yml restart api
  4. Notify users to regenerate their MFA recovery codes via Settings, or trigger a bulk regeneration as an admin

Rotating Agent Enrollment Secret

  1. Generate a new secret:

    Terminal window
    openssl rand -hex 32
  2. Update .env.prod with the new AGENT_ENROLLMENT_SECRET

  3. Restart the API

  4. Update any deployment scripts or MDM policies with the new secret

Rotating Session Secret

  1. Generate a new secret:

    Terminal window
    openssl rand -base64 64
  2. Update SESSION_SECRET in .env.prod

  3. Restart the API:

    Terminal window
    docker compose -f docker/docker-compose.prod.yml restart api
  4. All active sessions are invalidated — users must log in again

Rotating Database Password

  1. Generate a new password:

    Terminal window
    openssl rand -base64 24 | tr -d '/+='
  2. Update the password in PostgreSQL:

    Terminal window
    docker compose -f docker/docker-compose.prod.yml exec postgres \
    psql -U breeze -c "ALTER USER breeze PASSWORD 'new-password';"
  3. Update POSTGRES_PASSWORD and DATABASE_URL in .env.prod

  4. Restart the API:

    Terminal window
    docker compose -f docker/docker-compose.prod.yml restart api

Rotating Redis Credentials

  1. Set or update the Redis password:

    Terminal window
    redis-cli CONFIG SET requirepass "new-redis-password"
    redis-cli -a "new-redis-password" CONFIG REWRITE
  2. Update REDIS_URL in .env.prod:

    REDIS_URL=redis://:new-redis-password@localhost:6379
  3. Restart the API and worker processes:

    Terminal window
    docker compose -f docker/docker-compose.prod.yml restart api worker

Rotating S3 / Object Storage Credentials

  1. Create a new access key pair in your S3/R2/MinIO console

  2. Update S3_ACCESS_KEY and S3_SECRET_KEY in .env.prod

  3. Restart the API:

    Terminal window
    docker compose -f docker/docker-compose.prod.yml restart api
  4. Verify by uploading and downloading a test file

  5. Delete the old access key in your storage provider’s console

Rotating Cloudflare API Token

  1. In the Cloudflare dashboard, go to My Profile > API Tokens

  2. Create a new token with the same permissions (Client Certificates: Edit for the relevant zone)

  3. Update CLOUDFLARE_API_TOKEN in .env.prod

  4. Restart the API:

    Terminal window
    docker compose -f docker/docker-compose.prod.yml restart api
  5. Verify by triggering a certificate enrollment or renewal

  6. Delete the old token in the Cloudflare dashboard

Rotating TURN Secret

  1. Generate a new secret:

    Terminal window
    openssl rand -hex 32
  2. Update TURN_SECRET in both the TURN server configuration and .env.prod

  3. Restart the TURN server and the API:

    Terminal window
    docker compose -f docker/docker-compose.prod.yml restart api turn

Rotating Metrics Token

  1. Generate a new token:

    Terminal window
    openssl rand -hex 32
  2. Update METRICS_SCRAPE_TOKEN in .env.prod

  3. Update the token file:

    Terminal window
    echo "new-token" > monitoring/secrets/metrics_scrape_token
    chmod 600 monitoring/secrets/metrics_scrape_token
  4. Restart API and Prometheus:

    Terminal window
    docker compose -f docker/docker-compose.prod.yml restart api prometheus

Rotating Email Provider Credentials

Covers RESEND_API_KEY, SMTP_USER / SMTP_PASS, and MAILGUN_API_KEY.

  1. Rotate the credential in the provider’s dashboard (Resend, Mailgun, or your SMTP provider)

  2. Update the corresponding env var(s) in .env.prod

  3. Restart the API:

    Terminal window
    docker compose -f docker/docker-compose.prod.yml restart api
  4. Verify by triggering a test notification (e.g., password reset email)

Rotating SMS Provider Credentials (Twilio)

Covers TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN.

  1. In the Twilio console, generate a new Auth Token (or create a new API key pair)

  2. Update TWILIO_ACCOUNT_SID and/or TWILIO_AUTH_TOKEN in .env.prod

  3. Restart the API:

    Terminal window
    docker compose -f docker/docker-compose.prod.yml restart api
  4. Verify by triggering a test SMS alert

  5. Revoke the old credential in the Twilio console

Rotating Anthropic API Key

  1. Generate a new API key in the Anthropic console

  2. Update ANTHROPIC_API_KEY in .env.prod

  3. Restart the API:

    Terminal window
    docker compose -f docker/docker-compose.prod.yml restart api
  4. Revoke the old key in the Anthropic console

User API Keys

User-facing API keys are managed through the application, not through environment variables.

  • Individual rotation: Users regenerate their API key via Settings > API Keys > Regenerate, or via POST /api/v1/api-keys
  • Admin revocation: Admins can revoke any API key via the admin panel or DELETE /api/v1/api-keys/:id
  • Bulk revocation: In a security incident, an admin can revoke all API keys; users must generate new ones

No environment variable changes or restarts are required. The old key is immediately invalidated upon regeneration.

Emergency Rotation

If you suspect any secret has been compromised:

  1. Rotate the compromised secret immediately using the procedures above
  2. Check audit logs for unauthorized access during the exposure window
  3. Rotate related secrets — if JWT_SECRET was compromised, also rotate SESSION_SECRET; if database credentials leaked, also rotate APP_ENCRYPTION_KEY in case encrypted data was exfiltrated
  4. Notify affected users if their data may have been accessed
  5. File a post-incident report documenting the timeline, impact, and remediation