/voice,
/realtime/client-secret, or /ws traffic. Keep the runtime service behind
normal deployment protections and use provider-key quotas for public previews.
The platform API stores the OSS OpenDot control plane in PostgreSQL: app users, local auth credentials, user preferences, SDK API keys, versioned agents and pipelines, devices, device activation requests, device credentials, runtime session tokens, device state, and deployments.
Create the root environment file once:
POSTGRES_URI empty and the API/Drizzle commands use
localhost:${POSTGRES_PORT:-5432} with the configured POSTGRES_DB,
POSTGRES_USER, and POSTGRES_PASSWORD.
For Supabase, set POSTGRES_URI to the Supabase Postgres connection string and use POSTGRES_SSL=true. To verify Supabase Auth tokens at the OpenDot API boundary, set:
SUPABASE_URL enables JWKS verification for current Supabase signing keys. Set SUPABASE_JWT_SECRET only if the project still uses legacy HS256 JWT signing.
When a Bearer token is provided, the API maps the Supabase JWT sub to app_users.id. The OSS core is intentionally single-workspace and does not rely on Supabase user_metadata for authorization-critical product state. For local Compose without Supabase, leave OPENDOT_LOCAL_AUTH_DISABLED=false and use the OpenDot auth page.
Database inspection
Use Drizzle Studio to inspect the current schema and persisted platform data:https://local.drizzle.studio. The local Compose database uses:
Required provider keys
Set provider keys in the root.env:
OPENAI_BASE_URL is optional. Leave it blank for OpenAI, or set it to the
base of an OpenAI-compatible API such as https://example.com/v1. Agent-level
LLM settings can also override the base URL and API key environment variable
name from the Configuration page.
Sandwich Browser Test and Sandwich Dot sessions need DEEPGRAM_API_KEY plus the
configured LLM key. Speech-to-speech Browser Test and Speech-to-speech Dot
sessions use OPENAI_API_KEY inside the runtime; Deepgram is not required for
those Realtime sessions. The platform API mints a one-use realtime
browser-session token at POST /api/runtime/realtime-browser-sessions, the
runtime exchanges it for an OpenAI Realtime client secret at
/realtime/client-secret, and the browser uses that short-lived client secret
for the WebRTC call. Dot devices never receive an OpenAI key or client secret;
the runtime opens the server-side OpenAI Realtime WebSocket on their behalf.
The selected LLM Provider API controls which path the runtime calls:
Responses appends /responses, while Chat Completions appends
/chat/completions.
Frontend runtime URL
The browser test uses this WebSocket by default:VITE_RUNTIME_HTTP_URL is not set, the frontend derives the HTTP base from VITE_RUNTIME_WS_URL.
Speech-to-speech Browser Test gets its runtime HTTP URL from the platform API
response, which is built from OPENDOT_RUNTIME_PUBLIC_HTTP_URL.
Runtime/API trust
The runtime calls internal platform API endpoints to verify/voice,
/realtime/client-secret, and /ws flows and to proxy firmware activation:
OPENDOT_RUNTIME_INTERNAL_SECRET on the platform API and runtime
services. In hosted deployments, set the public runtime URLs to the externally
reachable runtime service so browser session tokens, realtime client-secret
exchange, and device OTA config point at the right host.
Turn-close defaults
These local defaults keep device turns balanced while reducing wait time:CLOSE_DEVICE_AFTER_TURN is enabled, the runtime closes the device audio
socket shortly after a completed Sandwich or Speech-to-speech turn so firmware
can return to idle or wake-word mode.
TTS defaults
The runtime TTS sample rate defaults to:- Encoding
- Sample rate
- Browser delivery
- Chunk style