Simple status-page style site for Caltrain service uptime and incidents. Alerts and notifications are received from Caltrain real-time email alerts.
This project is inspired by...
- The Missing GitHub Status Page
- My own experience taking the Caltrain
- And the current era of "vibe coding"
This codebase is completely vibe coded using GPT-5.3-Codex with small modifications from me.
- Static frontend (
index.html,styles.css,app.js) - File-based "backend" (
data/*.json) - Designed to work on GitHub Pages (no server required)
data/current-status.json- Current overall status (
operational,degraded,major,critical) - Current banner message
- Active incident IDs
incident_file_pathslist (append-only incident snapshot files to load)
- Current overall status (
data/incidents/index.json- Legacy manifest of incident files (still supported as fallback)
data/incidents/YYYY-MM/incidents.json- Legacy incident history shard format (still supported)
data/incidents/events/YYYY/MM/DD/*.json- Append-only incident snapshot files (new production ingestion format)
Uptime values shown on the page are computed dynamically in app.js from incident files referenced by data/current-status.json (incident_file_paths), with fallback support for data/incidents/index.json.
- Browser loads the static page.
app.jsfetchesdata/current-status.json.- It loads incident data files from
current-status.json.incident_file_paths(append-only snapshots). - It also supports legacy files from
data/incidents/index.json(fallback/compatibility). - The page renders:
- current status
- uptime percentages (computed from incidents)
- incident details
- incident history
- email alert template placeholder
- The page polls every 60 seconds to refresh.
- Push this repo to GitHub.
- Enable GitHub Pages for the repository (branch deploy).
- Serve from the repo root.
Production ingestion is handled by:
scripts/ingest_emails_imap.py
This script:
- Connects to Gmail via IMAP
- Reads alert emails
- Parses Caltrain alert fields (subject type, cause/effect, start/end dates)
- Writes a new append-only incident snapshot file per processed email
- Updates
data/current-status.json(the only file intentionally overwritten) - Marks processed emails as
\Seen(when enabled)
- Append-only:
data/incidents/events/YYYY/MM/DD/*.json
- Overwritten current state:
data/current-status.json
Each new email produces a new immutable incident snapshot file. The current status file maintains incident_file_paths, which acts as the list of snapshot files the frontend should load.
This avoids rewriting historical incident files while still allowing the site to compute the latest incident state and uptime.
This repo includes Python scripts for Gmail IMAP access:
- Production ingester:
scripts/ingest_emails_imap.py - Fetch/debug helper:
scripts/fetch_emails_imap.py
- Create a dedicated Gmail inbox (recommended).
- Enable 2-Step Verification on that Google account.
- Create a Gmail App Password.
- Copy
.env.exampleto.envand fill in:GMAIL_USERGMAIL_APP_PASSWORD
- (Recommended) Set filters:
INGEST_FROM_MATCHINGEST_SUBJECT_MATCH
data/incidents/events/YYYY/MM/DD/*.json(append-only incident snapshots)data/current-status.json(recomputes active incident summary and updatesincident_file_paths)data/ingestion-state.json(optional local dedupe state; gitignored)
- Production ingest (writes JSON files):
python3 scripts/ingest_emails_imap.py - Fetch/debug only (prints emails):
python3 scripts/fetch_emails_imap.py
- The parser is heuristic and tuned to the sample Caltrain alert format currently in the repo.
- The workflow uses IMAP
UNSEEN+ marking messages\Seenas the primary dedupe mechanism in GitHub Actions. data/incidents/index.jsonand monthly shard files remain supported as a legacy fallback source.
Because the page fetches JSON/text files, open it through a local web server (not file://).
Examples:
python3 -m http.servernpx serve .