Learn C++ through the learncpp.com curriculum — 345 lessons, auto-graded exercises, a sandboxed compiler, and an AI tutor that runs on your own API key.
cpproad turns the learncpp.com curriculum (34 chapters, 345 lessons) into an interactive course: read the lesson, write code in the browser, run it against real test cases, ask the tutor for a hint when you're stuck, and get the concept back tomorrow in review.
Free, open signup, no paid tier, no gated content.
| Layer | What runs it |
|---|---|
| App + API | Next.js App Router, TypeScript strict mode, Vercel |
| Data + auth | Supabase Postgres, magic-link auth, RLS on every table |
| Code execution | Judge0 on the RapidAPI shared host |
| Tutor | Gemini 2.5 Flash, streaming, user-supplied key |
Lesson summaries, exercises, and concept checks are authored offline and stored as plain
files under scripts/regenerated/v2/<lesson>/ — summary.md and exercises.json. They're
validated by scripts/validate_v2.ts and pushed into Postgres by scripts/push_v2.ts.
Serving a lesson is a database read. Nothing in the request path calls a model, so content is diffable in git, reviewable in a PR, and costs nothing per pageview. Content is paraphrased into cpproad's own voice, not scraped verbatim.
Bring your own Gemini key. It's AES-256-GCM encrypted before it reaches the database and
decrypted per request — cpproad never proxies a shared key, so tutor usage bills to the
person using it. Without a key, /api/chat returns 403 rather than falling back to
anything.
Hints escalate across four tiers: nudge → concept → structure → worked example. The tier comes from conversation state, so the tutor can't open with the answer.
Submissions compile and run in a sandboxed Judge0 instance, then get diffed against per-exercise test cases. Fork bombs, network egress, filesystem writes, OOM, infinite loops, and process enumeration were verified to fail closed as of 2026-06-11.
Production runs against RapidAPI's shared host, which means patch level, isolation, and rate
limits are outside our control. A self-hosted gVisor setup is staged in infra/judge0/ and
is not production-ready — the gVisor runtime is commented out and workers run
privileged. Re-harden before deploying it.
Every concept check a user answers writes both an attempt row and its scheduler state
(interval_index, next_due) in one RPC. GET /api/review/due returns up to 20 due cards
and nothing else touches the scheduler — all writes funnel through applyAttempt in
lib/content/review.ts so the two tables can't drift apart.
Connect a GitHub account and passed submissions get committed to your own public
cpproad-submissions repo via the Contents API, so practice shows up on your contribution
graph. Scope requested is public_repo; the token is encrypted at rest. Inert until an
operator registers an OAuth app.
Contributions are open, especially from people who want to teach: sharpen a lesson explanation, write a better exercise, fix a wrong test case, or flag where the curriculum glosses over a concept.
Every contribution starts with an issue. No unsolicited pull requests — open an issue, let it get triaged, then submit a PR referencing it. This keeps duplicate work down and means nobody burns a weekend on something already rejected or already in flight.
- Bug — open an issue with steps to reproduce.
- Wrong or unclear content — open an issue tagged
contentnaming the chapter and lesson number. The source files are underscripts/regenerated/v2/; fix them like any other file andscripts/validate_v2.tswill gate the merge. - Feature — describe the problem before the solution. Feature PRs without a prior issue get asked to open one.
Requires Node.js and the Supabase CLI (via npx).
npm install
cp .env.example .env.local # fill in the values below
git config core.hooksPath .githooks # blocks committing .env files
npx supabase db push # apply database migrations
npm run dev| Variable | Purpose |
|---|---|
NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY |
Supabase project (public) |
SUPABASE_SERVICE_ROLE_KEY |
Server-only; bypasses RLS |
JUDGE0_URL, JUDGE0_AUTH_TOKEN |
Judge0 code execution (currently RapidAPI shared host) |
API_KEY_ENCRYPTION_SECRET |
Encrypts user Gemini keys and GitHub tokens (openssl rand -base64 32) |
GITHUB_OAUTH_CLIENT_ID, GITHUB_OAUTH_CLIENT_SECRET |
Optional GitHub sync |
NEXT_PUBLIC_STATSIG_CLIENT_KEY, STATSIG_SERVER_SECRET_KEY |
Statsig analytics and feature gates (server key required at runtime) |
No server-side LLM key is needed: the tutor uses each user's own Gemini key, and lesson content is pre-generated under scripts/regenerated/v2/.
Supabase (Postgres + auth), Judge0 via RapidAPI, Google Gemini (user-supplied keys), Statsig, GitHub OAuth (optional), Vercel (hosting).
npm test # vitest run
npm run lint
npm run format:check- Implemented: lessons served from Postgres, in-browser editor with Judge0 grading, BYOK Gemini tutor with tiered hints, spaced-repetition review, optional GitHub sync.
- Unfinished: self-hosted gVisor Judge0 in
infra/judge0/is staged but not hardened (gVisor runtime commented out, privileged workers); GitHub sync is inert until an operator registers an OAuth app.
MIT — see LICENSE.
