Production Readiness
The checklist for running a self-managed ObjectOS deployment safely in production — headers, secrets, rate limits, CORS, boot guards, scheduled work and retention.
Use this checklist before exposing ObjectOS to production traffic. It holds for Community and Enterprise alike; the Enterprise adds section at the end lists the items the licensed image brings with it.
HTTP hardening
The runtime sends conservative security headers on its routes by default. Verify the final response, after any proxy that rewrites headers:
Content-Security-Policy;X-Content-Type-Options;X-Frame-Options;Referrer-Policy;Permissions-Policy;Cross-Origin-Resource-Policy;- HSTS, which the runtime does not send by default: add it at your proxy once TLS is confirmed.
curl -I https://app.example.comSecrets
Store these in a secret manager, and inject them at runtime:
| Secret | Purpose |
|---|---|
OS_AUTH_SECRET | Session signing base secret. Identical on every replica — replicas with different values reject each other's sessions. |
OS_SECRET_KEY | The key every stored secret is encrypted under. Required in a container, where a self-minted key dies with the filesystem. Back it up with the database: one without the other cannot decrypt anything. |
| Database credentials | Business database access |
| OIDC client secret | Single sign-on |
| Provider API keys | Email, storage, AI, integrations |
Never bake secrets into artifacts or images.
A deployment bound to a control plane holds a runtime token as well, but that one is not a secret you configure: it is minted during the binding and persisted by the runtime. There is no deployment-level API key to distribute for it.
Rate limiting
The runtime has a built-in token-bucket rate limiter, off until the app
declares a budget (server.security.rateLimit in its stack). An
over-budget caller is answered 429 with Retry-After. The bucket is keyed
by the signed-in user, and by the caller's address for anonymous traffic.
Behind a reverse proxy, the caller's address is the proxy's unless the app
sets server.trustProxy: true — and set that only behind a proxy that
overwrites X-Forwarded-For on every request, or callers can choose their
own address. The counters are kept in memory, per process. Sign-in can take
its own, tighter budget in Setup → Authentication.
Where the limit belongs at the edge instead — an ingress or gateway — start from budgets like these:
| Traffic | Example limit |
|---|---|
| Auth endpoints | 10/min/IP |
| Write requests | 60/min/IP |
| Read requests | 600/min/IP |
CORS
With no OS_CORS_ORIGIN, the runtime admits no cross-origin caller —
and needs none while the Console and the API share the public origin. A
front end served from another origin is listed explicitly, comma-separated:
OS_CORS_ORIGIN=https://portal.example.com,https://admin.example.com* is refused at boot while credentials are on.
Boot guards stay on
Two escape hatches exist for development. Neither belongs in production:
OS_ALLOW_DRIVER_CONNECT_FAILUREis unset. A database that cannot connect at startup refuses the boot, so a badOS_DATABASE_URL, a rotated password or a closed network path is a failed deploy — not a "started" server that answers 500 to every request. The flag boots without the database and never reconnects.OS_ALLOW_DEGRADED_TENANCYis unset. A deployment that asks for several organizations without the organizations runtime refuses to boot; this flag lets it boot with the isolation missing.
Scheduled work is a decision
Scheduled work that apps and packages declare — time-triggered flows and
packaged cron jobs — is off until the deployment sets
OS_AUTOMATION_SCHEDULED_WORK_ENABLED=true. A deployment that expects a
digest, a sweep or a reminder to fire must turn it on. Leaving it off is a
legitimate answer too; make it on purpose. Confirm the effective value with
os doctor, and read the startup summary: work listed there as disabled by
deployment policy is not broken, it is switched off. See
Environment Variables.
Retention is set before go-live
The platform deletes telemetry on a schedule: activity after 14 days, job
runs after 30, notifications after 90, and audit records leave hot storage
after 90. If your compliance regime needs longer, extend the windows through
the lifecycle.retention_overrides setting before go-live — data deleted
by policy is gone from every backup taken afterwards.
Go-live checklist
- TLS is terminated at the edge or ingress.
- Security headers are present on the final response.
- HSTS is enabled after TLS validation.
-
OS_AUTH_URLandOS_TRUSTED_ORIGINSmatch the public address. - CORS admits exactly the cross-origin front ends you serve.
- Rate limits protect sign-in and write endpoints.
-
OS_AUTH_SECRETandOS_SECRET_KEYare strong, stored as secrets, and escrowed. - The first administrator registered before the address was shared.
- OIDC callback URLs match the public domain.
-
OS_ALLOW_DRIVER_CONNECT_FAILUREandOS_ALLOW_DEGRADED_TENANCYare unset. -
OS_AUTOMATION_SCHEDULED_WORK_ENABLEDis a decision somebody made. - Retention windows match your policy.
- Business database backup and restore are tested — see Backup.
- Audit logs are retained according to your policy.
- Monitoring alerts on 5xx rate and readiness — see Observability.
- Rollback plan covers both the image version and the app artifact version.
Enterprise adds
-
OS_LICENSE_KEY— the deployment's entitlement — is stored as a secret. -
OS_CLOUD_URLis declared, and it matches the license mode. An ordinary license withoffis refused at startup; see Air-gapped. - The image is pinned by digest, and its signature, SBOM and provenance were verified (Docker).
- On several replicas,
OS_AUTH_SECRETandOS_SECRET_KEYare identical on every one of them. - On a multi-organization posture: the membership policy and the mounted
AI agents are declared,
OS_PLATFORM_OWNER_EMAILnames more than one verified administrator, and cross-organization negative access tests pass.