Configuration
Backend configuration happens through environment variables — exported in
your shell before docker compose up, set in a compose override file, or
placed in app/.env (loaded by the backend container; note that anything in
the compose file’s environment: block wins over app/.env). The frontend
reads VITE_* variables from frontend/.env.local at build/dev time.
Backend
Section titled “Backend”| Variable | Default | Purpose |
|---|---|---|
BACKEND_PORT |
8000 |
Host port the API is published on |
CORS_ORIGINS |
http://localhost:5173,http://127.0.0.1:5173 |
Comma-separated origins allowed to call the API from a browser |
SECRET_KEY |
secret |
JWT signing key — change this for anything beyond local evaluation |
FRONTEND_URL |
http://localhost:5173 |
Where the backend sends users back to after Google OAuth — set it to your dashboard’s real address |
MAX_FILE_SIZE |
50 MB | Per-file ingestion size limit |
| Variable | Purpose |
|---|---|
CLERK_PUBLISHABLE_KEY |
Your Clerk application’s publishable key |
CLERK_SECRET_KEY |
Clerk secret key — required for social sign-in session exchange |
CLERK_WEBHOOK_SECRET |
Signing secret for the /webhooks/clerk endpoint, if you wire Clerk webhooks up |
CLERK_AUTHORIZED_PARTIES |
Origins allowed in Clerk tokens; defaults to CORS_ORIGINS + FRONTEND_URL |
Leave these empty and social sign-in is simply off — email/password accounts
work without any of them. (The frontend still needs its
VITE_CLERK_PUBLISHABLE_KEY either way; see below.)
Retrieval tuning
Section titled “Retrieval tuning”| Variable | Default | Purpose |
|---|---|---|
TOP_K_PER_COLLECTION |
8 |
How many chunks to retrieve from each collection |
SIMILARITY_THRESHOLD |
0.7 |
Minimum similarity for a chunk to be considered |
MAX_CONTEXT_LENGTH |
12000 |
Cap on retrieved context, in characters |
LLM_NUM_CTX |
8192 |
The model’s context window, in tokens |
RAG_TIMEOUT |
10 |
Seconds to wait for retrieval before answering without it |
The two capacity numbers use different units, and they’re coupled:
MAX_CONTEXT_LENGTH counts characters of retrieved text, LLM_NUM_CTX
counts tokens of model context, and the retrieved text must fit inside the
model’s window. If it doesn’t, Ollama silently truncates the front of the
prompt — retrieval quietly shrinks with no error anywhere. That’s also the
tinyllama catch: tinyllama only has a 2048-token window, so if you’re
evaluating with it, lower LLM_NUM_CTX to 2048 and MAX_CONTEXT_LENGTH to
match (roughly 4 characters per token).
If you serve the dashboard from anywhere other than localhost:5173 — a
different port, another machine, or a real domain — add that origin:
services: backend: environment: - CORS_ORIGINS=https://insight.example.com,http://localhost:5173Browser requests from unlisted origins will fail even though curl works.
Frontend
Section titled “Frontend”| Variable | Purpose |
|---|---|
VITE_BACKEND_URL |
API base the browser should call — http://localhost:8000/api/v1 in dev, or the relative /api/v1 when nginx proxies same-origin |
VITE_CLERK_PUBLISHABLE_KEY |
Required — the dashboard refuses to start without it |
VITE_GOOGLE_PICKER_API_KEY |
Google API key for the Drive file picker |
VITE_GOOGLE_PICKER_APP_ID |
Google Cloud project number, for the same picker |
VITE_CHAT_STREAMING |
Set false to fall back to non-streaming chat (defaults on) |
VITE_DEBUG_MODE |
Set true to expose debug details (e.g. chunk indexes and similarity scores in citations) |
Put them in frontend/.env.local (template: frontend/.env.example).
Vite bakes them in at build/dev time — rebuild after changing them.
The credential encryption key
Section titled “The credential encryption key”Data-source credentials and Google grants are stored encrypted. The key
lives in app/.encrypted.bin, created on first boot by the init-kek
compose service. Two rules:
- Back the file up. Without it, every stored credential is unreadable — the API starts returning “Stored credentials could not be read” and affected sources must be recreated.
- Don’t skip
init-kek. Starting the backend with--no-deps(or otherwise preventinginit-kekfrom running on a fresh checkout) gets you “Encryption is misconfigured” errors instead of a working instance.
GPU acceleration
Section titled “GPU acceleration”Install the NVIDIA Container Toolkit so the Ollama container can use your GPU. Without it the stack still works — inference just runs on CPU.
Google Drive (OAuth)
Section titled “Google Drive (OAuth)”Google Drive connectivity takes two sets of credentials from one Google Cloud project (create one in the Google Cloud console, with the Drive API and Google Picker API enabled):
- A backend OAuth web client. Create OAuth credentials, authorize your
instance’s redirect URI (the value of
GOOGLE_OAUTH_REDIRECT_URI, e.g.https://insight.example.com/oauth/callback), and place the downloaded JSON atapp/secrets/gdrive_secret.json. The consent screen needs three scopes:openid,email, anddrive.file— the identity scopes are what lets Settings show which Google account is connected. - Picker keys for the frontend. Set
VITE_GOOGLE_PICKER_API_KEY(an API key) andVITE_GOOGLE_PICKER_APP_ID(the project number). Without them the Browse Drive files picker can’t open — and since thedrive.filescope only grants files the user has picked, that means no Drive file can be selected at all.
Also set FRONTEND_URL on the backend so the post-consent redirect lands on
your dashboard rather than localhost. If nginx sits in front, make sure
/oauth/ is proxied to the backend — Google’s callback arrives there, not
under /api/.
Running multiple instances
Section titled “Running multiple instances”Docker Compose project names isolate complete environments (separate databases, volumes, and networks) on one machine — useful for teams or for a staging copy:
export BACKEND_PORT=8001 SAMBA_PORT=1446docker compose -p alice up -d
# elsewhereexport BACKEND_PORT=8002 SAMBA_PORT=1447docker compose -p bob up -dManage each with the same -p flag: docker compose -p alice ps,
docker compose -p alice down, etc.
The bundled Samba test server
Section titled “The bundled Samba test server”The samba service is a small SMB server for development and testing.
Shares live in samba/shares/ on the host — drop files there and they’re
visible to pipelines through a data source pointed at it (credentials
testuser/testpass). The credentials are baked into the image at build
time, so changing them means editing samba/Dockerfile and rebuilding.
