The clinical-assist indexer no longer polls Nextcloud every five minutes; it scans once at start and then on POST /api/v1/vector-sync/scan with a bearer token. The admin panel shows what it reports and carries the button; the address and token are settings (or the environment). The button stays usable under lockdown, the fields do not. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
303 lines
15 KiB
Text
303 lines
15 KiB
Text
# ============================================================
|
|
# OPENBAO (optional — recommended for production)
|
|
# ============================================================
|
|
# When these three are set, the container fetches everything else below
|
|
# from OpenBao at kv/ped-ai/prod and ignores the equivalent .env values.
|
|
# Leave them unset (or blank) to fall back to .env-only (local dev, e2e).
|
|
#
|
|
# OPENBAO_ADDR=https://app.danvics.com
|
|
# OPENBAO_ROLE_ID=<from: bao read auth/approle/role/ped-ai/role-id>
|
|
# OPENBAO_SECRET_ID=<from: bao write -f auth/approle/role/ped-ai/secret-id>
|
|
# OPENBAO_KV_PATH=kv/ped-ai/prod # override path if needed
|
|
|
|
# ============================================================
|
|
# Everything below is sourced from OpenBao when OPENBAO_ADDR is set.
|
|
# Only fill these in for local dev / e2e / when running without vault.
|
|
# ============================================================
|
|
|
|
# ============================================================
|
|
# AI PROVIDER (choose one)
|
|
# ============================================================
|
|
|
|
# Option 1: OpenRouter (default, cheapest, NOT HIPAA)
|
|
AI_PROVIDER=openrouter
|
|
OPENROUTER_API_KEY=sk-or-v1-your-key
|
|
|
|
# Option 2: AWS Bedrock (HIPAA compliant with BAA)
|
|
# AI_PROVIDER=bedrock
|
|
# AWS_BEDROCK_REGION=us-east-1
|
|
# AWS_ACCESS_KEY_ID=AKIA...
|
|
# AWS_SECRET_ACCESS_KEY=...
|
|
# (Or use IAM role — no keys needed if running on EC2/ECS)
|
|
|
|
# Option 3: Azure OpenAI (HIPAA compliant with BAA)
|
|
# AI_PROVIDER=azure
|
|
# AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com
|
|
# AZURE_OPENAI_API_KEY=your-key
|
|
# AZURE_DEPLOYMENT_NAME=gpt-4o-mini
|
|
# AZURE_OPENAI_API_VERSION=2024-02-01
|
|
|
|
# Option 4: Google Vertex AI (HIPAA compliant with BAA)
|
|
# AI_PROVIDER=vertex
|
|
# Google models (Gemini and friends) are reached through LiteLLM, not the
|
|
# Google SDK — that dependency was removed. Configure them on the gateway
|
|
# and select them by their LiteLLM model id.
|
|
# GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
|
|
# (Or use default credentials if running on GCE/GKE/Cloud Run)
|
|
#
|
|
# Google STT — Gemini inline audio (auto-detected when GOOGLE_VERTEX_PROJECT set)
|
|
# TRANSCRIBE_PROVIDER=google
|
|
# GOOGLE_STT_MODEL=gemini-2.0-flash # or gemini-2.5-flash for better accuracy
|
|
#
|
|
# Google TTS — Google Cloud Text-to-Speech (auto-detected when GOOGLE_VERTEX_PROJECT set)
|
|
# TTS_PROVIDER=google
|
|
# GOOGLE_TTS_VOICE=en-US-Journey-F # female | en-US-Journey-D = male
|
|
# Other options: en-US-Studio-O, en-US-Neural2-C, en-US-Neural2-J
|
|
|
|
# Option 5: LiteLLM Proxy (self-hosted, routes to any provider)
|
|
# AI_PROVIDER=litellm
|
|
# LITELLM_API_BASE=http://localhost:4000
|
|
# LITELLM_API_KEY=sk-litellm-your-key
|
|
# Admin can discover available models via the admin panel
|
|
#
|
|
# LiteLLM Speech-to-Text
|
|
# TRANSCRIBE_PROVIDER=litellm
|
|
# LITELLM_STT_MODEL=whisper-1 # Use the model name from your LiteLLM model_list
|
|
# If your LiteLLM config uses full paths as model names, use the full path:
|
|
# LITELLM_STT_MODEL=openai/whisper-1
|
|
# NOTE: vertex_ai/chirp does NOT work via LiteLLM audio proxy.
|
|
# For Vertex AI speech, use TRANSCRIBE_PROVIDER=google (Gemini inline audio).
|
|
#
|
|
# LiteLLM TTS
|
|
# TTS_PROVIDER=litellm (auto-detected when LITELLM_API_BASE set)
|
|
# LITELLM_TTS_MODEL=tts-1 # Use model name from your LiteLLM model_list
|
|
# If your config uses full paths: LITELLM_TTS_MODEL=vertex_ai/google-tts
|
|
# LITELLM_TTS_VOICE=en-US-Journey-F # Google Cloud voice name (or alloy/nova for OpenAI)
|
|
|
|
# ============================================================
|
|
# TRANSCRIPTION (speech-to-text)
|
|
# ============================================================
|
|
|
|
# Option A: OpenAI Whisper (default if no AWS configured)
|
|
OPENAI_API_KEY=sk-your-openai-key
|
|
|
|
# Option B: Amazon Transcribe (HIPAA eligible, no S3 needed)
|
|
# Uses same AWS credentials as Bedrock above.
|
|
# Set TRANSCRIBE_PROVIDER=aws to force AWS even if OPENAI_API_KEY is set.
|
|
# Leave unset to auto-detect (uses AWS when AWS_BEDROCK_REGION is configured).
|
|
# TRANSCRIBE_PROVIDER=aws
|
|
|
|
# Option C: Local Whisper (privacy-first, no cloud API needed)
|
|
# Requires whisper.cpp or faster-whisper installed on the server.
|
|
# TRANSCRIBE_PROVIDER=local
|
|
# WHISPER_MODEL_SIZE=small # tiny, base, small, medium, large
|
|
# WHISPER_BINARY=whisper-cpp # or: whisper, faster-whisper
|
|
# WHISPER_MODEL_PATH= # custom path to .bin model file
|
|
# WHISPER_LANGUAGE=en
|
|
# WHISPER_THREADS=4 # defaults to CPU count - 1
|
|
|
|
# Amazon Transcribe Medical — better accuracy for clinical dictation
|
|
# Knows drug names, diagnoses, procedures, SOAP terminology
|
|
# HIPAA eligible (ensure your AWS account has a BAA)
|
|
# AWS_TRANSCRIBE_MEDICAL=true
|
|
# AWS_TRANSCRIBE_SPECIALTY=PRIMARYCARE
|
|
# Other options: CARDIOLOGY, NEUROLOGY, ONCOLOGY, RADIOLOGY, UROLOGY
|
|
|
|
# Optional
|
|
ELEVENLABS_API_KEY=
|
|
|
|
# Push Notifications (ntfy — self-hosted, optional)
|
|
# NTFY_URL=https://ntfy.yourdomain.com
|
|
# NTFY_TOKEN=tk_your_token_here
|
|
|
|
# App
|
|
PORT=3000
|
|
APP_URL=https://your-domain.com
|
|
|
|
# Cloudflare Turnstile (anti-bot on registration, optional)
|
|
# TURNSTILE_SITE_KEY=your-site-key
|
|
# TURNSTILE_SECRET_KEY=your-secret-key
|
|
JWT_SECRET=generate-a-random-64-char-string-here
|
|
|
|
# Application-layer encryption key for PHI at rest (Nextcloud tokens, audio backups)
|
|
# Generate with: openssl rand -hex 32
|
|
# REQUIRED in production. Rotating invalidates existing encrypted data.
|
|
DATA_ENCRYPTION_KEY=generate-with-openssl-rand-hex-32
|
|
|
|
# Email (for verification & password reset)
|
|
SMTP_HOST=smtp.gmail.com
|
|
SMTP_PORT=587
|
|
SMTP_USER=your-email@gmail.com
|
|
SMTP_PASS=your-app-password
|
|
SMTP_FROM=noreply@yourdomain.com
|
|
|
|
# Nextcloud (optional)
|
|
NEXTCLOUD_URL=https://cloud.yourdomain.com
|
|
|
|
# S3 Document Storage (optional — works with AWS S3, Backblaze B2, MinIO)
|
|
# S3_BUCKET=your-bucket-name
|
|
# S3_REGION=us-east-1
|
|
# S3_PREFIX=documents/
|
|
#
|
|
# For AWS S3: uses same AWS credentials as Bedrock above, or set S3-specific keys:
|
|
# S3_ACCESS_KEY_ID=...
|
|
# S3_SECRET_ACCESS_KEY=...
|
|
#
|
|
# For Backblaze B2:
|
|
# S3_ENDPOINT=https://s3.us-west-004.backblazeb2.com
|
|
# S3_REGION=us-west-004
|
|
# S3_ACCESS_KEY_ID=your-b2-application-key-id
|
|
# S3_SECRET_ACCESS_KEY=your-b2-application-key
|
|
#
|
|
# For MinIO (self-hosted):
|
|
# S3_ENDPOINT=http://minio:9000
|
|
# S3_REGION=us-east-1
|
|
# S3_ACCESS_KEY_ID=minio-access-key
|
|
# S3_SECRET_ACCESS_KEY=minio-secret-key
|
|
# S3_FORCE_PATH_STYLE=true
|
|
|
|
# ============================================================
|
|
# EMBEDDINGS (for Learning Hub semantic search)
|
|
# ============================================================
|
|
# Enables vector-based semantic search in Learning Hub
|
|
# Requires pgvector extension: apt-get install postgresql-16-pgvector
|
|
|
|
# Default model (Vertex AI text-embedding-005, 768 dims, English + code optimized)
|
|
EMBEDDING_MODEL=vertex_ai/text-embedding-005
|
|
EMBEDDING_DIMENSIONS=768
|
|
|
|
# Other Vertex AI embedding models:
|
|
# - vertex_ai/text-embedding-005 → 768 dims, English + code (recommended)
|
|
# - vertex_ai/gemini-embedding-001 → up to 3072 dims, multilingual + code
|
|
# - vertex_ai/text-multilingual-embedding-002 → 768 dims, multilingual focus
|
|
#
|
|
# LiteLLM usage (if using LiteLLM proxy):
|
|
# EMBEDDING_MODEL=text-embedding-005 # LiteLLM will route to configured provider
|
|
#
|
|
# OpenAI fallback (NOT HIPAA-eligible):
|
|
# Uses text-embedding-3-small if OPENAI_API_KEY is set and no Vertex/LiteLLM configured
|
|
|
|
# ============================================================
|
|
# DATABASE
|
|
# ============================================================
|
|
DATABASE_URL=postgresql://pedscribe:<password>@postgres:5432/pedscribe
|
|
DB_PASSWORD=pedscribe_secret_change_me
|
|
|
|
# ── Audio backups (optional) ────────────────────────────────────────────────
|
|
# Every recording is kept for 24 hours, whether its transcription succeeded or
|
|
# not. Unset, the audio lives in the audio_backups table; set, it goes to a
|
|
# bucket instead and only metadata stays in Postgres. The generated-images key
|
|
# is scoped to that bucket and cannot be reused here — create a bucket and a
|
|
# user of its own, and give the bucket a 24h expiry rule as a backstop.
|
|
# Audio is gzipped and AES-256-GCM encrypted before it is stored, either way.
|
|
# AUDIO_BACKUPS_S3_ENDPOINT=http://assets:9000
|
|
# AUDIO_BACKUPS_S3_BUCKET=audio-backups
|
|
# AUDIO_BACKUPS_S3_REGION=us-east-1
|
|
# Prefer the _FILE variants: credentials then stay out of the process environment.
|
|
# AUDIO_BACKUPS_S3_ACCESS_KEY_FILE=/run/secrets/audio-backups-access-key
|
|
# AUDIO_BACKUPS_S3_SECRET_KEY_FILE=/run/secrets/audio-backups-secret-key
|
|
# AUDIO_BACKUPS_S3_ACCESS_KEY=
|
|
# AUDIO_BACKUPS_S3_SECRET_KEY=
|
|
|
|
# ============================================================
|
|
# EVERY REMAINING VARIABLE THE APP READS
|
|
# Completed 2026-09-11: this file listed 18 of the 67 variables src/ actually
|
|
# reads. Each entry below shows the default the code falls back to, so an
|
|
# unset line means "this default", not "broken".
|
|
# ============================================================
|
|
|
|
# ── Mail ────────────────────────────────────────────────────────────────────
|
|
# SMTP_SECURE=false # true for implicit TLS (port 465)
|
|
|
|
# ── Admin lockdown ──────────────────────────────────────────────────────────
|
|
# With several admins, everything in the admin panel is editable by all of
|
|
# them. Set this and configuration becomes read-only in the panel and refused
|
|
# by the server: prompts, model policy, retrieval limits and budgets, TTS/STT,
|
|
# embeddings, SMTP and email templates. Day-to-day operation stays available —
|
|
# announcements, registration and invitations, feature flags, site details.
|
|
# A setting added later is locked until it is deliberately added to the
|
|
# editable list, rather than defaulting to open.
|
|
#
|
|
# It is deliberately an environment variable: a setting could be switched off
|
|
# by the very admin it restrains, so lifting it needs host access and a
|
|
# restart. Read access is unaffected — locked settings stay visible.
|
|
# ADMIN_LOCKDOWN=false
|
|
|
|
# ── Identity and limits ─────────────────────────────────────────────────────
|
|
# SITE_NAME=Pediatric AI Scribe
|
|
# API_RATE_LIMIT_MAX=200 # requests per window across /api
|
|
# LOGIN_RATE_LIMIT_MAX=10 # login attempts per 15 minutes
|
|
# Codes emailed for sign-in, per IP per hour. Lower than the login limit
|
|
# because each request sends mail to somebody else's address (default 5).
|
|
#LOGIN_CODE_RATE_LIMIT_MAX=5
|
|
# NODE_ENV=production # with APP_URL, puts the app in production mode:
|
|
# refuses to start without JWT_SECRET or a CORS origin
|
|
# CORS_ORIGINS= # extra allowed origins, comma-separated, beyond APP_URL
|
|
|
|
# ── Services ────────────────────────────────────────────────────────────────
|
|
# REDIS_URL=redis://ped-ai-redis:6379
|
|
# LOKI_URL=http://monitoring-loki:3100 # log shipping; unset disables it
|
|
# MCP_SERVER_URL= # legacy single MCP URL
|
|
|
|
# ── LiteLLM gateway ─────────────────────────────────────────────────────────
|
|
# LITELLM_MASTER_KEY= # falls back to OPENAI_API_KEY, then 'sk-litellm'
|
|
# LITELLM_DEFAULT_MODEL= # falls back to OPENAI_MODEL
|
|
# LITELLM_FALLBACK_MODEL= # tried when the default model fails
|
|
# OPENAI_MODEL= # legacy name for the default chat model
|
|
# LITELLM_TTS_VOICES= # comma-separated voices offered in the pickers
|
|
|
|
# ── Clinical Assistant: retrieval ───────────────────────────────────────────
|
|
# CLINICAL_ASSISTANT_MCP_URL=http://mcp:8000/mcp
|
|
# The indexer (a separate container) reconciles the library only when the
|
|
# admin panel asks. Address and token can also be set in the admin panel.
|
|
# CLINICAL_ASSISTANT_INDEXER_URL=http://mcp-indexer:8001
|
|
# CLINICAL_ASSISTANT_INDEXER_TOKEN=
|
|
# CLINICAL_ASSISTANT_MCP_URLS= # comma-separated, tried in order
|
|
# CLINICAL_ASSISTANT_SEARCH_TOOL=clinical_semantic_search # the only accepted value
|
|
# CLINICAL_ASSISTANT_MCP_INITIALIZE_TIMEOUT_MS=30000
|
|
# CLINICAL_ASSISTANT_MCP_REQUEST_TIMEOUT_MS=90000
|
|
# CLINICAL_ASSISTANT_MCP_SESSION_TTL_MS=600000
|
|
# CLINICAL_ASSISTANT_MCP_CONCURRENCY=3 # library searches in flight at once; they used to run one at a time
|
|
# CLINICAL_ASSISTANT_MCP_WARMUP= # open a session at boot
|
|
# CLINICAL_ASSISTANT_MCP_WARMUP_DELAY_MS=
|
|
|
|
# ── Clinical Assistant: models and prompts ──────────────────────────────────
|
|
# CLINICAL_ASSISTANT_IMAGE_MODEL=openai-gpt-image-1
|
|
# CLINICAL_ASSISTANT_PROMPT_MODEL= # model that writes the starter prompts
|
|
# CLINICAL_ASSISTANT_PROMPT_POOL_TARGET=1000
|
|
# CLINICAL_ASSISTANT_PROMPT_POOL_KEY=clinical-assistant:prompt-pool:v2
|
|
# CLINICAL_ASSISTANT_PROMPT_POOL_REFRESH_MS=
|
|
# CLINICAL_ASSISTANT_PROMPT_POOL_WARMUP_DELAY_MS=
|
|
# CLINICAL_ASSISTANT_EXAMPLE_CACHE_MS=600000
|
|
# PATIENT_TAKEHOME_BEHAVIOR= # overrides the admin-editable take-home guidance
|
|
|
|
# ============================================================
|
|
# OPENBAO (optional)
|
|
# The entrypoint fetches secrets from OpenBao only when OPENBAO_ADDR is set;
|
|
# unset, it uses this file and nothing else. A value set here or in the compose
|
|
# environment always wins over the OpenBao copy, so an override needs no
|
|
# OpenBao change.
|
|
#
|
|
# To use OpenBao: set OPENBAO_ADDR, OPENBAO_ROLE_ID, OPENBAO_SECRET_ID
|
|
# (and OPENBAO_KV_PATH if not kv/ped-ai/prod)
|
|
# To stop using it: unset OPENBAO_ADDR, and put the keys it held in this
|
|
# file. List them without printing values:
|
|
# bao kv get -format=json kv/ped-ai/prod | jq 'keys'
|
|
# ============================================================
|
|
# OPENBAO_ADDR=
|
|
# OPENBAO_ROLE_ID=
|
|
# OPENBAO_SECRET_ID=
|
|
# OPENBAO_KV_PATH=kv/ped-ai/prod
|
|
|
|
# Optional. Lets something outside this host scrape /metrics with
|
|
# `Authorization: Bearer <token>`. Unset by default, and an unset token can
|
|
# never match — Prometheus scrapes pediatric-ai-scribe:3000 directly over the
|
|
# Docker network, which needs no token. Requests arriving through the reverse
|
|
# proxy (they carry X-Forwarded-For) get a 404 either way.
|
|
METRICS_TOKEN=
|
|
|
|
# Gotenberg (LibreOffice behind an HTTP API), used to turn a generated deck or
|
|
# document into PDF. Defaults to http://gotenberg:3000 on the danvics_convert
|
|
# network. PDF is the one export allowed to fail: if this is unreachable the
|
|
# PowerPoint and Word downloads still work.
|
|
GOTENBERG_URL=
|