Skip to content

Environment variables

Subspace is configured almost entirely through environment variables read at boot. There is no separate config file: set a variable before starting the server (or export it in your process manager / systemd unit) and it takes effect on the next boot. This page is the exhaustive reference; see Self-hosting for a narrative setup walkthrough.

The baseline variables every install cares about: where data lives, what port and host to bind, and which deterministic fakes to use in dev/test.

Variable Default Purpose
SUBSPACE_HOME ~/Subspace Root data directory: embedded Postgres (pg/, unix socket only, no TCP) and content-addressed blobs (blobs/). Migrations run transactionally on every boot.
SUBSPACE_PORT 4780 Public HTTP/WebSocket port. Under the cloud profile, platform $PORT is used when this is unset.
SUBSPACE_HOST 127.0.0.1 Public bind address. A wildcard bind is accepted only under SUBSPACE_PROFILE=cloud; local installs retain the loopback-safe default.
SUBSPACE_PROFILE local Set to cloud for public cells. One fail-closed switch disables server PTY/Jupyter/stdio MCP, os.exec, containers, local MLOps, and the OKF watcher.
SUBSPACE_PUBLIC_ORIGIN unset Canonical browser origin, for example https://cell.example.com. Required and HTTPS under the cloud profile; drives OAuth redirects, Secure cookies, CSRF and WebSocket Origin checks.
SUBSPACE_TRUST_PROXY unset Set to railway only behind Railway’s overwriting edge. Enables trusted X-Forwarded-Proto and X-Real-IP; forwarded headers are ignored otherwise.
SUBSPACE_CLAIM_TOKEN unset One-shot first-admin credential. Required on an unclaimed non-loopback bind unless an unused admin bootstrap invite is already seeded; only its SHA-256 digest is stored.
SUBSPACE_INTERNAL_PORT 4782 Loopback-only capability-broker/WDK executor listener. It never binds $PORT or SUBSPACE_HOST.
SUBSPACE_SEED unset Set to mock-world to seed the shared e2e/demo fixture (distinct inboxes, calendars, CRM rows) on first boot.
PROVIDERS unset Set to fake to swap every external seam (Jupyter kernel, search embedder, mail/calendar/IM providers, LLM calls, dev-login) for deterministic fakes. Used by tests and pnpm dev:sandbox.
SUBSPACE_SHELL $SHELL Shell binary terminals spawn on the server host.
SUBSPACE_AUTH unset Set to off to disable identity entirely (legacy single-user behavior). The server refuses to boot with this on a non-loopback SUBSPACE_HOST.
SUBSPACE_DEVICE_TOKEN unset Bearer device token a non-browser client (desktop, mobile) stamps on every request; see Device pairing.
SUBSPACE_URL http://127.0.0.1:4780 Base URL used by @subspace/sdk and subspace-sdk mcp. This configures the external client process, not the server bind address.
SUBSPACE_TOKEN unset Scoped SDK/device token used by @subspace/sdk and subspace-sdk mcp; sent as x-subspace-device-token. Claimed instances reject the MCP process at startup when this credential is absent or invalid.
SUBSPACE_SDK_CONFIG ~/.subspace/sdk.json Node/Python SDK profile file override. The file contains named {url, token} profiles shared by the clients, CLI, verifier, and MCP command.
SUBSPACE_SDK_PROFILE file default Named SDK profile to select after explicit arguments and SUBSPACE_URL / SUBSPACE_TOKEN.
SUBSPACE_SESSION unset Web session used only by subspace-sdk token create. Prefer this environment variable to --session so the session secret stays out of shell history.
SUBSPACE_CORS_ORIGINS unset Comma/space-separated exact http(s) origins allowed to call token-authenticated SDK tRPC/socket-ticket routes from browser JavaScript. No wildcard; off by default; does not enable credentialed cookies.
SUBSPACE_AUTH_RATE_LIMIT 30 per 5 minutes Per-client request ceiling for authentication, pairing, and socket-ticket endpoints. Railway identity uses trusted X-Real-IP.
SUBSPACE_UPLOAD_MAX_BYTES 536870912 Maximum streamed /files request size (512 MiB by default). Multipart buffering is refused.
SUBSPACE_MODEL claude-sonnet-5 Model id used for agent runs, event-triage classification, mail-rule matching, and other LLM seams (Vercel AI SDK + Anthropic).
ANTHROPIC_API_KEY unset Seeds the llm.anthropic capability in the capability broker on first boot (insert-only; see Tools and approvals).
OPENAI_API_KEY unset Needed for real embeddings (text-embedding-3-small) when PROVIDERS=fake is not set; also seeds the llm.openai capability.

Set any WAL-G repository variable to turn on continuous archiving; the nightly backup job and monthly restore drill pick the rest up automatically. See Backup and restore for the full lifecycle.

