Patterns
Gestion des secrets (AWS Secrets Manager, Vault, Doppler)
Stocker `sk_live_*` hors du code source
Une clé `sk_live_*` Scell.io permet d'émettre des factures fiscalement contraignantes au nom du tenant : sa fuite est un incident P0. Quatre options de stockage acceptables. AWS Secrets Manager : intégration IAM native, rotation automatique, audit CloudTrail, ~0,40$/secret/mois. HashiCorp Vault : self-hosted, dynamic secrets, ACL fines, opéré par l'équipe. Doppler : SaaS dev-friendly, sync automatique vers CI/CD/runtime, gratuit jusqu'à 5 utilisateurs. GCP Secret Manager : intégration native Google Cloud. INTERDIT : `.env` commit dans Git, hardcoded dans le code, dans une variable `NEXT_PUBLIC_*`, dans une image Docker, dans un Helm values.yaml. Pattern obligatoire : (1) clé chargée au boot via SDK secrets, (2) IAM/ACL restreint l'accès aux services qui en ont besoin, (3) rotation tous les 90 jours, (4) audit log de chaque accès. Pour les secrets dérivés chiffrés en DB (S3 access keys, OAuth tokens SuperPDP), prévoir un script reseed idempotent en cas de rotation `ENCRYPTION_KEY` (cf. règle ai-services-mistral).
À retenir
- Options : AWS Secrets Manager, Vault, Doppler, GCP Secret Manager
- INTERDIT : .env en Git, NEXT_PUBLIC_*, hardcoded, Docker image
- Chargement au boot via SDK, IAM/ACL restrictif
- Rotation 90j + audit log d'accès
- Reseed script idempotent pour secrets chiffrés en DB
Exemple de code
// Boot — load Scell key from AWS Secrets Manager
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';
import { ScellClient } from '@scell/sdk';
const secrets = new SecretsManagerClient({ region: 'eu-west-3' });
async function loadScellClient(): Promise<ScellClient> {
const cmd = new GetSecretValueCommand({ SecretId: 'prod/scell/api-key' });
const { SecretString } = await secrets.send(cmd);
if (!SecretString) throw new Error('SCELL_API_KEY missing in Secrets Manager');
// Audit access
logger.info('scell.key.loaded', { source: 'aws-secrets-manager' });
return new ScellClient({ apiKey: JSON.parse(SecretString).api_key });
}
export const scell = await loadScellClient();