A polished, self-hosted IT certification exam preparation application for serious lab rats who prefer owning their data instead of renting flashcards from someone else's SaaS pile.
- MD-102 Microsoft Endpoint Administrator
- AZ-104 Azure Administrator Associate
- CompTIA Network+ N10-009
- CompTIA A+ Core 1 220-1101
- CompTIA A+ Core 2 220-1102
- CompTIA Security+ SY0-701
- CompTIA Linux+ XK0-005
- SC-300 Identity and Access Administrator
- AZ-700 Azure Network Engineer
- MS-102 Microsoft 365 Administrator
The original three exams ship with 320 seeded questions each. Additional exams ship with 60 seeded starter questions each and the same review/generation pipeline for expansion. Questions are mapped to exam objectives and structured for spaced repetition.
- React + Tailwind dark professional UI
- FastAPI backend with SQLite persistence
- Practice mode with immediate feedback
- Timed exam mode with 90-minute exam simulation
- Passing score display and pass/fail result
- Spaced repetition queue
- correct answers move further out
- wrong answers return quickly
- mastery score tracked per question
- Question flagging for review
- Explanation after every answer
- Objective mapping per question
- Progress dashboard by exam and objective
- Weak area tracking
- Attempt history with scores and dates
- Admin review queue
- Manual JSON question import
- Refresh button for scraper and Claude generation candidates
- Deduplication by normalized question fingerprint
- Single Docker container serving frontend and backend
- Persistent Docker volume for SQLite
cp .env.example .env
# Optional: edit .env and add ANTHROPIC_API_KEY for Claude question generation.
docker compose up -d --buildOpen:
http://localhost:8083
Health check:
curl http://localhost:8083/api/healthANTHROPIC_API_KEY= # optional, needed for LLM generation
ANTHROPIC_MODEL=claude-sonnet-4-20250514
DATABASE_PATH=/data/exam-prep.sqlite
The compose file creates a named volume:
exam-prep-data:/data
The SQLite database lives at:
/data/exam-prep.sqlite
- Open Admin.
- Select an exam.
- Click
Check for new questions. - Scraper and/or Claude generation creates unverified candidates.
- Review each question.
- Approve good items into the active question pool.
- Reject garbage. There will be garbage. This is the internet.
Paste this shape into the Admin manual import box:
{
"objective_code": "1.1",
"question_text": "A company wants to deploy Windows clients with minimal technician interaction. What should the administrator configure?",
"choices": [
"Windows Autopilot deployment profile",
"Manual local installation media",
"Disable device enrollment",
"Unmanaged workgroup deployment"
],
"correct_choice": "A",
"explanation": "Windows Autopilot supports cloud-driven Windows provisioning and maps to MD-102 deployment objectives."
}Manual imports enter the review queue unless marked verified through the API.
GET /api/healthGET /api/examsGET /api/exams/{exam_id}/queue?mode=practice&limit=20POST /api/answerPOST /api/attemptsGET /api/attemptsGET /api/admin/reviewPOST /api/admin/questionsPOST /api/admin/review/{question_id}POST /api/admin/refresh
Backend:
cd backend
python3 seed.py
uvicorn main:app --reload --port 8083Frontend:
cd frontend
npm install
npm run devFor production, use Docker. That is the point.
Community-sourced and generated questions are marked unverified and inactive until approved. The app intentionally keeps a human review gate because blindly trusting scraped exam content is how weak people build liability engines.



