quiz-app
Invite-only, timed, multiple-choice quizzes. Python Cloudflare Worker + D1, Next.js static export, Cloudflare Access (OTP).
- Stars
- 0
- Language
- Python
- Created
- Oct 23, 2026
- Updated
- Oct 25, 2026
Introduction
The Quiz Room
Invite-only, timed, multiple-choice quizzes. Questions live in sets under topics; a question has any number of options and any number of them may be correct; a set can carry a time limit. Nothing is marked — no right, no wrong, no count — until the solver submits or the clock runs out.
Take a quiz | Authoring plugin | Design | Deploy | Stack
What it is
- Sets under topics. "RAG › Embeddings and vector space · Set 1" is derived from the set's topic, subtopic, difficulty and sequence — never typed by hand.
- Multi-correct by design. Tick any combination; the answer key is the set of
options marked correct, and scoring is per set (
all_or_nothingorpartial). - The server owns the clock. The deadline is fixed at start, the browser's countdown only displays it, and closing the tab cannot buy time.
- Pausing freezes the clock. A paused attempt keeps its time, its answers, and the exact paper it was served.
- History is the solver's, not the author's. Editing or deleting a quiz never rewrites someone's past attempt: every attempt keeps its own copy of the questions, so the per-question breakdown survives even after the quiz is gone.
- Invite-only. Cloudflare Access with an email one-time PIN is the only way in.
The authoring plugin → tools/
Quizzes are authored and uploaded by a packaged agent plugin, not by hand-editing
JSON. It lives in this repo at tools/ and gives two skills:
| Skill | What it does |
|---|---|
quiz-create | Writes a quiz-set JSON in the house style and validates it against the server's own rules. |
quiz-upload | Logs in, imports the set, publishes it — hands-off. |
# Claude Code
claude plugin marketplace add akshayDev17/quiz-app
claude plugin install quiz-tools@quiz-app --scope user
# DSH
dsh plugin --profile web add file:"$PWD/tools"
Full instructions — first-run Gmail setup, usage, troubleshooting, what it leaves
on disk — are in tools/README.md.
Repo layout
| Path | What |
|---|---|
backend/ | Python Cloudflare Worker: the API, D1 repositories, migrations, tests. |
frontend/ | Next.js static export: the quiz UI, deployed as static assets under /quiz. |
tools/ | The authoring plugin — quiz-create and quiz-upload, packaged for Claude Code and DSH. |
docs/design.md | The design: topology, auth, schema, request flows. |
docs/deploy.md | The deploy runbook: Access, D1, secrets, CI, first-deploy checks. |
design/ | Clickable frontend sketches — open the HTML in a browser. |
Run it locally
Two processes; the frontend talks to the backend's origin.
# API + database, on :8787 (creates backend/quiz.db on first run)
cd backend && python3 local_server.py
# UI, on http://localhost:3000/quiz
cd frontend && npm install && NEXT_PUBLIC_API_BASE=http://127.0.0.1:8787 npx next dev
The dev server signs you in as DEV_EMAIL (default dev@example.com) and treats
that address as an admin, so /admin works without Cloudflare Access.
Deploy
Per docs/deploy.md: Cloudflare Access first, then D1, then
ADMIN_EMAILS. CI deploys on every push to main, so do not push to main
before §A–C are complete.
Stack
The same shape as ai-chat-archive: a Python Cloudflare Worker + D1 for the API, a
Next.js static export uploaded as static assets for the frontend, and Cloudflare
Access (email one-time PIN) as the only way in. No framework is used in the
backend — the router is a pure function of (method, path, body, identity), and
every repository is the single SQL implementation that both D1 and the tests run
against.