Patterns
API key rotation strategy
Two active keys, 90-day rotation, dual-validation
An API key that doesn't rotate is a compromised key on borrowed time. Scell.io pattern: each tenant can keep 2 `sk_live_*` keys simultaneously active (current key + rotation key). Rotation workflow every 90 days: (1) generate a new key via `POST /api/v1/api-keys`, (2) deploy the new key in all environments (CI, prod, secret manager), (3) wait 7 days for the new key to be used (verify via `GET /api/v1/api-keys/{id}/usage`), (4) revoke the old one via `DELETE /api/v1/api-keys/{old_id}`. The overlap window prevents outage if a service forgets the rolling. For compromised secrets (incident), bypass the window: revoke immediately and trigger a re-deploy. NEVER share a key between tenants or environments; NEVER hardcode in source; always via env var or secret manager (cf. secret-management pattern).
Key facts
- 2 keys active simultaneously (current + rotation)
- Scheduled rotation every 90 days
- 7-day minimum overlap window
- Verify usage via GET /api/v1/api-keys/{id}/usage before revocation
- Incident: immediate revocation, no window
Code example
// Cron job mensuel — génération clé de rotation
import { ScellClient } from '@scell/sdk';
import { vault } from './vault';
async function rotateScellKey() {
const scell = new ScellClient(await vault.read('scell/current'));
// 1. Generate new key
const newKey = await scell.apiKeys.create({ label: `rotation-${Date.now()}` });
// 2. Push to vault as 'next' (services should fall back to 'next' first)
await vault.write('scell/next', newKey.secret);
// 3. Trigger rolling re-deploy across services
await ci.triggerWorkflow('rolling-deploy', { reason: 'scell-key-rotation' });
// 4. After 7 days (separate cron), promote 'next' to 'current' and revoke old
// scheduleAfter('7 days', async () => {
// await vault.copy('scell/next', 'scell/current');
// await scell.apiKeys.delete(oldKeyId);
// });
}