9.1 KiB
Production Security Runbook
This document is the production security entrypoint for the web, API, worker, and operations surfaces. It links the detailed header and CORS checklist in docs/security/runbook.md and summarizes the security boundaries operators must preserve while triaging incidents or changing runtime configuration.
Security surfaces
| Surface | Boundary | Safe evidence to record |
|---|---|---|
| Web runtime | React Router responses, redirects, and static assets must carry the documented security headers without changing cache or redirect behavior. | Header names, expected policy values, response status, and generic route class. |
| API runtime | Hono API responses must keep request IDs, API security headers, and CORS decisions aligned with the API contract. | Method, path, status, stable error code, and x-request-id. |
| Worker runtime | Queue processors must use explicit configuration and report health without exposing provider secrets. | Worker enablement, queue name, job status/counts, and redacted dependency codes. |
| Providers | Mailjet, Redis, PostgreSQL, browser runtime, and deployment providers are secret-backed integrations. | Environment key names, readiness state, provider category, and correlation IDs. |
Headers and browser protections
Use docs/security/runbook.md as the detailed source of truth for required web and API headers. The expected header family includes x-content-type-options, referrer-policy, x-frame-options, permissions-policy, and explicit cross-origin policies. Header checks must confirm that static asset cache behavior, redirects, x-request-id, and CORS responses remain intact.
Do not weaken these protections to resolve an embedding, preview, or browser-origin issue. If a policy appears to block a legitimate client, capture the response status, affected header name, generic origin label, and request ID, then update the narrow runtime contract and this documentation together.
CORS allow and deny behavior
CORS is the browser-origin access boundary for the API. Allowed origins come from the canonical app and admin origins plus explicit WEB_ORIGIN and ADMIN_ORIGIN values when configured as full origins with scheme and host. Allowed responses should emit access-control-allow-origin matching the requested allowed origin, and denied or malformed origins must not emit that header.
Positive CORS checks alone are not sufficient. Incident triage should compare an expected allowed origin with a denied origin such as the generic attacker origin already documented in the detailed runbook, while recording only origin labels, header presence, status, and x-request-id. Never paste customer domains, tenant hostnames, raw browser headers, cookies, authorization headers, or environment dumps into tickets or logs.
Authentication and API tokens
API_ACCESS_TOKEN is the service-token boundary for authenticated API calls. API clients may present credentials through bearer authorization or documented API-key validation inputs, but diagnostics must never echo token values, token fragments, request bodies that contain credentials, cookies, or raw authorization headers.
Safe support notes include the endpoint, method, status, stable error code, and request ID. If an authentication incident requires comparison between clients, use labels such as service-token configured, missing token, or invalid token format; do not record the submitted credential or a reversible hash of it.
Environment secrets and redaction
Runtime configuration follows the redaction rules in docs/configuration.md. Secret-like settings include database URLs, Redis URLs, Supabase keys, Mailjet keys, browser runtime credentials, deployment webhooks, registry credentials, API tokens, cookies, passwords, and future provider credentials.
Errors, logs, health JSON, readiness output, documentation examples, and incident tickets may include environment key names, parse status, diagnostic codes, provider category, queue readiness, and worker enablement. They must not include raw secret values, full environment dumps, stack traces containing credentials, tenant identifiers, customer data, provider payloads, or secret-shaped examples.
Mailjet webhook and mail boundaries
Mailjet is the transactional mail boundary for outbound email and webhook verification. MAILJET_API_KEY and MAILJET_FROM_EMAIL must be configured together when Mailjet delivery is enabled. Webhook handling must verify signing before trusting provider events and must treat replay or tamper symptoms as security incidents.
When investigating mail failures, record the provider category, event type label, delivery status, request ID or job ID, and whether signature verification passed. Do not paste provider payloads, email addresses from customers, message bodies, webhook signatures, signing secrets, API keys, or provider dashboard screenshots containing personal data.
Database, Redis, and queue secrets
DATABASE_URL, BULLMQ_REDIS_URL, and Redis fallback configuration are operational secrets. Application and worker diagnostics may report database readiness, Redis parse state, queue names, failed-job counts, and redacted dependency codes. They must not print connection strings, passwords, usernames, hostnames tied to tenants, database names, Redis URLs, or raw job payloads.
Queue incident notes should use request IDs, job IDs, queue names, retry counts, and safe failure classifications. If a replay is required, perform it through approved operational tooling after confirming payload sensitivity; do not copy payload JSON into documentation or shared chat.
Request-id incident correlation
x-request-id is the preferred security-triage correlation key for API incidents, and generated request IDs or documented job IDs should be used for worker incidents. Operators may record request IDs, job IDs, status codes, stable error codes, dependency diagnostic codes, and generic surface labels.
Do not use request IDs as a reason to collect raw payloads. If deeper investigation is necessary, keep sensitive material inside the approved production logging or provider console boundary and summarize the finding with redacted facts only.
Documentation and logging prohibitions
Security documentation, runbooks, tickets, pull requests, verification summaries, and shared logs must not contain raw secrets, tenant identifiers, customer data, customer domains, provider payloads, webhook signatures, bearer tokens, API keys, passwords, registry credentials, Coolify webhook URLs, UUID-like deploy endpoints, or secret-shaped examples. Use placeholder names for configuration keys and generic labels for origins and providers.
If a secret or customer value is accidentally captured, rotate the affected credential where applicable, remove the exposure from shared systems, and record the incident using only redacted evidence and the containment actions taken.
Supported non-E2E verification
These commands are safe for Auto-Mode or local static verification because they do not launch browser automation, Playwright, Cypress, live Docker, live registry pushes, or production traffic:
test -s docs/security.md
python - <<'PY'
from pathlib import Path
text = Path('docs/security.md').read_text()
for marker in ('TO' + 'DO', 'T' + 'BD', 'FIX' + 'ME'):
assert marker not in text
PY
pnpm run verify:production-docs
pnpm exec vitest run scripts/verify-production-docs.test.mjs
pnpm --dir server exec vitest run server.test.ts
pnpm exec vitest run apps/api/src/app.test.ts apps/api/src/cors.test.ts packages/config/src/env-access-enforcement.test.ts packages/config/src/api.test.ts
Browser flows, Playwright, Cypress, live Docker smokes, registry push proof, Coolify execution, and provider-console inspection are manual runtime activities unless a human explicitly approves the exact focused run in the current session. Manual curl or header inspection must capture only header names, policy values, status, request ID, and generic origin labels; it must not record raw secrets, tenant identifiers, customer data, provider payloads, or full environment values.
Security incident response boundary
- Identify the affected surface: web headers, API auth, CORS, Mailjet, database, Redis, worker queue, deployment secret, or observability output.
- Capture safe correlation facts:
x-request-id, job ID, status, stable error code, header name, diagnostic code, and generic provider or origin label. - Reproduce with focused non-E2E verification when the issue is repository-contract related; keep live browser, provider, Docker, registry, and Coolify checks manual unless explicitly approved.
- Compare behavior against
docs/security/runbook.md,docs/api.md,docs/configuration.md, and the deployment runbook. - Contain the issue with the narrowest change that restores the documented boundary, then update implementation tests and documentation together.
- Exclude raw secrets, tenant identifiers, customer data, provider payloads, and full environment dumps from the incident record at every step.