Starlette · Python 3.10+ · SQLite / Supabase · Zero bloat. The API powering CalculusRuntime's user accounts, learning progress, bookmarks, quizzes, and solver history.
- Overview
- Tech Stack
- Project Structure
- Getting Started
- Environment Variables
- Database
- API Reference
- Authentication
- License
CalculusRuntime's backend is a lean, dependency-light REST API built on Starlette. It handles everything that needs to persist between sessions — who you are, how far you've gotten, what you've bookmarked, your best quiz scores, and your solver history.
No FastAPI. No Pydantic. No ORM. No python-dotenv. Just clean async Python and a storage layer that transparently switches between a local SQLite file and Supabase — your choice, one env var.
| Layer | Choice |
|---|---|
| Web framework | Starlette |
| Auth | PyJWT + bcrypt (HS256, 7-day tokens) |
| Database (local) | SQLite via stdlib sqlite3 + asyncio.to_thread |
| Database (cloud) | Supabase (Postgres) via supabase-py |
| Server | Uvicorn |
| Python | 3.10 → 3.14 ✓ |
backend/
├── main.py # App entry point, routing, CORS, lifespan
├── auth_utils.py # JWT creation/decoding, bcrypt hashing, request helpers
├── storage.py # Unified async storage layer (SQLite ↔ Supabase)
├── db.py # Raw SQLite helpers wrapped in asyncio.to_thread
├── progress_store.py # Progress-specific storage logic
├── routers/
│ ├── auth.py # POST /signup, POST /login, GET /me
│ ├── progress.py # GET /, POST /section/complete, DELETE /section/{id}
│ ├── bookmarks.py # GET /, POST /, DELETE /{id}
│ ├── quiz.py # GET /, POST /
│ └── solver_proxy.py # POST /log, GET /history
├── supabase_schema.sql # Schema + RLS setup for Supabase
├── requirements.txt
└── .env.example
git clone <repo-url>
cd backend
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activatepip install -r requirements.txtcp .env.example .env
# Edit .env — see Environment Variables belowuvicorn main:app --reload --port 8000The API will be live at http://localhost:8000.
Visit http://localhost:8000/docs for the interactive endpoint reference.
Copy .env.example to .env and fill in the values:
# Required — use a long, random string in production
SECRET_KEY=replace-with-a-long-random-string
# Token lifetime in minutes (default: 10080 = 7 days)
TOKEN_EXPIRE_MINUTES=10080
# --- Storage backend ---
# Set PROGRESS_DB=supabase to use Supabase; omit or set to "sqlite" for local SQLite
PROGRESS_DB=supabase
# SQLite path (used when PROGRESS_DB != supabase)
DB_PATH=calculusruntime.db
# Supabase (required when PROGRESS_DB=supabase)
SUPABASE_URL=https://xyzcompany.supabase.co
SUPABASE_SERVICE_ROLE_KEY=replace-with-your-backend-only-service-role-key
# CORS — comma-separated list of allowed origins
ALLOWED_ORIGINS=http://localhost:3000Warning
Never commit .env to version control. It's already in .gitignore, but double-check before pushing. Rotate any keys that may have been exposed.
No setup required. The database file is created automatically on first run. WAL mode and foreign keys are enabled out of the box.
- Set
PROGRESS_DB=supabasein your.env. - Open your Supabase project → SQL Editor.
- Run
supabase_schema.sql— creates all tables, indexes, and disables RLS on app-managed tables. - Use your service role key (not the anon/publishable key) as
SUPABASE_SERVICE_ROLE_KEY. CalculusRuntime manages its own JWT auth; Supabase Auth is not used.
Important
RLS is intentionally disabled on these tables — auth is handled entirely by the backend. Re-enabling RLS without adding policies will block all requests with a 500 error.
Schema overview:
| Table | Purpose |
|---|---|
users |
Accounts (username, email, hashed password) |
sections |
Per-user completed section tracking |
bookmarks |
Saved content bookmarks |
quiz_scores |
Best score per quiz per user |
solver_history |
Last 50 solver expressions + results |
All protected routes require:
Authorization: Bearer <token>
Obtain a token from POST /api/auth/signup or POST /api/auth/login.
| Method | Path | Auth | Body | Description |
|---|---|---|---|---|
POST |
/api/auth/signup |
— | { username, password, email? } |
Create account, returns JWT |
POST |
/api/auth/login |
— | { username, password } |
Sign in, returns JWT |
GET |
/api/auth/me |
🔒 | — | Current user profile |
| Method | Path | Auth | Body | Description |
|---|---|---|---|---|
GET |
/api/progress/ |
🔒 | — | Full snapshot: completed sections, quiz scores, bookmarks, solver count |
POST |
/api/progress/section/complete |
🔒 | { section_id } |
Mark a section complete |
DELETE |
/api/progress/section/{section_id} |
🔒 | — | Unmark a section |
| Method | Path | Auth | Body | Description |
|---|---|---|---|---|
GET |
/api/bookmarks/ |
🔒 | — | List all bookmarks (newest first) |
POST |
/api/bookmarks/ |
🔒 | { id, title, path } |
Add / upsert a bookmark |
DELETE |
/api/bookmarks/{id} |
🔒 | — | Remove a bookmark |
| Method | Path | Auth | Body | Description |
|---|---|---|---|---|
GET |
/api/quiz/ |
🔒 | — | All quiz scores for current user |
POST |
/api/quiz/ |
🔒 | { quiz_id, score, total } |
Save score (best score wins on upsert) |
| Method | Path | Auth | Body | Description |
|---|---|---|---|---|
POST |
/api/solver/log |
optional | { expression?, result? } |
Log a solver interaction |
GET |
/api/solver/history |
🔒 | — | Last 50 solver interactions |
| Method | Path | Description |
|---|---|---|
GET |
/ |
API info + endpoint index (JSON) |
GET |
/api/health |
Health check → { "status": "ok" } |
GET |
/docs |
Interactive HTML endpoint reference |
Tokens are HS256 JWTs signed with SECRET_KEY, valid for TOKEN_EXPIRE_MINUTES (default 7 days). Passwords are bcrypt-hashed before storage — plaintext passwords are never stored or returned.
Authorization: Bearer <your-token>© 2025 QuantumLogics Incorporated. All rights reserved. This software is proprietary — see LICENSE for the full terms.