pediatric-ai-scribe-v3/docs/configuration.md
Daniel ceccd18387
Some checks failed
Forgejo Docker Build / Root app tests (push) Successful in 47s
Forgejo Docker Build / Build Docker image (push) Successful in 22s
Forgejo Docker Build / Deploy to the host (push) Failing after 1s
docs: take the Learning Hub out of the docs
The feature was removed; the docs still described it as live. Worst of
it was api-reference.md, which documented nine /api/learning endpoints
and fourteen /api/admin/learning CMS endpoints — routes that answer 404
— plus POST /api/user/webdav-path, whose column was dropped by
migration. Anyone reading them was reading fiction.

Checked against the running system rather than assumed: no learning
table exists, users.webdav_learning_path is gone, generated_image_links
is gone, and no route mounts /api/learning or /api/admin/learning.

Two things that look like Learning Hub and are not, so they stay:

- learningRetrieval.js is live — My Resources uses it. Its settings keep
  the learning.* names because renaming them would orphan whatever an
  administrator has already set. retrieval-tuning.md now says so instead
  of listing the rows under two different feature names.
- the moderator role is still assignable. It gated the CMS and now
  grants nothing; authentication.md says that rather than implying
  powers it does not have. moderatorMiddleware has no callers left,
  which is worth removing on its own.

auth-admin-learning.md is now auth-admin.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
2026-09-12 22:24:29 +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 and My Resources each receive, and the reranker cap that overrides all three: retrieval-tuning.md.