| Name | Required | Default | Description |
|---|
DATABASE_URL | Yes | | PostgreSQL connection string for the Classmoji database. We recommend Neon for production. |
BETTER_AUTH_SECRET | Yes | | Secret used to sign auth sessions and tokens. Generate with openssl rand -base64 32. |
WEBAPP_URL | Yes | | Public base URL of the main web app (e.g. http://localhost:3000). |
SLIDES_URL | Yes | | Public base URL of the slides service. |
PAGES_URL | Yes | | Public base URL of the pages service. |
| Name | Required | Default | Description |
|---|
GITHUB_APP_NAME | No | | Slug of your Github App, used to build install and authorization links. |
GITHUB_APP_ID | No | | Numeric ID of your Github App. |
GITHUB_CLIENT_ID | No | | OAuth client ID for Github sign-in. |
GITHUB_CLIENT_SECRET | No | | OAuth client secret for Github sign-in. |
GITHUB_WEBHOOK_SECRET | No | | Secret used to verify incoming Github webhook signatures. Generate with openssl rand -base64 32. |
GITHUB_PRIVATE_KEY_BASE64 | No | | Base64-encoded private key for your Github App, used to authenticate API requests. |
AI features (quizzes, Ask Moji) run on Anthropic Claude. They are optional: when AI_AGENT_URL and AI_AGENT_SHARED_SECRET are unset, AI features that run on the AI agent are hidden and the related API routes return 503. Ask Moji can instead run as a background-jobs chat agent (ASK_MOJI_RUNTIME=trigger), which needs TRIGGER_SECRET_KEY and an Anthropic key rather than the AI agent.
| Name | Required | Default | Description |
|---|
AI_AGENT_URL | No | | URL of the AI agent service. Enables AI features when set with the shared secret. |
AI_AGENT_SHARED_SECRET | No | | Shared secret used to authenticate the webapp to the AI agent. |
ANTHROPIC_API_KEY | No | | Anthropic API key the AI agent uses to call Claude. |
LLM_MODEL | No | claude-sonnet-5-5 | Claude model used for quizzes. These accept only the models listed in packages/utils/src/aiModels.ts; any other value runs the default. Set the same value in all three places that read it: the AI agent and the background jobs, which run quizzes, and the web app, which shows it as the default on AI settings. |
EXPLORATION_MODEL | No | claude-sonnet-5-5 | Claude model used for code-aware quiz repository exploration. Same list and default as LLM_MODEL, read by the same three: the AI agent, the background jobs and the web app. |
EXPLORATION_MODE | No | | Controls the code-aware exploration behavior for quizzes. |
QUIZ_QUESTION_EFFORT | No | medium | Reasoning effort (low, medium, high, xhigh, max) for quiz question and conversation turns. A classroom with its own API key can override it. |
QUIZ_GRADING_EFFORT | No | high | Reasoning effort for final quiz grading. A classroom with its own API key can override it. |
EXPLORATION_EFFORT | No | low | Reasoning effort for the code-aware quiz’s excerpt selection (xhigh and max run as high). A classroom with its own API key can override it. |
ASK_MOJI_RUNTIME | No | ai_agent | Which runtime serves Ask Moji. ai_agent (also when unset): the AI agent, in the web app only. trigger: the ask-moji chat agent on Trigger.dev, in the web app, pages and slides, with the instructor insights page; it needs TRIGGER_SECRET_KEY and an Anthropic key (ANTHROPIC_API_KEY or the classroom’s own). Set it on the web app, pages and slides alike (the pages and slides apps embed the web app’s Ask Moji panel), and on the MCP server: it also gates the MCP server’s Ask Moji tools (usage, feedback review, settings), which work only where Ask Moji does. With trigger, the background jobs also need MCP_PUBLIC_URL. Any value other than trigger or ai_agent runs as ai_agent. |
MCP_PUBLIC_URL | With ASK_MOJI_RUNTIME=trigger | | Public origin of the MCP server (apps/mcp), for example https://mcp.example.org. The MCP server advertises it to its clients, and Ask Moji reads the course through it: set it on the background jobs too, or Ask Moji can’t answer questions. Use the public HTTPS origin, not a private network hostname. |
SYLLABUS_BOT_MODEL | No | LLM_MODEL, then claude-sonnet-5-5 | Claude model used for Ask Moji. With ASK_MOJI_RUNTIME=trigger it accepts only the models listed in packages/utils/src/aiModels.ts (any other value runs claude-sonnet-5-5), as LLM_MODEL does; on the AI agent any Claude model id works. Read by the AI agent and the background jobs, and by the web app to show the default. A classroom with its own API key can override it with an allow-listed model. |
SYLLABUS_BOT_EFFORT | No | low | Reasoning effort for Ask Moji. A classroom with its own API key can override it. |
AI_MAX_BUDGET_USD | No | 1.00 | Per-call cost ceiling in USD for quiz and Ask Moji agent calls on the AI agent with the platform ANTHROPIC_API_KEY. A runaway guard, not a spend limit. |
OPENAI_API_KEY | No | | Reserved. Not used by any current feature. |
LLM_PROVIDER | No | | Reserved. Only Anthropic/Claude is wired today. |
| Name | Required | Default | Description |
|---|
STRIPE_SECRET_KEY | No | | Stripe secret API key for subscription checkout and billing. |
STRIPE_WEBHOOK_SECRET | No | | Secret used to verify incoming Stripe webhook signatures. |
| Name | Required | Default | Description |
|---|
EMAIL | No | | Address used as the sender for outgoing email. |
EMAIL_PASSWORD | No | | Password (or app password) for the sending email account. |
| Name | Required | Default | Description |
|---|
TRIGGER_API_URL | No | | Base URL of the Trigger.dev API. Leave unset to use Trigger.dev cloud. |
TRIGGER_PUBLIC_API_URL | No | | Public Trigger.dev API address that GitHub Actions can reach; the generated autograding workflow reports results to it. Leave unset for Trigger.dev cloud. Not the worker-internal TRIGGER_API_URL. |
TRIGGER_SECRET_KEY | No | | Trigger.dev secret key used to authenticate background job runs. |
Large files (video, audio, big PDFs) go to a Cloudflare R2 bucket rather than into a classroom’s content repo. The four MEDIA_R2_* variables are optional and all-or-nothing: with any of them unset, media upload is hidden in the UI and the media API routes return 503.
| Name | Required | Default | Description |
|---|
MEDIA_R2_ACCOUNT_ID | No | | Cloudflare account the media bucket lives in. Used to build the R2 S3 endpoint. |
MEDIA_R2_ACCESS_KEY_ID | No | | Access key of an R2 API token scoped to the media bucket (object read/write). |
MEDIA_R2_SECRET_ACCESS_KEY | No | | Secret of that R2 API token. It never leaves the server: uploads are presigned and the browser writes straight to R2. |
MEDIA_R2_BUCKET | No | | Name of the R2 bucket media objects are written to. |
CLOUDINARY_CLOUD_NAME | No | | Cloudinary cloud name for image and media uploads. |
CLOUDINARY_API_KEY | No | | Cloudinary API key for signed uploads. |
CLOUDINARY_API_SECRET | No | | Cloudinary API secret for signed uploads. |
| Name | Required | Default | Description |
|---|
CALENDAR_SECRET | No | | Secret used to sign calendar feed (iCal) URLs. Generate with openssl rand -base64 32. |