Environment Variables
Canonical list of OS_* environment variables read by the ObjectStack runtime, CLI, and built-in plugins.
Environment Variables
All ObjectStack-owned environment variables use the OS_ prefix. Variables are
read at startup unless noted otherwise. Boolean variables accept true / false
(case-insensitive); flag-style variables also accept 1 / 0 where noted.
Legacy names. Pre-1.0 unprefixed names (
PORT,DATABASE_URL,BETTER_AUTH_SECRET, …) still work this release and emit a one-shot deprecation warning. See Legacy aliases at the bottom of this page. Industry-standard third-party names (NODE_ENV,HOME,OPENAI_API_KEY,TURSO_*, OAuth*_CLIENT_ID/*_CLIENT_SECRET,RESEND_API_KEY,POSTMARK_TOKEN,AI_GATEWAY_*,SMTP_*) are not renamed and are read as-is by their respective libraries.
Runtime & Server
| Variable | Type | Default | Description |
|---|---|---|---|
OS_PORT | number | 3000 | HTTP listener port for os serve / os dev / os start. Resolution: --port › OS_PORT › PORT › 3000. |
OS_HOME | path | $HOME/.objectstack | ObjectStack config / state directory. |
OS_MODE | enum | standalone | Runtime mode. standalone | cloud. |
OS_BOOT_EMPTY | flag | 0 | When 1, boot the kernel with no metadata artifact (CLI internal). |
OS_MIGRATE_AND_EXIT | flag | 0 | When 1, run pending migrations and exit (suitable for one-shot jobs). |
OS_DISABLE_CONSOLE | flag | 0 | When 1, do not mount the Console portal under /_console. |
OS_EAGER_SCHEMAS | flag | 0 | When 1, evaluate all lazily-built Zod spec schemas eagerly at module load instead of on first use — an emergency rollback of the lazy-schema memory optimization (higher startup RSS). |
OS_SKIP_SCHEMA_SYNC | flag | 0 | When 1, skip the implicit db:sync on boot. Use after running migrations manually. |
Port conflicts behave differently by mode. In dev (
os dev, orNODE_ENV=development) a busy port auto-hops to the next free one (up to +100) and the banner prints the actual bound port. In production (os start) a busy port is a hard error (exit 1) — it never silently drifts, because a shifted port breaks reverse-proxy upstreams,OS_AUTH_URLcallbacks, andOS_TRUSTED_ORIGINS(CORS). Pin the port explicitly in production (OS_PORT=8080 os start) and keepOS_AUTH_URL/OS_TRUSTED_ORIGINSin sync with it.
Database & Storage
| Variable | Type | Default | Description |
|---|---|---|---|
OS_DATABASE_URL | url | — | Database connection string (e.g. file:./data.sqlite, postgres://…, mongodb://…, memory://). libsql:// / *.turso.io (Turso) is inferred too, but its driver is an optional package: npm install @objectstack/driver-turso, otherwise the boot fails loudly with that command — it never falls back to SQLite. |
OS_DATABASE_AUTH_TOKEN | string | — | Auth token for a libSQL/Turso connection (--database-auth-token). The vendor's own TURSO_AUTH_TOKEN is read as a fallback and is not renamed (see the third-party names note above). Ignored by every other driver — their credentials live in the URL. |
OS_DATABASE_DRIVER | enum | inferred | Force a specific driver when the URL is ambiguous. memory | sqlite | sqlite-wasm | postgres | mysql | mongodb | turso. |
OS_DATABASE_SQLITE_JOURNAL_MODE | enum | wal | Journal mode for file-backed SQLite. wal (default) lets a dev server and CLI commands share one file without blocking each other, and is what makes the os migrate occupancy check reliable. Set to delete for SQLite's rollback journal — required when the database lives on a network filesystem (NFS/SMB), where WAL cannot work. The setting is applied, not merely skipped: delete converts a database that already adopted WAL back. Ignored for :memory:, for the WASM SQLite driver, and for non-SQLite drivers. A per-datasource sqliteJournalMode in driver config outranks it. See Journal mode. |
OS_ALLOW_DRIVER_CONNECT_FAILURE | boolean | false | Escape hatch for the driver-connect boot guard. By default a data driver that fails to connect at startup refuses the boot — a server that cannot reach its database must not report itself started and then fail every request. The same guard covers a declared datasource that objects bind to via datasource: '…', or an external one with validation.onMismatch: 'fail': those objects have no fallback datasource, so an unconnected one means they are all dead. Set to 1 to boot anyway, in an explicitly degraded state logged loudly at startup. There is no reconnection: whatever failed stays dead for the process lifetime and every query and schema sync routed to it fails. |
OS_STORAGE_LOCAL_ROOT | path | ./.objectstack/data/uploads | Root directory for the local file storage adapter, relative to the process cwd (used by os serve's default storage capability wiring). This is the same value as Setup → Settings → File Storage → Root directory; setting it here pins that field (it shows as locked-by-env). Renamed from OS_STORAGE_ROOT — see below. |
OS_STORAGE_ROOT | path | — | Deprecated alias for OS_STORAGE_LOCAL_ROOT. Still read for one release, with a startup warning; it will be removed in a future major. Rename it now. Before the rename the two halves of the platform spelled this value differently — the CLI wrote OS_STORAGE_ROOT while the settings service read OS_STORAGE_LOCAL_ROOT — so any value other than the default was silently discarded at startup and uploads landed in ./.objectstack/data/uploads regardless. If you set OS_STORAGE_ROOT on an older release, check where your uploads actually are before assuming a backup covered them. |
OS_ARTIFACT_PATH | path | — | Path or http(s):// URL to a compiled objectstack.json artifact to boot the kernel from. |
Secrets & Clustering
| Variable | Type | Default | Description |
|---|---|---|---|
OS_SECRET_KEY | string | — | 32-byte master key (64 hex chars or base64) for sys_secret encryption — encrypted settings, secret fields, datasource credentials. Required for containerized or multi-node deployments; on a single durable host os start mints and persists a dev key instead. See Deployment Modes. |
OS_DEV_CRYPTO_KEY | string | — | Development convenience crypto key, consulted after OS_SECRET_KEY. Do not use in production. |
OS_CLUSTER_DRIVER | string | memory | Cluster coordination driver id. When set to anything other than memory, the runtime treats the deployment as multi-node (and requires OS_SECRET_KEY). Non-memory drivers are opt-in sibling packages (e.g. redis via @objectstack/service-cluster-redis) — see Cluster. |
OS_REDIS_URL | url | — | Connection URL passed to a non-memory cluster driver (e.g. OS_CLUSTER_DRIVER=redis). |
Authentication
| Variable | Type | Default | Description |
|---|---|---|---|
OS_AUTH_URL | url | http://localhost:<OS_PORT> | Public base URL of the auth server. Required behind a proxy or in production. |
OS_AUTH_SECRET | string | auto-generated (dev) | Secret used to sign sessions and cookies. Required in production. |
OS_AUTH_TWO_FACTOR | boolean | false | Enable the low-level better-auth two-factor plugin. Keep disabled unless your UI handles enrollment, login challenge, and backup-code recovery. |
OS_DISABLE_SIGNUP | boolean | false | When true, block new email/password sign-ups. The very first user can still sign up to bootstrap admin. |
OS_AUTH_EMAIL_PASSWORD_ENABLED | boolean | settings default | Settings env override for auth.email_password_enabled. Controls local email/password login. |
OS_AUTH_SIGNUP_ENABLED | boolean | settings default | Settings env override for auth.signup_enabled. Takes precedence over UI settings and is preferred over OS_DISABLE_SIGNUP. |
OS_AUTH_REQUIRE_EMAIL_VERIFICATION | boolean | settings default | Settings env override for auth.require_email_verification. |
OS_AUTH_MEMBERSHIP_POLICY | auto | invite-only | auto | Settings env override for auth.membership_policy — what a newly created user joins (ADR-0093 D1). auto binds every new user to the deployment's default organization. invite-only grants membership solely through an explicit act: creating a workspace, accepting an invitation, an admin adding them, or SSO just-in-time provisioning. Applies to sign-up and to the backfill of pre-existing member-less users. An unrecognized value is rejected with an error log and ignored — the deployment keeps its current policy rather than silently reverting to auto. |
OS_AUTH_GOOGLE_ENABLED | boolean | settings default | Settings env override for auth.google_enabled. Requires Google OAuth credentials from Settings or env. |
GOOGLE_CLIENT_ID | string | — | Deployment-level Google OAuth client id for the open-source Google login implementation. |
GOOGLE_CLIENT_SECRET | string | — | Deployment-level Google OAuth client secret for the open-source Google login implementation. |
OS_TENANCY_POSTURE | single | group | isolated | derived from OS_MULTI_ORG_ENABLED | Which organization wall the authorization kernel enforces (ADR-0105 D1). single = no wall. group = organization_id IN accessible_org_ids — organizations are membership boundaries over one shared dataset, with union read access across every organization the caller belongs to. isolated = organization_id = <active organization>, the hard legal-entity wall (formerly spelled multi). Unset derives from OS_MULTI_ORG_ENABLED (true ⇒ isolated, else single), so existing deployments are unchanged. An unrecognized value refuses to boot rather than silently falling back to a posture with no wall. Both walled postures require the enterprise @objectstack/organizations runtime — without it the request resolves to single and boot is refused unless OS_ALLOW_DEGRADED_TENANCY=1. |
OS_MULTI_ORG_ENABLED | boolean | false | Superseded by OS_TENANCY_POSTURE, and still honoured: true selects the isolated posture. When true, organization creation/switching UI is exposed. |
OS_OIDC_PROVIDER_ENABLED | boolean | tracks MCP | When true, expose this instance as an OIDC identity provider. When unset it follows the MCP server surface (OS_MCP_SERVER_ENABLED, on by default) — the MCP human-client track is OAuth 2.1, so every MCP-enabled deployment is its own authorization server. |
OS_COOKIE_DOMAIN | string | — | Cookie domain for cross-subdomain session sharing (e.g. .example.com). |
OS_TRUSTED_ORIGINS | csv | — | Comma-separated list of origins permitted for auth callbacks / CSRF. |
OS_TERMS_URL | url | — | URL shown next to the "Terms of Service" link on the sign-up form. Empty string hides the link. |
OS_PRIVACY_URL | url | — | URL shown next to the "Privacy Policy" link on the sign-up form. Empty string hides the link. |
Auth settings precedence:
- Environment variables win over values saved in the Setup UI.
OS_AUTH_<SETTING_KEY>settings env vars are the canonical form for settings-backed auth controls.- Existing deployment toggles such as
OS_AUTH_TWO_FACTORandOS_DISABLE_SIGNUPremain supported. - Google credentials can be saved in Setup → Authentication.
google_client_secretis encrypted at rest. GOOGLE_CLIENT_IDandGOOGLE_CLIENT_SECRETremain supported for deployment-level Google OAuth configuration.
CORS
| Variable | Type | Default | Description |
|---|---|---|---|
OS_CORS_ENABLED | boolean | true | Enable CORS middleware. |
OS_CORS_ORIGIN | csv | * | Allowed origins (comma-separated, or *). |
OS_CORS_CREDENTIALS | boolean | true | Allow credentialed cross-origin requests. |
OS_CORS_MAX_AGE | number | 86400 | Preflight cache TTL in seconds. |
Branding (single-tenant)
| Variable | Type | Default | Description |
|---|---|---|---|
OS_PRODUCT_NAME | string | ObjectStack | Product name shown in the UI chrome and emails. |
OS_PRODUCT_SHORT_NAME | string | OS | Short name used in compact UI surfaces (favicons, mobile titles). |
OS_APP_NAME | string | — | Application name surfaced to auth providers (used as the OAuth app display name). |
| Variable | Type | Default | Description |
|---|---|---|---|
OS_EMAIL_PROVIDER | enum | log | Transport. log | smtp | resend | postmark. log (default) prints to stdout without sending — it is also how a deployment declares that it does not send mail. Any other value is a delivery intent, and a boot that cannot honour it fails loudly instead of substituting the log transport. |
OS_EMAIL_API_KEY | string | — | API key for resend / postmark. Required when either is selected — a boot without it fails rather than starting with a transport that records every message in sys_email as sent and delivers nothing. Set OS_EMAIL_PROVIDER=log for environments that should not send mail. |
OS_EMAIL_FROM | — | Default From: address. | |
OS_EMAIL_RETRIES | number | 0 | Retry count for transient send failures (0 = no retry). |
OS_EMAIL_SMTP_HOST | string | — | SMTP server hostname. Required when OS_EMAIL_PROVIDER=smtp — a boot without it fails loudly rather than starting with a transport that logs mail instead of sending it. |
OS_EMAIL_SMTP_PORT | number | 587 | SMTP port. 465 selects implicit TLS (SMTPS). |
OS_EMAIL_SMTP_SECURE | boolean | true | Require TLS. On port 465 that means implicit TLS; on any other port a required STARTTLS upgrade (a server that will not upgrade fails the send instead of leaking credentials in the clear). false connects in the clear and upgrades only when STARTTLS is offered. |
OS_EMAIL_SMTP_USER | string | — | SMTP AUTH username. Omit for servers that accept unauthenticated relay. |
OS_EMAIL_SMTP_PASSWORD | string | — | SMTP AUTH password. |
SMTP delivery ships in
@objectstack/plugin-email(ADR-0012) and is implemented withnodemailer, imported lazily so deployments that never select SMTP — and non-Node runtimes — never loadnode:net/node:tls. Any provider that speaks SMTP works through it: a corporate mail server, Aliyun DirectMail, Tencent SES, QQ / 163 enterprise mailboxes.These variables configure the transport at boot. The same connection can be configured at runtime in Settings → Mail (namespace
smtp_host/smtp_port/smtp_secure/smtp_user/smtp_password), which hot-swaps the live transport without a restart and whose Send test email button performs a real delivery. Settings-page values are overridden by the namespace's ownOS_MAIL_*env door (e.g.OS_MAIL_SMTP_HOST), which locks the field in the UI.
AI
These variables configure the ObjectOS in-product AI runtime, which is not
part of the open-source framework. The open-source framework has no
in-UI AI runtime to configure: it exposes AI through @objectstack/mcp (BYO-AI),
where the model and provider are configured in your own MCP client (Claude,
Cursor, a local model, …). See the AI Capabilities guide.
| Variable | Type | Default | Description |
|---|---|---|---|
AI_GATEWAY_MODEL | string | — | Vercel AI Gateway model id (for example openai/gpt-4.1-mini or anthropic/claude-sonnet-4-5). When set, the AI service boots with the gateway adapter before trying direct providers. |
AI_GATEWAY_API_KEY | string | — | Vercel AI Gateway API key. Required by the gateway SDK for real gateway calls; by itself it does not select the gateway adapter. Pair it with AI_GATEWAY_MODEL. |
OPENAI_API_KEY | string | — | Selects the direct OpenAI adapter when no gateway model is configured. Default model: gpt-4o. |
ANTHROPIC_API_KEY | string | — | Selects the direct Anthropic adapter when gateway and OpenAI are not configured. Default model: claude-sonnet-4-20250514. |
GOOGLE_GENERATIVE_AI_API_KEY | string | — | Selects the direct Google Gemini adapter when gateway, OpenAI, and Anthropic are not configured. Default model: gemini-2.0-flash. |
OS_AI_MODEL | string | provider default | Overrides the model id for direct OpenAI / Anthropic / Google provider detection. Does not override AI_GATEWAY_MODEL; gateway model ids already include the provider prefix. |
OS_AI_ACTION_API_BASE_URL | url | — | Base URL prepended to relative type: 'api' action targets exposed as AI tools. Without this or a plugin apiActionBaseUrl, relative API actions are skipped for AI exposure. |
OS_AI_PROVIDER | enum | — | Settings override for the AI provider. Supported values include gateway, openai, anthropic, google, deepseek, dashscope, cloudflare, siliconflow, and openrouter. When set, it locks the Settings UI value. |
OS_AI_GATEWAY_MODEL | string | — | Settings override for the Vercel AI Gateway model id. Use with OS_AI_PROVIDER=gateway. |
OS_AI_GATEWAY_API_KEY | string | — | Settings override for the Vercel AI Gateway API key. Use with OS_AI_PROVIDER=gateway. |
OS_AI_OPENAI_BASE_URL | url | — | Settings override for the OpenAI-compatible chat base URL. Use with OS_AI_PROVIDER=openai for Azure, local gateways, or third-party OpenAI-shaped APIs. |
OS_AI_OPENAI_API_KEY | string | — | Settings override for the OpenAI-compatible API key. Stored only in env; the Settings UI cannot overwrite it while present. |
OS_AI_OPENAI_MODEL | string | gpt-4o | Settings override for the OpenAI-compatible chat model id. |
OS_AI_ANTHROPIC_API_KEY | string | — | Settings override for the Anthropic API key. Use with OS_AI_PROVIDER=anthropic. |
OS_AI_ANTHROPIC_MODEL | string | claude-sonnet-4-20250514 | Settings override for the Anthropic model id. |
OS_AI_GOOGLE_API_KEY | string | — | Settings override for the Google Generative AI API key. Use with OS_AI_PROVIDER=google. |
OS_AI_GOOGLE_MODEL | string | gemini-2.0-flash | Settings override for the Google model id. |
OS_AI_<PROVIDER>_API_KEY | string | — | Settings override for preset OpenAI-compatible providers, for example OS_AI_DEEPSEEK_API_KEY, OS_AI_DASHSCOPE_API_KEY, OS_AI_SILICONFLOW_API_KEY, or OS_AI_OPENROUTER_API_KEY. |
OS_AI_<PROVIDER>_MODEL | string | provider default | Settings override for preset provider model ids, for example OS_AI_DEEPSEEK_MODEL=deepseek-chat. |
The boot-time LLM adapter priority is:
- Explicit
AIServicePlugin({ adapter })or per-plugingatewayModeloption. AI_GATEWAY_MODELusing@ai-sdk/gateway.OPENAI_API_KEYusing@ai-sdk/openai.ANTHROPIC_API_KEYusing@ai-sdk/anthropic.GOOGLE_GENERATIVE_AI_API_KEYusing@ai-sdk/google.MemoryLLMAdapterecho mode when no real provider is configured.
After boot, if the Settings service is mounted, the ObjectOS AI runtime
reads the ai Settings namespace and swaps the adapter when the provider
comes from env or a stored Settings UI value. The manifest default
provider=memory is ignored so it does not mask boot-time auto-detection;
an explicit OS_AI_PROVIDER=memory or stored UI value does switch the runtime
back to memory. Settings values resolve as:
env (`OS_<NAMESPACE>_<KEY>`) > stored Settings UI value > defaultThat means OS_AI_PROVIDER=openai plus OS_AI_OPENAI_BASE_URL=... wins over
both the Settings UI and the boot-time provider auto-detection. ObjectStack
does not read unprefixed settings override names such as AI_PROVIDER or
AI_OPENAI_BASE_URL.
For example, a third-party OpenAI-compatible provider can be deployment-locked without using the Settings UI:
OS_AI_PROVIDER=openai
OS_AI_OPENAI_API_KEY=sk-...
OS_AI_OPENAI_MODEL=your-model
OS_AI_OPENAI_BASE_URL=https://your-provider.example.com/v1Provider credentials keep their upstream names. ObjectStack does not rename
OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY, or
AI_GATEWAY_* to OS_*.
OpenAI-compatible base URLs. Use
OS_AI_OPENAI_BASE_URLwithOS_AI_PROVIDER=openai, configure the same fields in Settings → AI, or pass an explicit adapter in code. Do not rely onOPENAI_BASE_URLas a platform-level environment variable.
MCP Server
Enable this to make the app you built AI-operable: with the MCP server on, an
MCP client — Claude Code, Claude Desktop, Cursor, or a local model — connects to your
running app and gets a generated tool surface over your objects and actions
(query_records, run_action, …), bound to the caller's principal and governed by the
same RBAC / RLS as the Console. No custom tooling, no separate API. See
Actions as Tools for the tool set.
The HTTP surface and the long-lived stdio transport are separate switches
(they used to share one, which let =true silently attach an unscoped transport —
see the note below):
os start # HTTP MCP served at /api/v1/mcp by default
OS_MCP_SERVER_ENABLED=false os start # opt out of the HTTP MCP surface
OS_MCP_STDIO_ENABLED=true OS_MCP_STDIO_API_KEY=osk_... os start # start the local stdio transport as that key's identity| Variable | Type | Default | Description |
|---|---|---|---|
OS_MCP_SERVER_ENABLED | boolean | true | The MCP HTTP surface (/api/v1/mcp) is a core capability and defaults on. Set false to disable it (endpoint 404s, the Connect-an-Agent page disappears). |
OS_MCP_STDIO_ENABLED | boolean | false | Auto-start the long-lived stdio transport at boot. Opt-in and stricter than the HTTP surface. Requires OS_MCP_STDIO_API_KEY — stdio runs as that key's identity with RLS/FLS/tenant applied; if the key is missing or invalid, boot fails closed (stdio refuses to start). See ADR-0101. |
OS_MCP_STDIO_API_KEY | string | — | The osk_... API key the stdio transport runs as. Resolved through the same verify chain as the HTTP/REST surfaces, so reads are scoped to that identity's permissions. Mint one from Setup → Connect an Agent (or POST /api/v1/keys). For full authority, mint a key on a platform-admin or dedicated service identity — there is deliberately no system/unscoped bypass. |
OS_MCP_SERVER_NAME | string | objectstack | Server name advertised to MCP clients. |
OS_MCP_SERVER_TRANSPORT | enum | stdio | stdio | http. Use http (Streamable HTTP) for a remote client; stdio for a local one. |
Deprecated (one release): OS_MCP_SERVER_ENABLED=true also used to auto-start
the stdio transport — overloading the HTTP switch, so setting it to "make sure MCP
is on" silently attached the unscoped stdio bridge. That trigger still works but now
logs a deprecation warning; use OS_MCP_STDIO_ENABLED=true instead.
OS_MCP_SERVER_ENABLED=false only ever gated HTTP and is unaffected.
Search
Pinyin recall for $search (ADR-0098): with the switch on, every object's
display/name field gets a hidden, platform-maintained companion column storing
full pinyin + initials ("张伟" → "zhangwei zw"), so lookup pickers, list
quick-search and ⌘K match zhangwei / zw against CJK names — transparently,
with no client or object changes.
os start # auto-on when the stack's i18n config lists any zh-* locale
OS_SEARCH_PINYIN_ENABLED=true os start # force on regardless of locales
OS_SEARCH_PINYIN_ENABLED=false os start # force off (e.g. a zh-locale stack that doesn't want the extra column)| Variable | Type | Default | Description |
|---|---|---|---|
OS_SEARCH_PINYIN_ENABLED | boolean | locale-derived | Pinyin search recall. When unset, the default derives from the stack's configured locales (i18n.defaultLocale / supportedLocales / fallbackLocale): any zh-* locale turns it on; an explicit value always wins. Gates both the compile-time __search companion column and the plugin-pinyin-search populate hooks, so there is no half-state. Off ⇒ no extra column, and pinyin-pro is never loaded. |
Coverage includes sys_user — the people picker finds users by pinyin with no
per-object configuration. Rows that predate the switch are backfilled automatically
on the first boot after it turns on (paged, idempotent); for runtime bulk imports
that bypassed write hooks, rebuildSearchCompanion (from
@objectstack/plugin-pinyin-search) recomputes the column on demand. See
Queries → Pinyin recall.
Marketplace & Metadata
| Variable | Type | Default | Description |
|---|---|---|---|
OS_MARKETPLACE_CACHE | enum | on | off disables the in-memory marketplace listing cache. |
OS_MARKETPLACE_PUBLIC_BASE_URL | url | — | Public base URL of the marketplace registry (proxied from this runtime when set). |
OS_ALLOW_UNMASKED_OBJECT_METADATA | boolean | false | Escape hatch for the metadata-plane field-level security mask (ADR-0106 D8). By default every object schema served by /meta and /metadata is projected onto the fields the calling user may read, so a field they cannot read does not appear at all — not its name, label, type, picklist options, formula, visibleWhen predicate, defaultValue, or the requiredPermissions capability guarding it. Set to 1 to serve the full schema to every authenticated caller, as releases before this one did. This changes disclosure only: the data plane still masks values and refuses forbidden writes either way, and the console reads field affordances from /auth/me/permissions, so toggling it never changes UI correctness. The REST layer also honours a per-server metadata.maskObjectFields: false; this variable is the deployment-wide knob and covers the runtime /metadata dispatcher, which has no REST config to read. |
OS_METADATA_WRITABLE | csv | — (none) | Comma-separated metadata type names (e.g. hook,validation) granted a runtime escape hatch that treats them as allowOrgOverride: true, letting artifact-backed items of those protected types be overridden per-org outside their static registry declaration. See ADR-0005. |
Multi-tenant / Cloud control plane
These variables are only meaningful when running in OS_MODE=cloud or against
the hosted ObjectOS Cloud control plane.
| Variable | Type | Default | Description |
|---|---|---|---|
OS_BASE_URL | url | — | Public base URL of this runtime, used in generated webhooks and links. |
OS_ROOT_DOMAIN | string | — | Root domain for multi-tenant subdomain routing (e.g. example.com). |
OS_ORGANIZATION_ID | string | — | Pin the runtime to a single organization id (per-process scope). |
OS_ENVIRONMENT_ID | string | — | Pin the runtime to a single environment id (per-process scope). |
OS_CLOUD_URL | url | https://cloud.objectos.ai | Base URL of the cloud control plane the CLI / runtime talks to. Set to off / local / none / disabled to disable cloud. |
OS_CLOUD_API_KEY | string | — | Shared secret authenticating runtime → cloud SSO handoffs. |
OS_CLI_CLIENT_ID | string | — | OAuth client id used by os login. |
OS_TOKEN | string | — | Personal access token used by the CLI in non-interactive contexts (CI). |
OS_PLATFORM_SSO | boolean | true | When false, fall back to local auth instead of the platform SSO handshake. |
OS_PROVISION_SYNC | flag | 0 | When 1, force synchronous provisioning of artifact kernels (slower boot, deterministic). |
OS_RUNTIME_PORT | number | — | Port the cloud control plane uses to reach this runtime (distinct from public OS_PORT). |
OS_PREVIEW_MODE | enum | — | Preview deployment routing mode. |
OS_PREVIEW_BASE_DOMAINS | csv | — | Comma-separated base domains used to resolve preview hostnames. |
Observability
| Variable | Type | Default | Description |
|---|---|---|---|
OS_OBS_EXPORTER | enum | noop | OpenTelemetry exporter. noop | console | json | otlp. noop (default) imposes no runtime cost. |
OS_OBS_SERVICE_NAME | string | objectstack | service.name resource attribute. |
OS_OBS_DEPLOYMENT_ENV | string | production | deployment.environment resource attribute. |
OS_OTLP_ENDPOINT | url | — | OTLP/HTTP collector endpoint. Required when OS_OBS_EXPORTER=otlp. |
OS_OTLP_HEADERS | csv | — | Comma-separated key=value pairs added to every OTLP export. |
OS_OTLP_FLUSH_MS | number | 10000 | OTLP batch flush interval. |
Tuning (advanced)
| Variable | Type | Default | Description |
|---|---|---|---|
OS_KERNEL_CACHE_SIZE | number | 32 | Max in-memory artifact kernels per process (cloud mode). |
OS_KERNEL_TTL_MS | number | 900000 | Idle TTL for cached kernels in ms. |
OS_ENV_CACHE_TTL_MS | number | 300000 | Env-record cache TTL in ms. |
OS_ARTIFACT_CACHE_TTL_MS | number | 300000 | Artifact-record cache TTL in ms. |
OS_ARTIFACT_FETCH_TIMEOUT_MS | number | 60000 | Timeout for remote artifact fetches. Only a positive value is honored — an unset, non-numeric, or 0 value falls back to the 60s default (pass fetchTimeoutMs: 0 in artifactSource config to actually disable the timeout). |
OS_SANDBOX_HOOK_TIMEOUT_MS | number | 250 | Default CPU-time budget for a sandboxed hook body (QuickJS, ADR-0102): how much VM-active time a body may burn — idle host-await time and a nested hook's own run are NOT charged. A loaded/slow host rarely needs to raise this now (it is not wall-clock), but the knob remains. Only a positive integer is honored; unset / non-numeric / non-positive keeps the 250ms default. A hook body's own declared timeoutMs still wins over this. |
OS_SANDBOX_ACTION_TIMEOUT_MS | number | 5000 | Default CPU-time budget for a sandboxed action body (QuickJS). Same resolution rules as the hook variant above (positive integer only; an action body's own timeoutMs still wins). |
OS_SANDBOX_WALL_CEILING_MS | number | 30000 | Wall-clock ceiling (ADR-0102) — the backstop that cuts a hook/action body stuck on a host call that never settles (which burns no CPU, so the CPU budget alone would never fire). The effective ceiling is max(this, cpuBudget), so it can never cut a body still inside its CPU budget. Positive integer only; unset keeps 30s. |
OS_INLINE_SEED_BUDGET_MS | number | 8000 | Time budget for synchronous seed execution at boot before deferring to a worker. |
OS_TENANT_AUDIT | flag | 1 | Set to 0 to silence the tenant-isolation audit warnings emitted by the SQL driver. |
Legacy aliases
Some env vars accept a legacy alias for compatibility. Ecosystem-standard names (e.g. DATABASE_URL, AUTH_SECRET, BETTER_AUTH_*, PORT, CORS_*, MCP_SERVER_*) are permanently accepted and no longer warn. ObjectStack's own former names are deprecated — prefer the canonical OS_*.
Removed in 11 (rename required):
OS_MULTI_TENANT→OS_MULTI_ORG_ENABLED,AUTH_BASE_URL/OS_AUTH_BASE_URL→OS_AUTH_URL.
| Canonical | Legacy |
|---|---|
OS_PORT | PORT |
OS_DATABASE_URL | DATABASE_URL |
OS_AUTH_URL | BETTER_AUTH_URL |
OS_AUTH_SECRET | BETTER_AUTH_SECRET, AUTH_SECRET |
OS_ROOT_DOMAIN | ROOT_DOMAIN |
OS_CORS_ENABLED | CORS_ENABLED |
OS_CORS_ORIGIN | CORS_ORIGIN |
OS_CORS_CREDENTIALS | CORS_CREDENTIALS |
OS_CORS_MAX_AGE | CORS_MAX_AGE |
OS_AI_MODEL | AI_MODEL |
OS_MCP_SERVER_ENABLED | MCP_SERVER_ENABLED |
OS_MCP_SERVER_NAME | MCP_SERVER_NAME |
OS_MCP_SERVER_TRANSPORT | MCP_SERVER_TRANSPORT |
OS_METADATA_WRITABLE | OBJECTSTACK_METADATA_WRITABLE |
OS_HOME | OBJECTSTACK_HOME |
OS_DEV_CRYPTO_KEY | OBJECTSTACK_DEV_CRYPTO_KEY |