Production security baseline
A secure production setup is not one feature switch. It is a set of boundaries that must agree: who can call a handler, which tenant a request belongs to, which fields are personal, where keys live, and how an operator recovers from a bad deploy.
Use this page as a launch gate. It explains the order and the evidence to collect; the linked guides and recipes contain the implementation details. A passing test suite is necessary, but it does not replace a staging drill or a backup restore.
Choose the security profile
Section titled “Choose the security profile”Start by writing down which rows apply to your app. A single app can need all four profiles.
| Profile | Minimum additional controls |
|---|---|
| Authenticated internal app | Revocable sessions, handler access rules, audit, rate limits, backups |
| Multi-tenant SaaS | Tenant-scoped handlers, membership roles, ownership rules, cross-tenant operator boundaries |
| Public web surface | Authoritative tenant resolver, explicit anonymous access, field filtering, IP-based limits on public writes |
| Personal or regulated data | PII annotations, subject-key KMS, blind indexes where needed, GDPR export/erasure, key recovery procedure |
If you cannot name the tenant boundary, the public entry points, the privileged roles, and the data subjects before writing code, stop here and resolve those design questions first. Read Multi-tenancy, Auth and permissions, and Crypto-shredding.
Before you start
Section titled “Before you start”Complete this checklist with an application owner and an operations owner:
- a staging deployment that uses the same image and environment shape as production
- a test user for each relevant role, including a restricted tenant member and a system operator
- a secret manager and a documented break-glass procedure
- a tested Postgres, Redis, subject-key, and file-storage backup/restore path
- a rollback image tag and a known database migration boundary
- a list of public routes and the trusted input that resolves their tenant
- a data inventory: personal fields, owners, retention periods, and external processors
Keep the evidence with the release record. Never put credentials, plaintext keys, PATs, MFA recovery codes, or copied production payloads into that record.
1. Mount the account and security baseline
Section titled “1. Mount the account and security baseline”Start with the framework’s preset rather than hand-picking only the features that happen to be visible in the first screen.
- Security baseline recipe shows the
securityBaselineFeatures()composition and its production boot warning. - The preset covers sessions, crypto-shredding, rate-limiting, and audit. Your auth setup still needs the appropriate user, tenant, and email/password path.
- Sessions provide server-side revocation; a signed JWT alone is not a logout or force-logout mechanism.
Launch gate
Section titled “Launch gate”Run the app’s composition/boot check and then the production-like boot path. There must be no missing-security-baseline warning and no plaintext-PII fallback accepted as a production decision.
bun run bootbun run typecheckbun testThe exact test command can differ between app repositories. Keep the focused security-baseline and authentication integration tests in the release record.
2. Prove authorization, not just authentication
Section titled “2. Prove authorization, not just authentication”Authentication answers who is calling. Authorization answers what that identity may do.
- Declare handler access explicitly. Handlers are default-deny.
- Use field-level access for sensitive columns; do not hide fields with ad-hoc branches in a handler.
- Use entity ownership rules when users may see only their own rows.
- Keep
SystemAdminoperations in system-scoped features and require an access rule on every exposed handler. - Use Personal Access Tokens only for headless clients, with the smallest domain and read/write scope they need.
The Field-level access recipe and auth concept provide the concrete contracts.
Launch gate
Section titled “Launch gate”Test each boundary with a negative case:
- a regular tenant member cannot call an admin handler
- a caller without field-read access does not receive the field
- a caller without field-write access cannot submit the field
- a user from tenant A cannot read or mutate tenant B’s rows
- a PAT cannot call a handler outside its configured scopes
- a role downgrade or membership removal takes effect on the next PAT request
A UI hiding a button is not evidence. The HTTP/dispatcher request must be denied as well.
3. Close the tenant and public-surface boundary
Section titled “3. Close the tenant and public-surface boundary”Every request needs a trustworthy tenant context.
- Normal handlers use tenant-scoped
ctx.db; do not import another feature’s table or construct a second unscoped database path. - Cross-tenant work is isolated in a system-scoped feature with explicit roles and an audit reason.
- Public handlers use
roles: ["anonymous"], notopenToAll. - An anonymous request gets its tenant from an authoritative resolver such as the host/subdomain. The client must not submit the tenant id as authority.
- Anonymous writes need validation and an IP-based handler limit. The anonymous access guide and anonymous multitenant recipe show the complete boundary.
Launch gate
Section titled “Launch gate”Test a public route from two tenant hosts, then try an unknown host, a forged
tenant header, and a public write above its rate limit. The expected outcomes
must be tenant isolation, a safe resolver error, tenant-mismatch rejection, and
429 respectively. Do not ship a public resolver that trusts an unvalidated
client header.
4. Strengthen interactive login
Section titled “4. Strengthen interactive login”Add MFA after the ordinary login path works, and roll it out in stages:
- Mount TOTP-based MFA next to email/password auth.
- Store JWT, setup-token, and challenge-token secrets in the deployment secret store. Keep the setup and challenge purposes separate.
- Enroll administrators and support staff first.
- Verify normal login, challenge login, recovery-code login, disable, and session revocation in staging.
- Set tenant-wide
adminsorallenforcement only after the affected users have enrolled.
The Auth MFA recipe is the raw wiring contract; the guide is the rollout procedure. Recovery codes are shown once and must be handled like credentials.
Launch gate
Section titled “Launch gate”Record a successful MFA enrollment and login for each enforcement group, plus a negative login for an unenrolled user who should be blocked. Verify that enabling, disabling, or regenerating MFA invalidates the other live sessions as intended.
5. Protect personal data and keys
Section titled “5. Protect personal data and keys”Treat event history as durable. If personal data enters an event, it needs a durable erasure story before launch.
- Annotate personal fields with the correct subject, tenant, or ownership relationship. Deliberately non-personal fields need an explicit reason.
- Provision the subject-key KMS and the separate blind-index key before using lookupable encrypted fields.
- Keep the secrets master key separate from the platform KEK and blind-index key.
- Use a persistent file provider for GDPR exports; an in-memory provider loses export artifacts on restart.
- Verify that API responses, search indexes, exports, and mail do not expose ciphertext after the subject key is erased.
- Keep the key-manager migration and key-rotation guide as separate change procedures. Migration preserves values; rotation changes key material.
The Crypto-shredding recipe proves
subject-key erasure, user-owned data, blind indexes, and the [[erased]]
sentinel.
Launch gate
Section titled “Launch gate”Before traffic:
- Write and read a record created before the current release.
- Perform one controlled subject-key erase in staging.
- Verify detail, list, event replay/rebuild, search, and export behavior.
- Run an equality lookup for every lookupable encrypted field.
- Restore a backup into an isolated environment and repeat the read probe.
Stop if a production boot would fall back to plaintext or if a rebuild process lacks the blind-index key.
6. Deploy and operate the secure path
Section titled “6. Deploy and operate the secure path”The security boundary continues into the deployment system:
- inject secrets from the deployment secret store; never commit or echo them
- set
TZ=UTCfor the server process - build an immutable image and retain the previous image for rollback
- apply the checked-in schema before starting the new app
- run both liveness and readiness checks
- start the worker lane when the topology requires async projections or jobs
- configure persistent Postgres, Redis, file, and subject-key backups
- test a restore and record the result, not just that a backup exists
Choose the deployment topology that matches the operational owner:
- Docker for a reproducible image and a pre-deploy schema step
- Solo for one VM with a small operational surface
- K3s for multi-replica production, workers, HA Postgres, backups, and infrastructure-as-code
Launch gate
Section titled “Launch gate”Run the image’s schema-apply step against a staging database, boot the image,
exercise /health and /health/ready, then roll back to the previous image tag.
Confirm that the database boundary is understood before deciding whether a code
rollback is safe after a migration.
Release evidence checklist
Section titled “Release evidence checklist”Attach these results to the release or change record:
- feature graph and production boot validation passed
- handler, field, row, tenant, and PAT negative-access tests passed
- public resolver and anonymous rate-limit tests passed
- MFA enrollment, challenge, recovery, and rollout checks passed
- old encrypted data read successfully with the active KMS
- controlled erasure, export, search, and rebuild checks passed
- schema apply, health, readiness, and worker checks passed
- backup restore and image rollback were rehearsed
- no secrets, tokens, recovery codes, or plaintext PII entered logs or Git
A checklist item without an owner, timestamp, and evidence is not complete.
What this page does not replace
Section titled “What this page does not replace”This page is a security route, not a generic compliance certification and not a provider-specific runbook. It does not decide your retention period, legal role, DPA coverage, threat model, or incident response obligations. Use it to find the Kumiko control and the test that proves it, then complete the deployment-specific and legal work for your environment.