Variable Default Purpose
WALG_S3_PREFIX, WALG_FILE_PREFIX, WALG_GS_PREFIX, WALG_AZ_PREFIX, WALG_SSH_PREFIX, WALE_*_PREFIX unset Any one of these (standard WAL-G repository variables) turns on PostgreSQL archive_command (archive_timeout=60s) and enables the 02:10 nightly backup-push job, which retains 14 full backups. Hosted cells use a tenant-specific R2 WALG_S3_PREFIX; a composed recovery point is only minted when the blob replica is also complete.
SUBSPACE_WALG_BIN bundled WAL-G 3.0.8 Dev override for the WAL-G binary path.
SUBSPACE_BLOB_REPLICA_URL unset Enables the durable blob-replicator outbox consumer: referenced blobs upload to ab/cd/<sha256> at this URL; deletes become seven-day delayed tombstones.
SUBSPACE_BLOB_REPLICA_TOKEN unset Bearer auth for SUBSPACE_BLOB_REPLICA_URL.
RESTIC_REPOSITORY unset With this set, the same nightly job snapshots okf/ (the canonical markdown dump) via restic and retains 14 daily snapshots.
SUBSPACE_RESTIC_BIN restic Dev override for the restic binary path.

Hosted cells also configure KMS-backed tenant-key escrow. A non-fake SUBSPACE_PROFILE=cloud boot refuses to proceed unless the complete set is present. The AWS SDK uses its normal workload-identity credential chain; no variable contains the plaintext DEK.

Variable Default Purpose
SUBSPACE_TENANT_ID unset Stable control-plane tenant id. It is authenticated by the escrow service and bound into the AWS KMS encryption context.
SUBSPACE_KMS_PROVIDER unset Hosted DEK wrapper provider. The production implementation currently accepts aws.
SUBSPACE_KMS_KEY_ID unset Immutable AWS KMS symmetric key ARN used for Encrypt/Decrypt (aliases are rejected so repointing one cannot strand retained generations). Grant the cell role only those actions and require the tenant/purpose encryption context.
SUBSPACE_KEY_ESCROW_URL unset Control-plane base URL for wrapped-generation escrow and recovery. HTTPS is mandatory except on loopback. The control plane is separate fleet infrastructure, not the tenant-cell server.
SUBSPACE_KEY_ESCROW_TOKEN unset Per-cell bearer credential for the escrow API. It authenticates the cell but is never sent to KMS or written to escrow audit records/logs.

The production image also contains a separate fleet executable. Set SUBSPACE_SERVICE=key-escrow-control-plane on a dedicated service to select it. It connects to an external control-plane PostgreSQL database and never starts or opens a tenant cell’s embedded database. Its migrations live in the control_plane schema.

Variable Default Purpose
SUBSPACE_SERVICE cell Set to key-escrow-control-plane to run the durable escrow API instead of a tenant cell.
SUBSPACE_KEY_ESCROW_DATABASE_URL $DATABASE_URL External PostgreSQL connection URL for the fleet escrow database. Required under the control-plane service.
SUBSPACE_KEY_ESCROW_DB_SSL require Database TLS mode: require, verify-full, or disable. Use disable only for a loopback test database.
SUBSPACE_KEY_ESCROW_DB_POOL_SIZE 10 PostgreSQL pool size, bounded to 2–100 connections.
SUBSPACE_KEY_ESCROW_CELL_TOKENS [] Secret JSON array of {credentialId, tenantId, cellId, token} entries to upsert at boot. Each token is credentialId.<32+ character random secret>. Only its SHA-256 digest is stored; omitting an existing entry does not revoke it.
SUBSPACE_KEY_ESCROW_HOST 0.0.0.0 Control-plane bind address.
PORT / SUBSPACE_KEY_ESCROW_PORT 4783 Public control-plane port (PORT wins for Railway compatibility).
SUBSPACE_KEY_ESCROW_SHUTDOWN_MS 30000 Drain deadline before the process closes its database pool, bounded to 1–120 seconds.

Provision SUBSPACE_KEY_ESCROW_CELL_TOKENS through the platform secret manager; do not place it in a checked-in environment file. Rotation is an upsert with a new token. To revoke a cell immediately, set disabled_at on its control_plane.cell_credentials row (or delete the credential) before removing the old platform secret. GET /livez is process liveness. GET /readyz verifies the database and latest migration; it returns 503 as soon as SIGTERM drain begins, and new escrow calls return 503 while in-flight requests finish.

Configuring SUBSPACE_GITOPS_REPO turns on declarative config deployment from a git repo: config pages and the plugin set sync from the repo on boot and on a poll interval. See GitOps for the full model.

