MetroMark is a transit exploration tracker for people who like to discover and complete transit systems.
- Explore routes on a live map.
- Filter by mode, frequency, and search text.
- Focus one route while keeping other filtered routes visible in the background.
- Click stations to mark them visited (when signed in).
- Track progress across visible routes.
- Install Node.js 20+.
- Copy
.env.development.exampleto.env.development. - Add Supabase keys and Transitland API key to
.env.development. - Apply Supabase migrations:
supabase/migrations/20260417_metromark_core.sqlsupabase/migrations/20260418_user_filter_presets.sql
- Run:
npm install
npm run start:devOptional explicit profiles:
npm run start:dev
npm run start:prodThen open:
MetroMark uses server-side caching to control Transitland API usage.
- Transit requests are made by the Node server, not by browser clients directly.
- Server responses are cached in Supabase Postgres with PostGIS enabled.
- The browser keeps only a short session cache for smoother map interactions.
This means repeated users/views should hit server cache instead of repeatedly calling Transitland for the same area.
Defaults now use stronger retention for production hosting:
TRANSIT_CACHE_TTL_HOURS=2160(90 days)TRANSIT_CACHE_STALE_DAYS=30(background reverify threshold)
Background scripts:
npm run harvest:core(development profile)npm run harvest:core:prod(production profile)npm run backup:nonrecoverable(development profile)npm run backup:nonrecoverable:prod(production profile)
Admin operations page:
- Open
/adminand providex-admin-keyvalue. - Track burn rate, harvest queue, and storage size.
- Manually trigger harvest, nonrecoverable backup, city queueing, and station overrides.
For full hosting migration steps (Windows scheduler + Cloudflare), see docs/MIGRATION-WINDOWS.md.
Workspace layout cheat sheet: docs/WORKSPACE-LAYOUT.md.
- Transit data: Transitland
- Terms: https://www.transit.land/terms
- Very wide zoom levels intentionally pause new fetches to avoid expensive global pulls.
- Cached, in-view routes can still render while fetch is paused.
- Route frequency quality depends on available upstream headway data.