Skip to main content

Patterns

Secret management (AWS Secrets Manager, Vault, Doppler)

Storing `sk_live_*` outside source code

A Scell.io `sk_live_*` key allows issuing fiscally binding invoices in the tenant's name: leakage is a P0 incident. Four acceptable storage options. AWS Secrets Manager: native IAM integration, automatic rotation, CloudTrail audit, ~$0.40/secret/month. HashiCorp Vault: self-hosted, dynamic secrets, fine-grained ACL, team-operated. Doppler: dev-friendly SaaS, automatic sync to CI/CD/runtime, free up to 5 users. GCP Secret Manager: native Google Cloud integration. FORBIDDEN: `.env` committed to Git, hardcoded in code, in a `NEXT_PUBLIC_*` variable, in a Docker image, in a Helm values.yaml. Mandatory pattern: (1) key loaded at boot via secrets SDK, (2) IAM/ACL restricts access to services that need it, (3) rotation every 90 days, (4) audit log of each access. For DB-stored encrypted derived secrets (S3 access keys, SuperPDP OAuth tokens), provide an idempotent reseed script in case of `ENCRYPTION_KEY` rotation (cf. ai-services-mistral rule).

Key facts

  • Options: AWS Secrets Manager, Vault, Doppler, GCP Secret Manager
  • FORBIDDEN: .env in Git, NEXT_PUBLIC_*, hardcoded, Docker image
  • Boot-time loading via SDK, restrictive IAM/ACL
  • 90-day rotation + access audit log
  • Idempotent reseed script for DB-encrypted secrets

Code example

// 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();

See also

Your cookie preferences

We use cookies to improve your experience. Essential cookies are always active. Cookie policy.