pediatric-ai-scribe-v3/docs/configuration.md
Daniel a505244b97
Some checks failed
Forgejo Android APK / Root app tests (push) Successful in 47s
Forgejo Docker Build / Root app tests (push) Successful in 45s
Forgejo Android APK / Build signed APK (push) Successful in 2m8s
Forgejo Docker Build / Build Docker image (push) Successful in 10s
Forgejo Docker Build / Deploy to the host (push) Failing after 0s
refactor: remove the embedding settings, whose only consumer is gone
Embeddings existed here for Learning Hub semantic search — the card said
so itself. Learning Hub was removed, and nothing took its place: the
clinical corpus is embedded by the indexing service, not by this app.
What was left was a settings page that configured a model, tested it,
reported its dimensions, and fed nothing.

src/utils/embeddings.js had exactly one importer, src/routes/adminConfig
.js, which used it for the three routes this deletes. Outside those, the
only mentions of embedding in the server were a comment and a settings
prefix.

Gone: the module, its three admin routes, the dimension probe, the
Discover & test kind and its two panels, the admin.js block behind them,
the embeddings. prefix from both the writable-settings allowlist and the
lockdown list (it can no longer be written at all, so locking it says
nothing), and docs/embeddings-setup.md, which documented Learning Hub
search end to end.

'embedding' stays in NON_CHAT_MODES — that is the filter keeping
embedding models out of the chat-model list, and the gateway still
serves them.

Docs still describe nine /api/learning endpoints that no longer exist,
left from the Learning Hub removal. Not touched here; that is its own
subject.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
2026-09-12 21:47:09 +02:00

11 KiB

Configuration

Runtime configuration sources, in override order (later wins for overlapping keys):

  1. .env file / container environment variables (startup only)
  2. 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.
AUDIO_BACKUPS_S3_ENDPOINT, _BUCKET, _REGION Optional. Keeps the 24-hour recording copies in object storage instead of the audio_backups column; metadata stays in Postgres either way. See docs/speech.md.
AUDIO_BACKUPS_S3_ACCESS_KEY_FILE, _SECRET_KEY_FILE Credentials for the above, read from files so they never appear in the process environment. AUDIO_BACKUPS_S3_ACCESS_KEY / _SECRET_KEY are the inline fallback.
DB_PASSWORD / DATABASE_URL Postgres password or full connection string.
DB_HOST, DB_PORT, DB_USER, DB_NAME Read by scripts/maintenance.js when DATABASE_URL is not set (defaults postgres / 5432 / pedscribe / pedscribe). The app itself takes its connection from DATABASE_URL / DB_PASSWORD.
REDIS_URL Redis for operational cache and prompt suggestions. Absent means those features degrade, not that the app fails.
SITE_NAME Name used in emails and page titles (default Pediatric AI Scribe).
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 litellm / openrouter / bedrock / azure. If unset, the default is litellm when LITELLM_API_BASE is set, otherwise openrouter; the startup loader then lets the last initialized non-OpenRouter client win. vertex is not a provider any more — it logs an advisory and falls back to OpenRouter. Reach Gemini through LiteLLM.
OPENROUTER_API_KEY OpenRouter key (not HIPAA-eligible).
AWS_BEDROCK_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY Bedrock chat provider.
AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_KEY, AZURE_DEPLOYMENT_NAME, AZURE_OPENAI_API_VERSION Azure OpenAI.
LITELLM_API_BASE, LITELLM_API_KEY OpenAI-compatible AI gateway (Bifrost, LiteLLM, or similar). LITELLM_MASTER_KEY then OPENAI_API_KEY are tried if LITELLM_API_KEY is unset.
LITELLM_DEFAULT_MODEL Default chat model for the LiteLLM provider; falls back to OPENAI_MODEL.
LITELLM_FALLBACK_MODEL Fallback model within LiteLLM. There is no implicit cross-provider fallback.

Speech-to-text

Variable Purpose
TRANSCRIBE_PROVIDER Use litellm; auto mode uses LiteLLM when configured.
TTS_PROVIDER Same idea for speech output; litellm is the supported value.
LITELLM_STT_MODEL Model name for LiteLLM-routed STT.

Text-to-speech

Variable Purpose
LITELLM_TTS_MODEL, LITELLM_TTS_VOICE LiteLLM-routed TTS model and default voice.
LITELLM_TTS_VOICES Comma-separated LiteLLM-compatible voices exposed in voice search and user preferences.

Embeddings

Variable Purpose
EMBEDDING_MODEL LiteLLM embedding model name (default openai-text-embedding-3-large).
EMBEDDING_DIMENSIONS Vector dimensions (default 3072).

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_SECRET_KEY Cloudflare Turnstile. The verification is a no-op when this is unset. The matching site key is currently hardcoded in public/index.html (data-sitekey on the register and forgot-password widgets), not read from the environment — a different deployment has to edit that file.
LOKI_URL Optional Loki ingest URL for shipping audit/api/access logs.
NTFY_URL, NTFY_TOKEN Optional ntfy push for new-login / password-change notifications. The topic is not configurable — src/utils/notify.js derives it per user as pedscribe-{userId}.
METRICS_TOKEN Lets /metrics be scraped from off-host with Authorization: Bearer <token>. Without it, any request carrying X-Forwarded-For gets a 404, so only same-host scrapes succeed.
API_RATE_LIMIT_MAX General request ceiling per IP per minute (default 200).
LOGIN_RATE_LIMIT_MAX Sign-in attempts per IP per 15 min (default 10). Raised in the e2e stack so multi-worker Playwright runs do not trip it.
LOGIN_CODE_RATE_LIMIT_MAX Emailed sign-in codes per IP per hour (default 5). Lower than the sign-in limit because each request sends mail to somebody else's address. See docs/authentication.md.

Integrations

Variable Purpose
GOTENBERG_URL Document conversion service for PDF export (default http://gotenberg:3000). PowerPoint and Word are produced in-process and keep working when this is unreachable; only PDF fails.
S3_BUCKET, S3_REGION, S3_PREFIX, S3_ENDPOINT, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_FORCE_PATH_STYLE Document object storage (S3_PREFIX defaults to documents/). S3_FORCE_PATH_STYLE=true for MinIO, Backblaze B2, most non-AWS providers.

Object storage is resolved per purpose — documents, generated-images, audio-backups — by src/utils/objectStorage.js: the purpose's own variables first, then the shared S3_* ones, then a per-purpose bucket name. Any credential also accepts a _FILE suffix naming a file to read it from, and the _FILE form always wins. Full rules in speech.md.

Nextcloud has no environment variables: each user enters their own URL, username and token in Settings, and those are stored encrypted on the users row.

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.

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

  1. Set the base URLLITELLM_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.

  2. Set the API keyLITELLM_API_KEY accepts any key format the gateway issues (virtual keys, bearer tokens, etc.).

  3. Update model names — Different gateways use different naming conventions. Bifrost requires provider/model format (e.g., openrouter/gpt-4.1), while LiteLLM can use deployment aliases (e.g., openrouter-gpt-4.1). Update model names in:

    • Admin Panel → Models (chat models)
    • Admin Panel → Models → Discover & test → Speech / Transcription (Set makes a model the default)
    • LITELLM_TTS_MODEL env var (if set)
  4. Restart the containerdocker 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

Retrieval sizing

How many corpus excerpts the Clinical Assistant, the Learning Hub and My Resources each receive, and the reranker cap that overrides all three: retrieval-tuning.md.