MVP-first workspace for:
- Milestone 1: GTFS-RT validation + sampling harness
- Milestone 2: Fastify proxy with cache/stale fallback/rate-limit/monitoring
- Milestone 3: Mobile-first React + MapLibre web app with graceful fallbacks
apps/api- backend proxy, static GTFS endpoints, schedule fallback logicapps/web- mobile web app (Vite + React + MapLibre)scripts- GTFS ingestion + data quality sampling/report generation
- Install dependencies:
npm install
- Build static GTFS artifacts:
npm run build:gtfs
- Run API + web app:
npm run dev
- Open:
- Web:
http://localhost:5173 - API health:
http://localhost:8787/api/health
- Web:
- One-shot feed validation:
npm run validate:feeds
- 48-hour feed sampler:
npm run sample:feeds -- --duration-hours 48 --interval-seconds 60
- Summarize latest sample:
npm run summarize:sample
Key API env vars (defaults shown):
PORT=8787GTFS_RT_TTL_MS=10000GTFS_RT_FETCH_TIMEOUT_MS=3000API_RATE_LIMIT_MAX=60API_RATE_LIMIT_WINDOW_MS=60000MAX_SSE_CONNECTIONS_PER_IP=5GTFS_TIMEZONE=Pacific/Honolulu
- Deploy both web + API on one VM:
./scripts/deploy-hetzner.sh <VM_PUBLIC_IP> hele.one root
- Full guide:
deploy/README.md
- The frontend uses polling by default (10-60s) to keep MVP simple.
- SSE endpoint exists in API (
/events/vehicles) for later optimization. - If TripUpdates are empty, stop ETAs automatically fall back to scheduled times.