Homeschool Management System
OurSchool is a self-hosted homeschool management system for families who take attendance seriously, grade assignments carefully, and really don't want to maintain a pile of spreadsheets. It handles the administrative grind — attendance, subjects, assignments, grading, reports, and a shameless gamification points system — so you can spend more time on the actual teaching.
Beta —
v1.0.0-beta.1
Pre-stable software. The database schema may have breaking changes until the planned 2026–2027 stable release. Use the built-in system backup/restore (with dry-run preview) to safeguard your data between updates.
- Multi-user auth — Separate logins for parents (admin) and students. Program administrators see the whole picture for all students while students get a streamlined view of their own work and progress.
- Attendance tracking — Daily records with status and notes. Flexible academic terms (
semester,quarter,trimester, orcustom) that map to your jurisdiction's reporting requirements. - Subjects — Configure subject areas with names, descriptions, and colors. They persist across terms so you're not re-entering them every year.
- Assignment templates → student assignments — Create a template once, assign it to one or more students. Inline row grading and bulk-grade support mean less clicking.
- Optional gamification — As assignments are graded, students earn points redeemable for whatever your household considers a reward. The whole system is opt-in and can be toggled off in Admin Center.
- Journal — Teacher and student entries with date tracking, reactions, and threaded replies.
- Reports — Performance reports, attendance summaries, assignment completion rates, grade trends, and term report cards.
- System backup / restore — Full export/import with dry-run preview, cross-version compatibility (hopefully...), and stable external IDs for conflict-free entity resolution.
- Integration API — REST API with Bearer token and API key (
os_prefix) auth. MCP-ready:GET /api/metafor enum/permission discovery. Full reference at/docs.
The fastest path. Just yoink the official images from GHCR.
# 1. Grab the compose file and sample env
curl -O https://raw.githubusercontent.com/DGAzr/ourschool/main/docker-compose.ghcr.yml
curl -O https://raw.githubusercontent.com/DGAzr/ourschool/main/env.EXAMPLE
# 2. Set up your environment
cp env.EXAMPLE .env
# Edit .env — at minimum, replace SECRET_KEY with a real secret:
# openssl rand -hex 32
# 3. Launch (includes a bundled PostgreSQL container)
docker compose -f docker-compose.ghcr.yml --profile local-db up -d
# 4. Open the app
open http://localhost:4173That's it. The backend runs migrations and seeds an admin account automatically on first start.
⚠️ Change the default credentials immediately after first login.
Admin login:admin/admin123— these are public knowledge and exist only to get you in the door.
📌 External database? Skip
--profile local-dband setDATABASE_URLin.envinstead.
🏷️ Image tag: The compose file defaults to
v1.0.0-beta.1. ChangeIMAGE_TAGin.envto pin a different release. All published tags: ghcr.io/dgazr/ourschool-backend.
If OurSchool saves you time and/or a mild argument with your spreadsheet, I'd appreciate it!
For contributors or anyone who wants to run the app without Docker.
- Python 3.11+
- Node.js 20+
- PostgreSQL
# Create and activate a virtual environment
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Configure environment
cp env.EXAMPLE .env
# Edit .env — set DATABASE_URL (or POSTGRES_* vars) and SECRET_KEY
# Generate a strong SECRET_KEY: openssl rand -hex 32
# Run migrations
alembic upgrade head
# Seed the initial admin account
python seed_data.py
# Admin login: admin / admin123 (change it!)
# Add --full for demo students, subjects, terms, and sample assignments
# Start the API server
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000cd frontend
npm install
npm run devThe app is available at:
| Service | URL |
|---|---|
| Frontend (dev) | http://localhost:4173 |
| API | http://localhost:8000 |
| API docs | http://localhost:8000/docs |
API docs can be disabled with
ENABLE_API_DOCS=falsein.env.
OurSchool has a REST API for external integrations — handy for AI tools, automation, or a second screen that shows grades without navigating the UI. More endpoints coming soon!
User session (Bearer token)
curl -X POST http://localhost:8000/api/auth/login \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=admin&password=admin123"
# Returns a JWT — use as: Authorization: Bearer <token>API key (create under Admin → API Keys)
curl -H "X-API-Key: os_YOUR_KEY_HERE" \
http://localhost:8000/api/points/admin/overview| Permission | What it grants |
|---|---|
students:read |
Read student information |
assignments:read |
Read assignment data |
assignments:grade |
Grade student assignments |
points:read |
Read student points and transaction history |
points:write |
Adjust student points |
GET /api/metaReturns all active assignment types, assignment status enum values, and available API key permissions. Useful for AI/MCP clients that need to enumerate valid values before taking action.
curl -X POST "http://localhost:8000/api/integrations/assignments/123/grade" \
-H "X-API-Key: os_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{"points_earned": 85.0, "teacher_feedback": "Nice work!", "letter_grade": "B+"}'import requests, os
API_KEY = os.getenv("OURSCHOOL_API_KEY")
headers = {"X-API-Key": API_KEY}
r = requests.get("http://localhost:8000/api/points/admin/overview", headers=headers)
for student in r.json()["student_points"]:
print(f"{student['student_name']}: {student['current_balance']} pts")Full endpoint reference: http://localhost:8000/docs
# Create a new migration
alembic revision --autogenerate -m "description"
# Apply migrations
alembic upgrade headBackend (pytest + httpx — set DATABASE_URL and SECRET_KEY first):
pytestFrontend checks:
cd frontend
npx tsc --noEmit # type-check
npm run lint # lint
npm run build # production build
npm run knip # unused exports / dead codeContributors can build and run locally using the base compose file:
# Dev mode (live-reload via docker-compose.override.yml, auto-merged):
docker compose up --build
# Production-style build (ignores the dev override):
docker compose -f docker-compose.yml up --build -d| FastAPI 0.138 | Web framework |
| SQLAlchemy 2.0 + psycopg3 | ORM + PostgreSQL driver |
| Alembic 1.18 | Database migrations |
| Pydantic 2.13 | Data validation |
| python-jose + bcrypt 5 | JWT auth + password hashing |
| React 19 | UI |
| TypeScript 6 | Type safety |
| Tailwind CSS 4 | Styling |
| React Router 7 | Routing |
| TanStack Query 5 | Server state |
| Vite 8 | Build tool |
| lucide-react | Icons |
| date-fns | Date formatting |
| react-markdown | Markdown rendering (journal) |
End users: Use docker-compose.ghcr.yml (pulls pre-built images from GHCR) as shown in Quick Start above. The --profile local-db flag adds a bundled Postgres container; omit it and set DATABASE_URL for an external database.
Contributors: Use docker-compose.yml (builds from local Dockerfiles). The docker-compose.override.yml is merged automatically for live-reload dev mode.
Security checklist before going live:
- Generate a real
SECRET_KEY(openssl rand -hex 32). The app refuses to start without it. - Change the default admin password immediately after first login.
- Set strong DB credentials; the default
postgres/postgresis for local dev only. - Restrict
ALLOWED_ORIGINSto your actual domain. - Put a TLS-terminating reverse proxy (nginx, Caddy, Traefik) in front; the bundled frontend doesn't do TLS or rate limiting.
- Set
BACKEND_BIND=0.0.0.0only when behind such a proxy (default is loopback). - Disable API docs in production if desired:
ENABLE_API_DOCS=false.
Licensed under the GNU Affero General Public License v3 (AGPLv3).
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.




