Skip to Content
Self-hostingEnvironment variables

Environment variables

Set these in your host’s environment settings (on Vercel: Project Settings → Environment Variables). For local development, put them in apps/app/.env.local; apps/app/.env.local.example is a commented starting point. For which Vercel scope each variable goes in, see Deploying to Vercel.

Generate secrets with openssl rand -hex 32. Every variable has its own link: hover its row and copy the #.

Required is Yes when every deployment needs it, Optional when it has a working default, and If … when only that setup needs it.

Quick start

The minimum for a working deployment with the default backends:

apps/app/.env.local
# Auth BETTER_AUTH_URL=https://screenplay.example.com # production only BETTER_AUTH_PRODUCTION_URL=https://screenplay.example.com BETTER_AUTH_SECRET=<openssl rand -hex 32> GITHUB_CLIENT_ID=... GITHUB_CLIENT_SECRET=... # Data DATABASE_URL=postgres://... LIVEBLOCKS_SECRET_KEY=sk_... PUBLIC_BLOB_READ_WRITE_TOKEN=... # a public Blob store for thumbnails PRIVATE_BLOB_READ_WRITE_TOKEN=... # a second, private Blob store for canvas files # Agent ANTHROPIC_API_KEY=sk-ant-... AGENT_DEFAULT_MODEL=anthropic:claude-sonnet-5-5 # Secrets ENCRYPTION_KEY=<openssl rand -hex 32> TERMINAL_AUTH_SECRET=<openssl rand -hex 32>

Auth

VariableRequiredDescription
BETTER_AUTH_URLIf productionThis deployment’s URL. Leave it unset on preview deploys, which fall back to https://$VERCEL_URL. Locally it defaults to http://localhost:$PORT.
BETTER_AUTH_PRODUCTION_URLYesThe stable URL registered with your GitHub OAuth app. It must be the same everywhere, because preview sign-ins are proxied through it.
BETTER_AUTH_SECRETYesSigns sessions and OAuth state. It must be identical in production and every preview deploy.
GITHUB_CLIENT_IDYesClient ID of your GitHub OAuth app.
GITHUB_CLIENT_SECRETYesClient secret of the same OAuth app.

Data

VariableRequiredDescription
DATABASE_URLYesPostgres connection string. See Database.
LIVEBLOCKS_SECRET_KEYIf LiveblocksServer-side Liveblocks key for the default Yjs host. It never reaches the browser. See Yjs host.
LIVEBLOCKS_WEBHOOK_SECRETOptionalSigning secret for a Liveblocks ydocUpdated webhook pointed at /api/liveblocks/webhook. With it, canvas thumbnails refresh even when nobody has the canvas open (for example while the agent works). Without it, thumbnails refresh from open clients.
PUBLIC_BLOB_READ_WRITE_TOKENIf Vercel BlobToken for the public Vercel Blob store that holds thumbnails. Connect a Blob store with public access to the project with the env var prefix PUBLIC_BLOB and it’s injected automatically; run vercel env pull to use it locally. See Blob store.
PRIVATE_BLOB_READ_WRITE_TOKENIf Vercel BlobToken for a second, private Vercel Blob store that holds canvas files, the files agents save for a canvas’s members. The thumbnails store is public and a store’s access can’t be changed, so files need their own. Create a Blob store with private access and connect it with the env var prefix PRIVATE_BLOB. Without it, agents can’t save or open files. See Blob store.
ENCRYPTION_KEYYes64 hex characters. Encrypts project environment variables and presets at rest.

Agent

At least one model provider must be configured. Each provider enables itself when its variables are set. See Model providers.

VariableRequiredDescription
ANTHROPIC_API_KEYIf AnthropicEnables Anthropic models.
OPENAI_API_KEYIf OpenAIEnables OpenAI models.
GOOGLE_GENERATIVE_AI_API_KEYIf Google GeminiEnables Gemini models.
AI_GATEWAY_API_KEYIf Vercel AI GatewayEnables models through the AI Gateway. It must be set explicitly, even on Vercel.
OPENAI_COMPATIBLE_BASE_URLIf OpenAI-compatibleBase URL of any OpenAI-compatible endpoint: OpenRouter, Groq, vLLM, LM Studio… Setting it enables the provider.
OPENAI_COMPATIBLE_API_KEYOptionalBearer token for that endpoint. Leave it unset for an endpoint without auth, such as a local LM Studio.
AGENT_DEFAULT_MODELOptionalThe default model as provider:model, e.g. openai:gpt-6-astra. If its provider isn’t configured, new chats use the first enabled model instead. Default: anthropic:claude-sonnet-5-5.
SANDBOX_HARNESSESOptionalComma-separated coding CLIs to install in every sandbox, runnable from a shell: claude-code, codex, opencode-gateway, opencode-compat. Unset means none. See Coding CLIs.

Terminal access

VariableRequiredDescription
TERMINAL_AUTH_SECRETYesHMAC key for the short-lived terminal and shared-frame credentials. A user gets one only after passing the canvas-membership check.
TERMINAL_AUTHOptionalHow terminal tabs authenticate to the sandbox: bearer (default) or ttyd-credential. An unknown value fails at startup.

Choose TERMINAL_AUTH based on who uses the deployment:

  • bearer (default, no extra setup). The terminal is reached through a random, unguessable sandbox URL, and that URL is the whole credential. URLs can leak through browser history, Referer headers, proxy logs, and screen shares, and anyone holding one gets a writable shell until the sandbox is gone. Use it only when every user is trusted.
  • ttyd-credential (no extra infrastructure). The terminal daemon requires a per-sandbox secret derived from TERMINAL_AUTH_SECRET, which is sent over the WebSocket handshake instead of in the URL, so a leaked URL alone isn’t enough. The secret is shared by the sandbox’s members and lasts as long as the sandbox, so it can’t be revoked per user mid-session. Deleting the chat invalidates it.

