It documented 18 of 67, so anyone setting up a deployment had to find the rest by reading source — including CORS_ORIGINS, REDIS_URL, NODE_ENV and the whole Clinical Assistant retrieval group. Each entry now shows the default the code falls back to, so an unset line reads as "this default" rather than "broken". Also documents the OpenBao switch: the entrypoint only fetches secrets when OPENBAO_ADDR is set, a compose or env_file value always beats the OpenBao copy, and turning it off means unsetting that one variable and putting the keys here — with the command to list the key names without printing values. A test now derives the set of variables from source, so this cannot drift again. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
267 lines
13 KiB
Text
267 lines
13 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_VERTEX_PROJECT=your-gcp-project-id
|
|
# GOOGLE_VERTEX_LOCATION=us-central1
|
|
# 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)
|
|
|
|
# ── 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
|
|
# 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
|
|
# 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_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
|