| APP_NAME="AI Exam Success API" | |
| ENVIRONMENT="development" | |
| # PostgreSQL target for production/local DB work. | |
| # If DATABASE_URL is not set, the app falls back to a local SQLite file so the | |
| # mock backend can run immediately during early development. | |
| DATABASE_URL="postgresql+psycopg://postgres:postgres@localhost:5432/exam_success" | |
| # Production recovery controls. Development does not require these. | |
| DATABASE_BACKUP_STRATEGY="" | |
| DATABASE_BACKUP_RETENTION_DAYS="14" | |
| DATABASE_RESTORE_TESTED_AT="" | |
| DATABASE_BACKUP_DIR="" | |
| CORS_ORIGINS="http://localhost:3000,http://127.0.0.1:3000,http://localhost:3003,http://127.0.0.1:3003" | |
| UPLOAD_DIR="uploads" | |
| # AI provider settings. | |
| # Use AI_PROVIDER="mock" for local free development without an API key. | |
| # Use AI_PROVIDER="sarvam" with SARVAM_API_KEY for real AI outputs (recommended). | |
| # Use AI_PROVIDER="openrouter" with OPENROUTER_API_KEY for OpenRouter models. | |
| AI_PROVIDER="sarvam" | |
| SARVAM_API_KEY="" | |
| SARVAM_BASE_URL="https://api.sarvam.ai/v1" | |
| SARVAM_MODEL_MAIN="sarvam-30b" | |
| SARVAM_MODEL_HEAVY="sarvam-105b" | |
| SARVAM_MODEL_NOTES="sarvam-30b" | |
| SARVAM_MODEL_QUIZ="sarvam-30b" | |
| SARVAM_MODEL_FLASHCARDS="sarvam-30b" | |
| SARVAM_MODEL_EXAM_MODE="sarvam-105b" | |
| SARVAM_MODEL_PYQ_ANALYSIS="sarvam-105b" | |
| SARVAM_MODEL_VIDEO_SCRIPT="sarvam-105b" | |
| SARVAM_TIMEOUT_SECONDS="90" | |
| SARVAM_MAX_RETRIES="2" | |
| AI_FALLBACK_TO_MOCK="true" | |
| MOCK_AI_MODEL_NAME="mock-exam-tutor-v1" | |
| # Exa is used only when the student explicitly asks to search the web or asks | |
| # for current/latest information. Keep this backend-only; never use NEXT_PUBLIC_. | |
| EXA_API_KEY="" | |
| EXA_BASE_URL="https://api.exa.ai" | |
| EXA_SEARCH_TIMEOUT_SECONDS="15" | |
| EXA_SEARCH_MAX_RESULTS="6" | |
| # Legacy OpenRouter settings (set AI_PROVIDER="openrouter" to use) | |
| OPENROUTER_API_KEY="" | |
| OPENROUTER_BASE_URL="https://openrouter.ai/api/v1" | |
| OPENROUTER_MODEL_MAIN="deepseek/deepseek-v4-flash" | |
| OPENROUTER_MODEL_LLAMA="qwen/qwen3-next-80b-a3b-instruct" | |
| AI_MAX_RETRIES="2" | |
| AI_TIMEOUT_SECONDS="120" | |
| # Learn Anything lesson authoring (OpenAI-compatible chat via Groq). | |
| # Server-side only — never put this in NEXT_PUBLIC_*. | |
| GROQ_API_KEY="" | |
| # Z.AI / GLM free Flash models (text + vision). Server-side only. | |
| # Text: glm-4.7-flash Vision: glm-4.6v-flash | |
| # https://docs.z.ai/ base: https://api.z.ai/api/paas/v4/ | |
| ZAI_API_KEY="" | |
| ZAI_BASE_URL="https://api.z.ai/api/paas/v4" | |
| ZAI_MODEL_TEXT="glm-4.7-flash" | |
| ZAI_MODEL_VISION="glm-4.6v-flash" | |
| ZAI_TIMEOUT_SECONDS="90" | |
| # Scale knobs for ~1k concurrent students (raise in production Postgres deploys). | |
| DATABASE_POOL_SIZE="10" | |
| DATABASE_MAX_OVERFLOW="20" | |
| RATE_LIMIT_ENABLED="true" | |
| RATE_LIMIT_AI_REQUESTS_PER_DAY="80" | |
| # TTS provider settings. | |
| # AI4Bharat Indic Parler is the local Indian-English + Malayalam teacher voice. | |
| # Accept its free Hugging Face access terms once, then set HUGGINGFACE_API_KEY. | |
| # Use TTS_PROVIDER="mock" only for silent timing tests. | |
| TTS_PROVIDER="ai4bharat" | |
| TTS_DEFAULT_PROVIDER="ai4bharat" | |
| VIDEO_TTS_PROVIDER="ai4bharat" | |
| TTS_DEFAULT_VOICE="nila" | |
| TTS_OUTPUT_DIR="public/generated/audio" | |
| AI4BHARAT_TTS_MODEL="ai4bharat/indic-parler-tts" | |
| AI4BHARAT_TTS_SPEAKER="Anjali" | |
| AI4BHARAT_TTS_ENGLISH_SPEAKER="Mary" | |
| AI4BHARAT_TTS_DEVICE="auto" | |
| AI4BHARAT_TTS_PRECISION="float32" | |
| AUDIO_SCENE_TIMEOUT_SECONDS="45" | |
| AUDIO_TOTAL_TIMEOUT_SECONDS="300" | |
| AUDIO_MAX_RETRIES="2" | |
| AUDIO_MAX_CONCURRENCY="3" | |
| KOKORO_VOICE="af_heart" | |
| KOKORO_SPEED="0.95" | |
| INDIC_TTS_MODEL="" | |
| INDIC_TTS_LANGUAGE="ml" | |
| EDGE_TTS_VOICE_EN="en-US-JennyNeural" | |
| EDGE_TTS_VOICE_ML="ml-IN-SobhanaNeural" | |
| EDGE_TTS_RATE="+15%" | |
| ELEVENLABS_API_KEY="" | |
| ELEVENLABS_VOICE_ID="" | |
| AUDIO_NORMALIZE="true" | |
| # Local Remotion final render settings. | |
| GENERATED_VIDEO_JOBS_DIR="generated-video-jobs" | |
| GENERATED_VIDEO_OUTPUT_DIR="generated-videos" | |
| VIDEO_RENDER_TIMEOUT_SECONDS="420" | |
| VIDEO_MIN_OUTPUT_BYTES="4096" | |
| VIDEO_BETA_MAX_SCENES="5" | |
| VIDEO_BETA_MAX_DURATION_SECONDS="90" | |
| VIDEO_SCENE_TEXT_MAX_CHARS="700" | |
| VIDEO_MAX_CONCURRENT_RENDER_JOBS_PER_USER="1" | |
| VIDEO_ENFORCE_BETA_LIMITS_IN_DEVELOPMENT="false" | |
| # Storage provider settings. | |
| # Local generated media is delivered through authenticated /generated routes. | |
| # Use STORAGE_PROVIDER="r2" or "s3" with an S3-compatible bucket for cloud media. | |
| STORAGE_PROVIDER="local" | |
| STORAGE_BUCKET="" | |
| STORAGE_REGION="" | |
| STORAGE_ENDPOINT_URL="" | |
| STORAGE_ACCESS_KEY_ID="" | |
| STORAGE_SECRET_ACCESS_KEY="" | |
| STORAGE_PUBLIC_BASE_URL="" | |
| STORAGE_UPLOAD_PREFIX="uploads" | |
| STORAGE_AUDIO_PREFIX="generated-audio" | |
| STORAGE_VIDEO_PREFIX="generated-videos" | |
| # Auth settings. | |
| # Demo auth is permitted only when explicitly enabled for localhost development. | |
| AUTH_ENABLED="false" | |
| AUTH_PROVIDER="dev" | |
| ALLOW_INSECURE_DEV_AUTH="true" | |
| FRONTEND_BASE_URL="http://127.0.0.1:3000" | |
| JWT_SECRET_KEY="change-this-before-real-users" | |
| JWT_ALGORITHM="HS256" | |
| ACCESS_TOKEN_EXPIRE_MINUTES="10080" | |
| SUPABASE_URL="" | |
| SUPABASE_JWT_SECRET="" | |
| SUPABASE_ANON_KEY="" | |
| # Backend only. Required to fully remove Supabase Auth identities when | |
| # AUTH_PROVIDER="supabase". Never copy this into a NEXT_PUBLIC_* variable. | |
| SUPABASE_SERVICE_ROLE_KEY="" | |
| # Transactional account email. Required for password recovery when | |
| # AUTH_PROVIDER="jwt" in production. | |
| EMAIL_PROVIDER="disabled" # disabled | resend | |
| RESEND_API_KEY="" | |
| EMAIL_FROM="DocDoe <support@docdoe.in>" | |
| PASSWORD_RESET_TOKEN_MINUTES="30" | |
| PASSWORD_RESET_REQUEST_COOLDOWN_SECONDS="60" | |
| # Optional Google sign-in. | |
| # Create OAuth credentials in Google Cloud and add this redirect URI: | |
| # http://127.0.0.1:8000/auth/google/callback | |
| GOOGLE_CLIENT_ID="" | |
| GOOGLE_CLIENT_SECRET="" | |
| GOOGLE_OAUTH_REDIRECT_URI="http://127.0.0.1:8000/auth/google/callback" | |
| # Beta invite gate. | |
| # Set BETA_ACCESS_ENABLED=true and a strong BETA_INVITE_CODE to require an invite | |
| # code at signup. Leave BETA_ACCESS_ENABLED=false for open signup (dev/demo). | |
| BETA_ACCESS_ENABLED="false" | |
| BETA_INVITE_CODE="" | |
| # Per-user AI rate limiting. | |
| # Set RATE_LIMIT_ENABLED=true outside local development. It prevents a single | |
| # user from exhausting credits and applies per user (JWT sub) or server-observed | |
| # client IP when no token is present. Resets every 24 hours from first request. | |
| RATE_LIMIT_ENABLED="false" | |
| RATE_LIMIT_AI_REQUESTS_PER_DAY="50" | |
| # --- Stripe Billing (Popular 299 + Premium 599 subscriptions) --- | |
| # Prefer a restricted TEST key (rk_test_...) with only the Checkout, Customers, | |
| # Subscriptions, and Billing Portal permissions this backend needs. | |
| # Create recurring test Prices for both active plans. Checkout stays disabled | |
| # until the key, webhook secret, and requested Price are all present. | |
| # Example: | |
| # STRIPE_PRICE_IDS=popular_299=price_sub_abc,premium_599=price_sub_def | |
| # For webhook testing: `stripe listen --forward-to http://127.0.0.1:8000/billing/webhook` | |
| # (this prints a local whsec_... you paste here; in prod use the dashboard endpoint secret). | |
| STRIPE_SECRET_KEY="" | |
| STRIPE_WEBHOOK_SECRET="" | |
| STRIPE_PRICE_IDS="" | |
| # ── Observability + scaling (optional; A+ when set) ─────────────────────────── | |
| # Shared rate-limit + prompt cache across replicas (e.g. Upstash free tier): | |
| REDIS_URL= | |
| # Error tracking — paste a Sentry project DSN to capture backend + frontend errors: | |
| SENTRY_DSN= | |
| SENTRY_TRACES_SAMPLE_RATE=0.1 | |