feat: initial commit — Curio mastery tutor

Full Next.js App Router application with:
- AI-graded lesson checkpoints (BKT/Elo mastery tracking)
- Auth.js v5: credentials, Google, GitHub, generic OIDC
- Anonymous-session-first with migrate-on-signin
- Admin panel: users, blueprints, reports, site settings
- Password reset + email verification (nodemailer/SMTP)
- Site config: require_auth + signups_enabled flags
- server-only guards on all DB/generation/verification modules
- PostgreSQL 16 + pgvector, Redis cache, Drizzle ORM
- 271 unit tests (Vitest), golden-eval harness, Playwright e2e stubs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-21 22:25:43 +02:00
commit 4e38b5a791
194 changed files with 30789 additions and 0 deletions
+107
View File
@@ -0,0 +1,107 @@
DATABASE_URL=postgresql://curio:curio@localhost:5433/curio
# Auth.js v5
AUTH_SECRET= # openssl rand -hex 32
AUTH_URL=http://localhost:3000
AUTH_GOOGLE_ID=
AUTH_GOOGLE_SECRET=
AUTH_GITHUB_ID=
AUTH_GITHUB_SECRET=
# Authentik / generic OIDC (optional — any OIDC-compliant IdP: Keycloak, Dex, etc.)
# Leave AUTH_OIDC_ISSUER blank to disable the SSO button entirely.
AUTH_OIDC_ISSUER= # e.g. https://authentik.example.com/application/o/curio/
AUTH_OIDC_CLIENT_ID=
AUTH_OIDC_CLIENT_SECRET=
AUTH_OIDC_DISPLAY_NAME=SSO # Label shown on the login button
# Email verification (optional — nodemailer)
SMTP_HOST=smtp.eu.mailgun.org
SMTP_PORT=587
SMTP_USER=curio@arnaudne.fr
SMTP_PASS=1e314233c1b2c72b89dee03622bde622-0b3150f2-43f373c4
EMAIL_FROM=curio@arnaudne.fr
# Langfuse — LLM call observability (optional; logs to console when absent)
LANGFUSE_SECRET_KEY=
LANGFUSE_PUBLIC_KEY=
LANGFUSE_HOST=https://cloud.langfuse.com
# Per-session output token cap (~50k ≈ 34 full lessons of grading + probes)
SESSION_TOKEN_BUDGET=50000
REDIS_URL=redis://localhost:6379
# Upstash Redis (REST API) — cold-start rate limiter; optional, degrades gracefully
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=
# Generator model — strong, creative (defaults to Anthropic Claude)
LLM_GENERATOR_PROVIDER=anthropic
LLM_GENERATOR_MODEL=claude-sonnet-4-6
ANTHROPIC_API_KEY=
# Grader model — different provider for decorrelation (defaults to OpenAI)
LLM_GRADER_PROVIDER=openai
LLM_GRADER_MODEL=gpt-4o-mini
OPENAI_API_KEY=
# Cheap grader — fast/low-cost model for rubric-matched majority cases (§10.2 cascade)
# Falls back to LLM_GRADER_* when not set (no performance benefit, but safe default)
LLM_CHEAP_GRADER_PROVIDER=openai
LLM_CHEAP_GRADER_MODEL=gpt-4o-mini
# Safety mode: open | controlled (default) | enterprise-safe (§14)
CONTENT_SAFETY_MODE=controlled
# Embedding model — used for RAG retrieval (defaults to OpenAI)
LLM_EMBED_PROVIDER=openai
LLM_EMBED_MODEL=text-embedding-3-small
# ── Google Gemini (provider=google) ──────────────────────────────────────────
# Models: gemini-2.0-flash, gemini-2.5-pro, etc. Supports embeddings.
GOOGLE_GENERATIVE_AI_API_KEY=
# ── Mistral (provider=mistral) ────────────────────────────────────────────────
# Models: mistral-large-latest, mistral-small-latest. Supports embeddings.
MISTRAL_API_KEY=
# ── Cohere (provider=cohere) ──────────────────────────────────────────────────
# Models: command-r-plus, command-r. Supports embeddings (embed-english-v3.0).
COHERE_API_KEY=
# ── Groq (provider=groq) ──────────────────────────────────────────────────────
# Models: llama-3.3-70b-versatile, mixtral-8x7b-32768. No embeddings.
GROQ_API_KEY=
# ── xAI / Grok (provider=xai) ────────────────────────────────────────────────
# Models: grok-3, grok-3-mini. No embeddings.
XAI_API_KEY=
# ── AWS Bedrock (provider=bedrock) ────────────────────────────────────────────
# Models: anthropic.claude-sonnet-4-6-v1, amazon.titan-embed-text-v2:0, etc.
# Uses AWS SDK credential chain (env vars, ~/.aws/credentials, IAM role).
AWS_REGION=us-east-1
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
# ── Azure OpenAI (provider=azure) ─────────────────────────────────────────────
# model = deployment name (not model name). Supports embeddings.
AZURE_OPENAI_RESOURCE_NAME=
AZURE_API_KEY=
# ── OpenRouter (provider=openrouter) ─────────────────────────────────────────
# Routes to any model. No embeddings — keep LLM_EMBED_PROVIDER=openai.
# Model IDs: "anthropic/claude-sonnet-4-6", "google/gemini-2.0-flash", etc.
OPENROUTER_API_KEY=
# ── Ollama (provider=ollama) ──────────────────────────────────────────────────
# Local. No API key. Supports embeddings (nomic-embed-text).
OLLAMA_BASE_URL=http://localhost:11434/api
# ── Fully local example (no API keys) ────────────────────────────────────────
# LLM_GENERATOR_PROVIDER=ollama
# LLM_GENERATOR_MODEL=llama3.2
# LLM_GRADER_PROVIDER=ollama
# LLM_GRADER_MODEL=llama3.2
# LLM_EMBED_PROVIDER=ollama
# LLM_EMBED_MODEL=nomic-embed-text