Some checks failed
Forgejo Docker Build / Build Docker image (push) Blocked by required conditions
Forgejo Docker Build / Deploy to the host (push) Blocked by required conditions
Forgejo Android APK / Root app tests (push) Successful in 50s
Forgejo Docker Build / Root app tests (push) Successful in 48s
Forgejo Android APK / Build signed APK (push) Has been cancelled
Nothing documented My Resources, the slide renderer, PubMed or web search, and the authentication doc predated both sign-in codes and registration invitations. docs/my-resources.md is new and covers the feature end to end: what a resource is, where its material comes from, why both searches run in the route rather than as tools the model never called, why keyword engines get the topic while retrieval gets the instruction too, how a presentation is designed as a deck rather than written as markdown, the separate multi-image path, and what the export pipeline is made of. docs/authentication.md gains sign-in codes — storage, lifetime, reuse, supersession, guessing, and that two-factor still applies — and registration invitations, including the exact condition that decides when a code may be deleted and why it is written to match the status the list displays. Both new rate limits are in the table, with a note that Express matches app.use paths on segment boundaries, so a new sign-in endpoint needs its own limiter or it has none at all. docs/deployment.md now says what the runtime image carries and why — pandoc for Word, python3 with apk-installed lxml and pillow for the slide renderer, python-pptx pinned, and that PDF conversion is not in the image at all but goes to Gotenberg, so Word and PowerPoint still work when it is down. docs/configuration.md picks up LOGIN_RATE_LIMIT_MAX, LOGIN_CODE_RATE_LIMIT_MAX and GOTENBERG_URL, none of which were listed. README gains a My Resources section and indexes the two new docs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
209 lines
9.2 KiB
Markdown
209 lines
9.2 KiB
Markdown
# 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. |
|
|
| `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`. If unset, the startup loader uses configured credentials and the last initialized provider in Bedrock → Azure → Vertex → LiteLLM order wins; otherwise OpenRouter is the default. |
|
|
| `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. |
|
|
| `GOOGLE_VERTEX_PROJECT`, `GOOGLE_VERTEX_LOCATION`, `GOOGLE_APPLICATION_CREDENTIALS` | Vertex AI chat provider. |
|
|
| `LITELLM_API_BASE`, `LITELLM_API_KEY` | OpenAI-compatible AI gateway (Bifrost, LiteLLM, or similar). |
|
|
|
|
### Speech-to-text
|
|
|
|
| Variable | Purpose |
|
|
|---|---|
|
|
| `TRANSCRIBE_PROVIDER` | Use `litellm`; auto mode uses LiteLLM when configured. |
|
|
| `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_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. |
|
|
| `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 |
|
|
|---|---|
|
|
| `NEXTCLOUD_URL` | Nextcloud base URL (per-user credentials entered in app). |
|
|
| `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_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
|
|
|
|
1. **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.
|
|
|
|
2. **Set the API key** — `LITELLM_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 → Settings → `stt.model` (speech-to-text)
|
|
- Admin Panel → Settings → `tts.model` (text-to-speech)
|
|
- `LITELLM_TTS_MODEL` env var (if set)
|
|
|
|
4. **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.
|
|
|
|
5. **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` |
|
|
|
|
## 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](retrieval-tuning.md).
|