KMS and secrets
Per-merchant credentials are encrypted at rest with AWS KMS. Understand the model, the local-dev bypass, and the operational failure modes.
Breeze Buddy stores per-merchant telephony and STT/TTS credentials in Postgres. Each credential value is encrypted with AWS KMS before insertion and decrypted on read. The app-level credentials in .env (Azure OpenAI, Daily, default ElevenLabs) are not KMS-wrapped — those are read directly from the process environment.
Developers: this is automatic
Per-merchant credential isolation and KMS wrapping are part of Breeze Buddy’s managed infra — as a developer you just reference credentials by ID in your templates (or let the platform resolve them from the lead’s reseller/merchant scope) and the runtime decrypts on the fly. This page is for operators configuring the multi-tenant vault.
Two classes of secret
| Class | Example | Where it lives | Encryption |
|---|---|---|---|
| App-level | AZURE_OPENAI_API_KEY, DAILY_API_KEY, JWT_SECRET_KEY | Process env | Not wrapped — your secrets manager is responsible. |
| Per-merchant | A merchant’s Twilio account SID, a merchant’s Sarvam key | Postgres, encrypted | KMS-wrapped on write; decrypted on read. |
The per-merchant wrapping is what makes multi-tenant key isolation cheap. Rotating a merchant’s Twilio key is a single DB update; it has no effect on any other merchant.
The decrypt path
On every request that needs a merchant credential:
- Load the encrypted blob from Postgres.
- Call KMS
DecryptusingAWS_REGION+ the configured access key. - Cache the plaintext in memory for the duration of the call.
- Use the plaintext to call the merchant’s upstream provider.
The cache is per-process, never written to disk, and evicted when the call ends.
Required env
| Var | Purpose |
|---|---|
AWS_REGION | Region your KMS key lives in. |
AWS_ACCESS_KEY_ID | Access key with kms:Decrypt permission on the merchant key. |
AWS_SECRET_ACCESS_KEY | Matching secret. |
SKIP_KMS_DECRYPT | false in production. true only for local dev (see below). |
The local-dev bypass
Setting SKIP_KMS_DECRYPT=true tells Breeze Buddy to treat the value in Postgres as plaintext — no KMS call. This lets you insert a merchant’s Twilio SID directly during local development without AWS creds.
Never in production
SKIP_KMS_DECRYPT=true disables encryption-at-rest for per-merchant credentials. Production secrets leak to disk at insertion time. Enforce at the secrets-manager / deployment config layer that this var is never true in production.
Failure modes
- KMS region mismatch — decrypt fails with a region error. Verify
AWS_REGIONmatches the key’s region. - IAM permission missing — decrypt fails with
AccessDenied. Grantkms:Decrypton the merchant key’s ARN. - KMS rate limits — at high call volume, you may hit per-second decrypt quotas. Monitor
ThrottlingExceptionin logs and request a quota increase. - Credential rotation — rotating the app-level AWS creds without propagation means all calls using per-merchant credentials fail. Stage rotations carefully.
- Key deletion / disable — disabling the KMS key immediately breaks all merchant operations. Do not disable keys as part of routine cleanup.
JWT signing keys
JWT_SECRET_KEY signs user access tokens. It is app-level, not per-merchant. Rotating it invalidates every outstanding access token — users and integrations must re-auth. Document the rotation procedure in your incident-response runbook before you need it.