Secrets & tenancy, by construction
Letting agents operate production is a trust problem before it is an engineering one. So we built the safety architecture first. This is exactly how it works.
Four kinds of secret. Four homes.
The rule of thumb: anything that belongs to a customer lives in the customer's own AWS account. Lemma's control plane holds only its own operational secrets.
| Kind | Example | Where it lives | Who can read it |
|---|---|---|---|
| Control-plane config | our DB password, the model API key, signing keys | AWS Secrets Manager in Lemma's account | control-plane tasks only |
| Control-plane row-level | a user's TOTP seed, an OIDC client secret | Fernet ciphertext in Lemma's Postgres | decrypted in-process |
| Customer infra | RDS master password, Redis auth | Secrets Manager in the customer's account | the customer's own infra |
| Customer app env | the app's DATABASE_URL, STRIPE_KEY | a JSON secret in the customer's account | write-only to Lemma |
Write-only, start to finish
The app's own secrets are detected, set by the customer, and injected into the container without ever touching Lemma's database, logs, or agent transcripts.
Detect
A deploy reads the app's .env.example and declares the variable names it needs. Platform-provided names (DATABASE_URL, PORT) are dropped; Lemma injects those itself.
Wait
The deploy parks on a checklist card until every declared key is set or explicitly skipped. The pipeline visibly waits on you. Nothing ships half-configured.
Set, masked
You enter values in a masked form. They're written into a single JSON secret in your own account's Secrets Manager. Lemma's database records only the key name, who set it, and when. Never the value.
Inject per-key
At container launch each variable is delivered individually from your Secrets Manager via ECS ValueFrom. The value never passes through Lemma's application code.
∎ Because chat transcripts persist, agents only ever see secret names, never values, and the product will never ask for a secret in chat.
Safe by construction
Read-only data queries
When the Data agent queries a customer database, it runs inside the customer's own network as a dedicated read-only role, in a read-only transaction with a statement timeout and row cap. It's off by default; the workspace owner opts in. No write path exists.
Rotatable encryption
Row-level secrets are encrypted with a Fernet key-ring, newest-first. Rotation is zero-downtime: add a key, re-encrypt, drop the old one, and no reads break. Signing keys rotate the same way.
Production fail-fast
A hosted environment refuses to boot on a weak, empty, or placeholder key, for signing or encryption. Un-populated secrets fail loudly instead of silently running a default.
The same architecture gets us compliant
Tenant isolation, an audit trail on every agent action, and identity controls aren't a later retrofit. Most of it is already in the codebase.
- Cross-tenant isolation, enforced at the data layer
- Audit trail on every agent action + approval
- MFA (TOTP) and OIDC single sign-on
- Failed-login lockout + idle session timeout
- Enforced spend caps, so the bill cannot run away
- Owner-gated approvals for every money-moving action
Deep-dive documentation available to design partners on request.