Skip to main content
The system is split into isolated pieces that each do one job and hold only the credentials that job needs. n8n sits in the middle and decides what happens to data. Everything else either produces data or reads it.

The boundaries

n8n, self-hosted behind a Caddy reverse proxy.Runs the daily schedule, calls the scraper, commits each class’s snapshot to the data repo through the GitHub Contents API, updates the rolling index, and sends error notifications. Conditional logic lives here, not in application code.See n8n workflow.

What each boundary is trusted with

A daily run

Design choices

The scraper’s job is to collect. If it also decided where data goes and who gets notified, every new notification channel or storage target would mean a code change and a redeploy. With n8n as the orchestrator, those become workflow branches, and every execution is logged with its full input and output.
Each snapshot becomes a timestamped commit that cannot be quietly edited. That is the property the project exists to provide. JSON files in a repository are also enough for one student over a school year; a database can be added later without changing the snapshot format.
The calendar needs a change count for every day in a month. Fetching and diffing dozens of snapshots per page view would be slow, so those counts live in the rolling index. Detail views need only two snapshots, so they are diffed on demand from the raw files. See Change detection.