Neither mode offers per-user revocation. For a deployment shared with people you don’t fully trust, use ttyd-credential and keep canvas membership tight. Design notes are in apps/app/docs/adr/0002-byo-harness-terminal.md.

Sandbox

VariableRequiredDescription
VERCEL_OIDC_TOKENIf Vercel SandboxAuthenticates the default Vercel Sandbox provider, and it’s the only credential Screenplay uses for it: a Vercel access token won’t do. It’s injected automatically on Vercel; locally, vercel env pull fetches a ~12-hour token. See Sandbox provider.
SHARED_FRAMESOptionaloff hides Go live, so every hosted frame stays a per-viewer preview and no frame runs a browser in the sandbox. Default: on. See Shared frames.
VERCEL_SANDBOX_IMAGEOptionalThe Vercel Container Registry image chat sandboxes boot from. Default: screenplay-workspace, the image built from apps/app/vercel-sandbox-image in the app’s own Vercel project. See Sandbox image.

Scheduled jobs

VariableRequiredDescription
CRON_SECRETOptionalLets Vercel Cron call /api/pr-watch/tick, which checks every canvas with an open pull request every five minutes, so chats hear about failing checks, conflicts and merges with nobody’s canvas open. Vercel sends it on each cron call; any random string works (openssl rand -hex 32). Without it, pull request status only updates while someone has the canvas open. The desktop app runs the same check every minute and doesn’t need it.

Serving

VariableRequiredDescription
NEXT_PUBLIC_BASE_PATHOptionalServe the app under a path prefix such as /app. It’s inlined at build time. See Mount path.
PORTOptionalThe port the app listens on. Local URLs and the local WebSocket servers’ origin check use it. Default: 3000.
CHROMIUM_PATHOptionalChromium binary for thumbnail capture outside Vercel. Defaults to Puppeteer’s bundled browser.

Build switches

These select alternative backends. The desktop app sets them together; see Local build and Desktop app. A hosted deployment leaves them all unset.

VariableRequiredDescription
SCREENPLAY_DESKTOPOptional1 builds the self-contained server the desktop shell runs (Next’s standalone output). Read at build time.
NEXT_PUBLIC_SCREENPLAY_LOCALOptional1 removes the multi-user surface (single local user). Inlined at build time.
SANDBOX_BACKENDOptionallocal: each chat’s code is a git worktree on the host.
SCREENPLAY_DBOptionalpglite: embedded Postgres.
NEXT_PUBLIC_YJS_HOSTOptionallocal: in-process y-websocket server. Inlined at build time.
BLOB_STOREOptionallocal-fs: thumbnails and canvas files on local disk.
AGENT_ENGINEOptionalexternal: the agent is an installed coding CLI over ACP.
SCREENPLAY_ACP_HARNESSOptionalWith AGENT_ENGINE=external, the coding CLI a chat uses when it hasn’t picked one: claude-code or codex. Default: claude-code. See Coding CLIs.
THUMBNAIL_CAPTUREROptionaltauri-webview: capture thumbnails in the desktop webview.

Desktop build

The desktop shell sets these for the app it launches (see Desktop app). You only set them yourself when running the local build by hand.

VariableRequiredDescription
SCREENPLAY_WORKTREE_ROOTOptionalWhere the local sandbox provider keeps its clones and worktrees. Default: ~/.screenplay/worktrees.
LOCAL_BLOB_DIROptionalWhere the local-fs blob store writes thumbnails, with BLOB_STORE=local-fs. Default: .screenplay/blobs. See Blob store.
LOCAL_BLOB_BASE_URLOptionalThe URL LOCAL_BLOB_DIR is served from, which thumbnail URLs start with. Default: /blobs, the app’s own route.
LOCAL_FILES_DIROptionalWhere the local-fs file store keeps canvas files’ bytes, with BLOB_STORE=local-fs. Nothing serves it directly. Default: .screenplay/files; the desktop shell points it at files under the app’s data folder.
YJS_PERSISTENCE_DIROptionalWhere the local y-websocket server saves each canvas, with NEXT_PUBLIC_YJS_HOST=local. Default: .data/yjs in the working directory. See Yjs host.
NEXT_PUBLIC_YJS_WS_PORTOptionalPort of the local y-websocket server. Inlined at build time, so the page and the server agree. Default: 1234.
SCREENPLAY_COORDINATOR_ROOTOptionalWhere each canvas’s Coordinator chat runs its coding CLI, one folder per canvas. The desktop app sets it under its data directory. Default: ~/.screenplay/coordinator.
PGLITE_DATA_DIROptionalDirectory for the embedded PGlite database. Default: ./.pglite.
PGLITE_MIGRATIONS_DIROptionalWhere PGlite migrations are read from. The packaged app points it at its bundled drizzle/local.
SCREENPLAY_TERMINAL_WS_PORTOptionalPort for the local terminal WebSocket server. Default: an ephemeral port. The server listens on 127.0.0.1 and accepts only the app’s own page with the secret minted at launch.
TAURI_CONTROL_URLOptionalThe desktop shell’s localhost control server, which captures thumbnails in a webview and opens the native folder picker. Without it, tauri-webview thumbnails fail and adding a local folder falls back to typing its path.
TAURI_CONTROL_TOKENOptionalThe per-launch token that control server asks for before it snapshots the canvas window for the agent on the Mac.
Last updated on