> ## Documentation Index
> Fetch the complete documentation index at: https://docs.opendot.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Runtime configuration

> Environment variables and local runtime behavior for voice sessions and devices.

The runtime process handles live Sandwich voice sessions for the browser and
Dot devices, browser Speech-to-speech client-secret exchange, and the
Speech-to-speech Dot device bridge.

The runtime verifies browser voice-session tokens, realtime browser-session
tokens, and device credentials with the platform API before accepting `/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:

```bash theme={null}
cp .env.example .env
```

For local pnpm runs, leave `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:

```bash theme={null}
SUPABASE_URL=https://your-project-ref.supabase.co
VITE_SUPABASE_URL=https://your-project-ref.supabase.co
VITE_SUPABASE_ANON_KEY=your-supabase-anon-key
PLATFORM_AUTH_REQUIRED=true
OPENDOT_LOCAL_AUTH_DISABLED=true
```

`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:

```bash theme={null}
pnpm --filter ./platform run db:studio
```

Open `https://local.drizzle.studio`. The local Compose database uses:

```bash theme={null}
POSTGRES_PORT=5432
POSTGRES_SSL=false
```

If only the database is needed, start it with:

```bash theme={null}
docker compose up -d postgres migrate
```

## Required provider keys

Set provider keys in the root `.env`:

```bash theme={null}
DEEPGRAM_API_KEY=...
OPENAI_API_KEY=...
OPENAI_BASE_URL=
OPENAI_MODEL=gpt-5-mini
OPENAI_MAX_OUTPUT_TOKENS=512
OPENAI_REASONING_EFFORT=low
OPENAI_VERBOSITY=low
```

`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:

```text theme={null}
ws://localhost:8787/voice
```

Override it for the frontend with:

```bash theme={null}
VITE_RUNTIME_WS_URL=ws://localhost:8787/voice
```

The Dot Device page can use an explicit HTTP base URL:

```bash theme={null}
VITE_RUNTIME_HTTP_URL=http://localhost:8787
```

If `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:

```bash theme={null}
OPENDOT_RUNTIME_INTERNAL_SECRET=opendot-local-runtime-internal-secret-change-me
PLATFORM_API_INTERNAL_URL=http://localhost:8788/api
OPENDOT_RUNTIME_PUBLIC_HTTP_URL=http://localhost:8787
OPENDOT_RUNTIME_PUBLIC_WS_URL=ws://localhost:8787/voice
```

Use the same `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:

```bash theme={null}
DEEPGRAM_ENDPOINTING_MS=300
DEEPGRAM_UTTERANCE_END_MS=1000
MIN_TRANSCRIPT_CHARS=2
CLOSE_DEVICE_AFTER_TURN=true
CLOSE_DEVICE_AFTER_TURN_DELAY_MS=300
```

When `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:

```bash theme={null}
DEEPGRAM_TTS_SAMPLE_RATE=24000
```

For browser playback experiments, tune the TTS stage in **Configuration**:

* **Encoding**
* **Sample rate**
* **Browser delivery**
* **Chunk style**
