How a transit agency is described to Transit, which providers are active and what it takes for a provider to appear on the public site.
| Reference | Value |
|---|---|
| Subject and boundary | Provider manifests, the registry, the public catalog and the path for adding a provider. Variable names are on ‣. |
| Verified source | config/providers, registry.py, snapshots/providers.py, core/models.py, live_policy.py, health/checks.py at main 9733eb26d92533f56cdc8bc46b81b93fbb5987c8, checked November 10, 2026. |
| Support state | STM and OC Transpo active; STO an inactive template. Configuration alone does not establish public availability or permission to use a source. |
| Provider | Manifest | State | Feeds | Feed authentication |
|---|---|---|---|---|
| STM (Montréal) | stm.yaml |
Active, public | Static GTFS, GIS, trip updates, vehicle positions, i3 alerts | Realtime and i3 use STM_API_KEY in the apiKey header |
| OC Transpo (Ottawa) | octranspo.yaml |
Active, public | Static GTFS, trip updates, vehicle positions | Realtime uses OCTRANSPO_API_KEY in Ocp-Apim-Subscription-Key |
| STO (Outaouais) | sto.yaml |
Inactive template | Static GTFS, trip updates, vehicle positions, service alerts | Key plus a per-minute SHA-256 hash in query parameters |
STO stays inactive until its required source statement and actual data update date are filled in. The data proxy answers /data/v1/sto/* with HTTP 410.
| Section | Holds |
|---|---|
provider |
ID, display name, timezone, bounds, city, attribution text, is_active, live_source_lag_seconds, max_vehicle_speed_kmh |
public |
enabled, EN/FR labels, map fit and max bounds, geocode context, basemap URL and bounds, poster index, agency alert links |
feeds |
One entry per endpoint: endpoint_key, feed_kind, source_url_env_var, auth type and credential variable, refresh_interval_seconds and optional refresh_phase_seconds |
realtime |
lines_without_live_data: lines the provider publishes no live data for |
| Key | STM | OC Transpo | Effect |
|---|---|---|---|
feeds.*.refresh_interval_seconds, refresh_phase_seconds |
Vehicles 20 s at phase 10 s; trip updates 30 s; i3 alerts 300 s | Vehicles 20 s, unphased; trip updates 30 s; no alerts feed (#746, merge 9c713e39) | Capture cadence (see ‣) |
provider.live_source_lag_seconds |
0 (default) | 60 | Published as manifest.files.live.source_lag_s; the web app widens its Not reporting cutoff and estimated-position horizon by it |
provider.max_vehicle_speed_kmh |
130 (default) | 130 (default) | Silver nulls a reported speed above the cap, negative or non-finite, keeping the row; the default was raised from 100 in #742 because highway buses report 101–117 km/h |
realtime.lines_without_live_data |
metro_realtime: route type 1 |
otrain_realtime: route type 0 |
Declared lines are left out of the live silent and non-responding trip counts (gold.scheduled_running_trips_at). The daily silent_trip_days rollup (gold/rollups.py) still counts them. STO declares none |
public.basemap_bounds |
−75, 44.8 to −72.5, 46.3 | −77.2, 44.5 to −74.2, 46.3 | Extract area for the provider's basemap archive, wider than the map's max bounds |
A lines_without_live_data entry has a gap_id and selects routes by route_ids, route_types or both; the static load syncs it into core.providers. Each realtime Silver load records the last time a declared route appeared in vehicle positions or trip updates in core.route_live_observations. A declared line seen within the last 15 minutes is promoted automatically and counted like any other line. Any recorded observation also makes the health API report a degraded component <provider>_live_policy_<route> saying the configuration is outdated, so the owner can update the manifest.
Secrets never appear in a manifest; each feed names the environment variable that holds its URL or key. list-providers and scheduled jobs use active manifests only. seed-core records every manifest, so inactive state is explicit in core.providers.
transit-ops publish-providers --default-provider <id> builds /v1/providers.json; the default provider is a required option. A provider is included only if all of these hold:
public.enabled and provider.is_active are true, with a city and bounds.manifest.json matches the configured ID, timezone and bounding box, and its generation ID belongs to that provider.The web app discovers cities from this catalog. Providers with public.enabled=false are skipped. A readiness failure on an enabled provider raises and aborts the whole catalog build; providers.json is written only after every enabled provider and the selected default pass, so that validation failure leaves the existing catalog unchanged. publish-providers is run by an operator; no workflow runs it.
Provider IDs are always explicit inside the publisher: test_explicit_provider_ids.py walks the snapshots package syntax tree and fails on any provider_id parameter or field default. A provider without a GIS feed, such as OC Transpo, gets gis_status: not-configured from the static pipeline instead of a failure.
The web build reads the same manifests for share cards: build-og.ts renders an EN and FR home card for each provider with is_active and public.enabled, using its public labels.