A feed-agnostic GTFS publisher. Reads each operator's upstream source via a per-feed adapter package, and publishes the three artifacts any GTFS consumer needs:
-
Raw GTFS Schedule zips — the spec-compliant
.gtfs.zipfor each feed, content-addressed by sha256. This is the artifact external GTFS tooling (onebusaway, gtfs-to-html, transit planners, validators) reads. It is strictly the public GTFS Schedule spec — no per-feed extensions are added here. -
GTFS-RT feeds — a Fastify HTTP server proxies live
vehicle_positions,trip_updates, andservice_alertsGTFS-Realtime protobuf feeds. Same upstream sources as the static side; the protocol-buffer wire format is the standard, no extensions. -
SQLite blobs —
<id>-<hash12>.sqlite3.gz, one per feed. This is what the n3ary/app PWA stores in OPFS and queries at runtime. Per-feed extensions are injected here — the cluj adapter, for example, addsnetworks.network_colorand a_neary_configtable — but the extensions never leak into the published.gtfs.zip. The zip stays spec-clean; the sqlite blob carries whatever each consumer needs.
Note
Live artifacts: https://gtfs.n3ary.com/feeds.json
(R2 via custom domain gtfs.n3ary.com)
- It is not a GTFS validator. It consumes whatever the adapter produces.
- It is not a feed-source registry. Operators live in
feeds/<id>/config.json, withsource.publisherpointing at a published adapter package (e.g.@n3ary/gtfs-adapter-<feed>fromn3ary/gtfs-adapters). - It is not a trip planner. It only publishes data.
.
├── packages/
│ ├── gtfs-static/ static pipeline: resolve feeds → acquire zips → make-sqlite → R2
│ ├── gtfs-rt/ Fastify GTFS-RT proxy: serves protobuf over HTTP
│ └── spec/ shared @n3ary/gtfs-spec (types, SQL DDL, ZIP readers)
├── feeds/<id>/config.json per-feed overrides (source.publisher, secrets[], timing, smoke)
├── countries.json single source of truth for which feeds to publish
├── docs/
│ ├── architecture/ data-pipeline.md (system-level diagram)
│ └── ops/ secrets-and-deploy.md, publishing.md
└── .github/workflows/ daily.yml (publish), pr-validation.yml
- Add
feeds/<id>/config.json(copy an existing one as a template). - Set
source.typeto"adapter"+source.publisherto the adapter's npm package name. - Add the feed's required secrets to
secrets[](env var names). - Add the env vars to the GitHub Actions secrets on this repo, then add
them to
.github/workflows/daily.yml's env block. - Add the Transitous source name (if any) to
countries.json'sinclude[]so the catalog lookup finds it.
No code change in apps/gtfs-static/src/cli.ts — the orchestrator is
feed-agnostic and discovers adapters by source.publisher at runtime.
npm install
npm run pipeline # full build → outputs/ + (optionally) R2
npm test # vitest --run --passWithNoTests- docs/architecture/data-pipeline.md — system-level diagram of the static + RT pipelines, what each publishes
- docs/ops/secrets-and-deploy.md — R2 credentials, daily workflow, RT proxy deploy
- docs/standards/version-management.md — the bump-on-PR rule
main is protected — every change goes through a PR.
.github/workflows/pr-validation.yml
bumps package.json#version on the PR branch and runs test + lint +
pipeline smoke.
The visual identity (logo, favicon, social card, wordmarks) lives in the canonical n3ary/branding repo and is served at https://branding.n3ary.com. This repo doesn't ship any brand assets — if you need the GitHub org avatar, the repo social preview, or a logo, pull from there (see the SETUP.md there for the URL map).
Schedule data © its respective transit operators (per-feed
license.attribution_text in feeds.json). Generated for public transit
information purposes.
PolyForm Noncommercial License 1.0.0 — free for individuals, hobbyists, education, research, and charitable organizations. Any commercial use (paid products, paid services, or hosted services for revenue) needs a separate license from the author. See the LICENSE file for the full terms.