Every service reads a gitignored env file. Copy the checked-in example and fill the blanks. setup.sh does this for you.
Local Supabase values come from supabase status (or the Compose stack).

Backend (backend/.env)

string
default:"http://127.0.0.1:54321"
required
Supabase REST / Realtime endpoint.
string
required
Service-role key. Full DB access. Backend only — never put this in a NEXT_PUBLIC_* var.
string
Master key for the credential store. Unset → keys fall back to plaintext on the agent row. Generate with python -m credentials.keygen.
string
default:"8000"
WebSocket / HTTP port.
string
default:"INFO"
DEBUG | INFO | WARNING | ERROR.
Optional (safe defaults if omitted):

Frontend (frontend/.env.local)

string
default:"http://127.0.0.1:54321"
required
Browser Supabase client. Inlined into the bundle.
string
required
Public anon JWT (RLS-gated). Safe to expose to the browser.
string
default:"ws://localhost:8000/ws"
Backend WebSocket. Set this if the API is not on localhost:8000.
Server-only (no NEXT_PUBLIC_ prefix — used by /api/realtime/token):
NEXT_PUBLIC_* values ship to the browser. The service-role key is not one of them.

Compose (repo-root .env)

Required: SUPABASE_SERVICE_ROLE_KEY, SUPABASE_ANON_KEY. See Docker Compose.