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 — and 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 — 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 — the bill cannot run away
- Owner-gated approvals for every money-moving action
Deep-dive documentation available to design partners on request.