Variable Default Purpose
SUBSPACE_GITOPS_REPO unset Clone URL or local path of the config repo. Setting this enables gitops sync.
SUBSPACE_GITOPS_BRANCH main Branch to sync from.
SUBSPACE_GITOPS_PATH repo root Subdirectory within the repo holding pages/** and an optional plugins.json.
SUBSPACE_GITOPS_POLL 300 (seconds) Poll interval after the blocking boot sync; 0 means boot-only (no poll).
SUBSPACE_GITOPS_MODE readonly Set to write-back so managed pages become editable again: every edit exports back to the repo as a commit.
SUBSPACE_GITOPS_WRITEBACK_BRANCH the sync branch In write-back mode, picks direct-to-branch commits (default) or a PR side branch.
SUBSPACE_GITOPS_TOKEN unset Seeds the gitops.git.token secret: private HTTPS remotes authenticate through GIT_ASKPASS, injected per git invocation. Absent = ambient auth (ssh-agent, credential helper).
SUBSPACE_GITOPS_GIT_USERNAME x-access-token Overrides the basic-auth username paired with SUBSPACE_GITOPS_TOKEN.
SUBSPACE_GITOPS_WEBHOOK_SECRET unset Seeds the gitops.webhook.secret used to verify POST /gitops/webhook (GitHub/Gitea HMAC or GitLab token header). No secret configured means the webhook 403s for everyone.
SUBSPACE_PLUGINS unset Comma/space-separated npm plugin specs to reconcile at boot; a repo-pinned plugins.json (when gitops is configured) wins over this.

Subspace runs as a single all-in-one process by default. Opt-in posture B splits it into a trusted web plane and a secret-scrubbed worker plane over the same home, selected with a CLI flag rather than an env var:

Terminal window
subspace-server --role=web # tier 0: HTTP/WS/tRPC, Postgres, approvals, broker routes
subspace-server --role=worker # tier 1: agent workflow world, scrubbed env, broker RPC only
subspace-server --role=all # default: single process, both roles
Variable Default Purpose
SUBSPACE_WORKER_URL :4781 Loopback control API the web role uses to start/resume workflows on the worker role.
SUBSPACE_BROKER_URL :4782 Loopback capability-broker URL the worker uses. In split containers this is the internal listener, never the public port.

Provider and platform credentials are never read ambiently by tool code; they seed the encrypted ops.secrets table once at boot (insert-only, a rotated row is never clobbered by a stale env var) and are acquired through the capability broker from then on:

Variable Seeds capability
SLACK_BOT_TOKEN slack.send
SLACK_APP_TOKEN slack.socket
TELEGRAM_API_ID, TELEGRAM_API_HASH, TELEGRAM_SESSION telegram
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET google.oauth (also Google login)
DEEPGRAM_API_KEY deepgram.stt
ANTHROPIC_API_KEY llm.anthropic
OPENAI_API_KEY llm.openai
WANDB_API_KEY wandb.api
DAGSTER_API_TOKEN dagster.api
DEVIN_API_KEY devin.api
DROPBOX_ACCESS_TOKEN provider.dropbox
MICROSOFT_GRAPH_ACCESS_TOKEN, MICROSOFT_GRAPH_DRIVE_ID provider.sharepoint
SUBSPACE_GITOPS_TOKEN gitops.git.token
SUBSPACE_GITOPS_WEBHOOK_SECRET gitops.webhook.secret

Variables that override binaries, timings, or debug behavior. Most are only relevant when developing Subspace itself or writing e2e fixtures.

Variable Default Purpose
SUBSPACE_CLAUDE_BIN claude Overrides the Claude Code CLI binary the coding-agent dispatch ladder (code.claude.run) spawns.
SUBSPACE_BD_BIN bd Overrides the bd issue-tracker CLI binary code.task.create shells out to.
SUBSPACE_OS_EXEC unset Set to 1 to enable the os.exec agent tool (also implicitly on under PROVIDERS=fake).
SUBSPACE_CONTAINERS unset Set to 1 to enable the real docker containers.* agent tools.
SUBSPACE_DOCKER_BIN docker Overrides the docker binary for containers.* (e2e points this at a fixture).
SUBSPACE_SANDBOX unset Set to 1 to register the lower-trust sandbox.exec/read/write agent tools. Containers have a named per-run volume, no host mounts, credentials, or network, and fixed resource limits.
SUBSPACE_SANDBOX_IMAGE bash:5.2-alpine3.21 Overrides the minimal image used by sandbox.*. The value is passed as one validated Docker image reference, never through a shell.
SUBSPACE_SANDBOX_DOCKER_BIN docker Overrides only the sandbox adapter’s Docker CLI binary. Supplying it also enables the adapter for deterministic tests; it never enables containers.*.
EVALS_LIVE unset Set to 1 to run the eval harness against the real model instead of recorded fixtures (SUBSPACE_MODEL). See Evals.
SUBSPACE_GRADER_MODEL claude-haiku-4-5 Model used for the cheap post-run observation grader and the weekly pattern clusterer.
RUN_INTROSPECTION off off | observe | full (gates post-run observation grading and pattern clustering).
SUBSPACE_INDEX_DEBOUNCE_MS 0 under PROVIDERS=fake, 1500 otherwise Debounce window for the search indexer; 0 means synchronous (embed on every commit).
SUBSPACE_MCP_SERVERS unset JSON array ([{name, command, args?}], stdio) of MCP servers to bind, for names not claimed by an mcp/ page. Page-defined servers take precedence per name. See MCP servers.
SUBSPACE_DEVIN_SWEEP_MS 120000 (2 min) Poll interval for the Devin session status sweep.
SUBSPACE_MLOPS_SWEEP_MS 20000 (20s) Poll interval for the mlops run-watcher sweep.