Configuration
Runtime configuration sources, in override order (later wins for overlapping
keys):
.env file / container environment variables (startup only)
app_settings table (live, editable from Admin Panel with 2-minute cache)
Environment variables
Core (production-required)
| Variable |
Purpose |
APP_URL |
Public base URL. Enables production mode — fail-closed CORS, HSTS, secure cookies. |
JWT_SECRET |
HMAC key for JWT signing and OIDC state. Server refuses to start without it in production. |
DATA_ENCRYPTION_KEY |
AES-256-GCM key for PHI at rest (Nextcloud tokens, audio backups). 64 hex chars (openssl rand -hex 32). Refuses to start without it in production. |
DB_PASSWORD / DATABASE_URL |
Postgres password or full connection string. |
PORT |
HTTP listen port (default 3000). |
NODE_ENV |
production forces prod-only guards on even without APP_URL. |
CORS
| Variable |
Purpose |
CORS_ORIGINS |
Comma-separated additional allowed origins beyond APP_URL. |
AI provider
| Variable |
Purpose |
AI_PROVIDER |
openrouter / bedrock / azure / vertex / litellm. Auto-detected by credential presence if unset. |
OPENROUTER_API_KEY |
OpenRouter key (not HIPAA-eligible). |
AWS_BEDROCK_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY |
Bedrock / Transcribe / Transcribe-Medical. |
AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_KEY, AZURE_DEPLOYMENT_NAME, AZURE_OPENAI_API_VERSION |
Azure OpenAI. |
GOOGLE_VERTEX_PROJECT, GOOGLE_VERTEX_LOCATION, GOOGLE_APPLICATION_CREDENTIALS |
Vertex AI + Gemini (STT/TTS). |
LITELLM_API_BASE, LITELLM_API_KEY |
OpenAI-compatible AI gateway (Bifrost, LiteLLM, or similar). |
Speech-to-text
| Variable |
Purpose |
TRANSCRIBE_PROVIDER |
google, aws, local, openai, litellm. Auto-detects if unset. |
OPENAI_API_KEY |
OpenAI Whisper. |
GOOGLE_STT_MODEL |
Gemini model used as STT (default gemini-2.0-flash). |
AWS_TRANSCRIBE_MEDICAL |
true enables Transcribe Medical. |
AWS_TRANSCRIBE_SPECIALTY |
PRIMARYCARE / CARDIOLOGY / NEUROLOGY / ONCOLOGY / RADIOLOGY / UROLOGY. |
WHISPER_BINARY, WHISPER_MODEL_SIZE, WHISPER_MODEL_PATH, WHISPER_LANGUAGE, WHISPER_THREADS |
Local whisper.cpp / faster-whisper. |
LITELLM_STT_MODEL |
Model name for LiteLLM-routed STT. |
Text-to-speech
| Variable |
Purpose |
GOOGLE_TTS_VOICE |
Google Cloud TTS voice (e.g. en-US-Journey-F). |
ELEVENLABS_API_KEY |
ElevenLabs (not HIPAA-compliant). |
LITELLM_TTS_MODEL, LITELLM_TTS_VOICE |
LiteLLM-routed TTS. |
Embeddings
| Variable |
Purpose |
EMBEDDING_MODEL |
Embedding model name (default text-embedding-005, Vertex). |
EMBEDDING_DIMENSIONS |
Vector dimensions (default 768). |
Email (SMTP)
| Variable |
Purpose |
SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, SMTP_FROM |
SMTP config for verification + password reset emails. Overridable per-instance via app_settings. |
Security / external
| Variable |
Purpose |
TURNSTILE_SITE_KEY, TURNSTILE_SECRET_KEY |
Cloudflare Turnstile. Turnstile check is no-op when secret is unset. |
LOKI_URL |
Optional Loki ingest URL for shipping audit/api/access logs. |
NTFY_URL, NTFY_TOPIC |
Optional ntfy push for new-login / password-change notifications. |
Integrations
| Variable |
Purpose |
NEXTCLOUD_URL |
Nextcloud base URL (per-user credentials entered in app). |
S3_BUCKET, S3_REGION, S3_PREFIX, S3_ENDPOINT, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_FORCE_PATH_STYLE |
Document object storage. S3_FORCE_PATH_STYLE=true for MinIO, Backblaze B2, most non-AWS providers. |
app_settings — live runtime configuration
Key-value rows in the app_settings table. Read via config.get(key, default)
with 2-minute in-memory cache. Writes invalidate the cache immediately.
Registration & site
| Key |
Purpose |
registration_enabled |
true/false. Gate new signups. |
site.name |
Display name. |
site.auto_delete_days |
Days before encounters auto-expire (default 7). |
Announcements
| Key |
Purpose |
announcement.text |
Banner text. Empty = banner hidden. |
announcement.type |
info / warning / error / success. |
SMTP overrides (override env)
smtp.host, smtp.port, smtp.user, smtp.pass, smtp.from.
Email templates
email.{flow}.subject, email.{flow}.body where {flow} is
verify / reset / new_login / password_changed.
OIDC / SSO
| Key |
Purpose |
oidc.enabled |
Toggle SSO. |
oidc.issuer |
OIDC issuer URL. |
oidc.client_id, oidc.client_secret |
OAuth client credentials. |
oidc.button_label |
Login-page button text (default "Sign in with SSO"). |
oidc.disable_local_auth |
Hide local login form when SSO is enabled. |
oidc.allowed_ips |
CIDR whitelist for SSO (optional). |
AI / models / prompts
| Key |
Purpose |
models.default |
Default model ID. |
models.disabled |
JSON array of disabled model IDs. |
models.custom |
JSON array of admin-added models. |
ai.allow_model_fallback |
Enable silent fallback to secondary model on primary failure. Default false — fallback could spill to a non-BAA provider. |
stt.model, tts.model, tts.voice |
System-wide STT/TTS defaults (users can override per-account). |
prompt.{name} |
Prompt overrides. Any template in src/utils/prompts.js can be replaced live. |
embeddings.model, embeddings.dimensions |
Override embedding config. |
Feature flags
feature.* — any key matching this prefix can be consulted via config.get('feature.foo').
Internal migration flags
| Key |
Purpose |
migration.text_indexes_c |
Set to 'true' once lookup-critical text indexes have been converted to COLLATE "C". Prevents re-running. |
Admin panel
The Admin Panel (/admin route, admin-only) exposes everything above plus:
- User list: verify, disable, delete, promote to admin/moderator.
- Session viewer: active sessions per user, admin-revoke.
- Logs: audit / api / access tables with filtering.
- Detailed health:
/api/health/detailed reports configured providers
(admin-only; the public /api/health returns only {ok: true} to avoid
leaking stack info).
- Model management: enable/disable, add custom, set default, discover from
provider.
- Prompt editor: live-edit any
PROMPTS.* key.
- Test SMTP / test STT / test TTS.
Switching AI gateways
The LITELLM_API_BASE and LITELLM_API_KEY variables work with any
OpenAI-compatible gateway — LiteLLM, Bifrost, or other proxies.
Migration steps
-
Set the base URL — LITELLM_API_BASE should include /v1 if the
gateway serves on that path (e.g., https://gateway.example.com/v1).
The application normalizes double /v1 paths internally for TTS, STT,
and embedding endpoints.
-
Set the API key — LITELLM_API_KEY accepts any key format the
gateway issues (virtual keys, bearer tokens, etc.).
-
Update model names — Different gateways use different naming
conventions. Bifrost requires provider/model format
(e.g., openrouter/vendor-model-sonnet-4.6), while LiteLLM uses aliases
(e.g., openrouter-vendor-model-sonnet-4.6). Update model names in:
- Admin Panel → Models (chat models)
- Admin Panel → Settings →
stt.model (speech-to-text)
- Admin Panel → Settings →
tts.model (text-to-speech)
LITELLM_TTS_MODEL env var (if set)
-
Embedding model — Set via Admin Panel → Settings →
embeddings.model. The embedding vector column is VECTOR(768), so
any model producing 768 dimensions works without re-embedding
(e.g., vertex/text-embedding-005). Switching to a model with
different dimensions requires altering the column and re-embedding all
content.
-
Restart the container — docker compose up -d --force-recreate to
pick up .env changes (a plain restart does not re-read .env).
Verified gateways
| Gateway |
Model format |
Notes |
| Bifrost |
provider/model |
Virtual keys, semantic caching, MCP gateway |
| LiteLLM |
Custom aliases |
Requires PostgreSQL + Redis |
| Any OpenAI-compatible |
Varies |
Must serve /v1/chat/completions, /v1/audio/speech, /v1/audio/transcriptions, /v1/embeddings |