pdf-quiz-generator/backend/.env.example
Daniel c60295e264 fix: thumbnails may be kept, the embedding model may not be changed here
**Caching.** `/uploads` answered `private, no-store` for everything, so a page
of forty thumbnails re-fetched forty images every time it was drawn. A
derivative may now be kept for a week by the browser that asked for it —
`private`, never a shared cache, because a shared cache in front of
access-controlled images is how one learner is served another's figure. It is
safe to keep because it cannot change: `thumbs/256/<key>` is made once from an
immutable original. Originals still say no-store.

**The embedding model is env-only.** Every vector in the database came from it,
and vectors from different models are not comparable — change it and search
returns noise until 3,000 questions, 334 articles and every card have been
re-embedded. The settings page now shows it as text with Test and Regenerate
beside it, and the API refuses a change rather than ignoring one, naming
`LITELLM_EMBEDDING_MODEL` in the refusal.

**The figure audit retries and gives up.** Its second run met a proxy outage and
reported all 327 figures unreadable, having changed nothing but spent the time.
Three tries each with backoff now, and it aborts after twelve consecutive
failures: a run that says "everything is unreadable" has told you nothing.

**`.env.example` is complete.** It listed 23 of the 53 settings; it now lists
all of them, grouped, each with the default it falls back to and — where it
matters — what happens if it is wrong.

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

107 lines
4.9 KiB
Text

# Every setting the backend reads, with the default it falls back to. Blank
# means "off" or "not configured" throughout — nothing here has a secret in it,
# and nothing here is required except the database, the secret key and a model.
# ── Database and sessions ──────────────────────────────────────────────
DATABASE_URL=postgresql://quiz:quiz@postgres:5432/quiz
SECRET_KEY=change-me-to-a-random-secret-key-in-production
ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=1440
# Optional bootstrap admin. Leave blank to use first-user-becomes-admin.
DEFAULT_ADMIN_EMAIL=
DEFAULT_ADMIN_PASSWORD=
# Redis: Celery's broker, the rate limiters, job progress and site settings.
REDIS_URL=redis://redis:6379/0
# ── Models ─────────────────────────────────────────────────────────────
# Everything goes through one LiteLLM proxy. Per-job models (extraction, the
# tutor, TTS, and so on) are chosen by an administrator in Settings → AI
# models; this is the fallback when a job has no model of its own.
LITELLM_MODEL=gpt-4o-mini
LITELLM_API_KEY=your-api-key-here
LITELLM_API_BASE=
# The embedding model, and the one model setting that is NOT editable in the
# interface. Every stored vector was produced by it, and vectors from different
# models are not comparable — change this and search returns noise until every
# question, article and card has been re-embedded. That is a deployment, so it
# lives here. `EMBEDDING_DIMENSIONS` must match what the model returns.
LITELLM_EMBEDDING_MODEL=
EMBEDDING_DIMENSIONS=1024
# The cross-encoder that reorders search results. Unset, unreachable or
# malformed and the fused ranking is returned untouched — never fewer results.
LITELLM_RERANK_MODEL=cohere-rerank-v4.0-pro
# Direct provider keys, used only where the proxy does not carry the service.
OPENAI_API_KEY=
ELEVENLABS_API_KEY=
GOOGLE_TTS_API_KEY=
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_REGION=us-east-1
AWS_BEDROCK_REGION=us-east-1
# Self-hosted speech, for TTS and dictation without leaving the machine.
LOCAL_SPEECH_GATEWAY_URL=http://local-speech-gateway:8110
# ── Where the site lives ───────────────────────────────────────────────
# Used in the links inside emails, so a wrong value sends people nowhere.
APP_URL=https://pedshub.com
LOG_LEVEL=INFO
# ── Storage ────────────────────────────────────────────────────────────
# `local` keeps uploads on the volume at UPLOAD_DIR; `s3` puts them in a
# bucket and reads them back through it. Thumbnails live beside the original
# either way.
STORAGE_BACKEND=local
UPLOAD_DIR=/app/uploads
MAX_UPLOAD_SIZE=524288000
S3_ENDPOINT_URL=http://minio:9000
S3_ACCESS_KEY=
S3_SECRET_KEY=
S3_BUCKET=pedshub-media
S3_REGION=us-east-1
# Page chunks for extraction context.
CHROMA_PERSIST_DIR=/app/chroma_data
# ── Email ──────────────────────────────────────────────────────────────
# With MAIL_USERNAME or MAIL_FROM blank, mail is logged instead of sent — which
# also means sign-in codes and verification links go nowhere.
MAIL_USERNAME=your-email@example.com
MAIL_PASSWORD=your-app-password
MAIL_FROM=your-email@example.com
MAIL_PORT=587
MAIL_SERVER=smtp.gmail.com
MAIL_STARTTLS=true
MAIL_SSL_TLS=false
# Where the contact form's messages land.
ADMIN_EMAIL=
# ── Bot protection ─────────────────────────────────────────────────────
# Cap, self-hosted beside the app. Leave the secret blank to disable the
# challenge; sign-up then accepts submissions without one.
CAP_SECRET_KEY=
CAP_SITE_KEY=
CAP_API_URL=http://cap:3000
# ── Single sign-on ─────────────────────────────────────────────────────
# Any OIDC provider. Blank means the site uses its own accounts only.
OIDC_PROVIDER_URL=
OIDC_CLIENT_ID=
OIDC_CLIENT_SECRET=
OIDC_SCOPES=openid email profile
OIDC_PROVIDER_NAME=SSO
# ── Live sessions ──────────────────────────────────────────────────────
BBB_SERVER_URL=
BBB_SECRET=
# ── Clinical corpus (read-only) ────────────────────────────────────────
# A separate Milvus collection the assistant may query but never write to.
CLINICAL_MILVUS_URI=
CLINICAL_MILVUS_TOKEN=
CLINICAL_MILVUS_COLLECTION=mcp_bge_m3_1024