Repository navigation
Description
Problem
#1846 added staticGtfsHash to /api/v2/metadata.json so Watchdog can tell whether it is validating realtime data against the same static bundle Maglev serves. Watchdog downloads the same feed, takes the SHA-256 of the zip bytes, and compares it with staticGtfsHash. If they differ, it re-downloads. If they still differ after that, it reports that either its feed URL is wrong or the backend is running on outdated data.
The multi-feed spec in #1811 changes what that value means. staticGtfsHash reads import_metadata.file_hash. Under section 6 ("Import pipeline"), step 3, that column becomes a dataset fingerprint: a SHA-256 over the ordered import_feeds rows plus the hash of the stop consolidation file. Section 4.3 also notes that single-feed deployments get a new ETag on upgrade.
Once that lands, Watchdog can no longer use staticGtfsHash:
- Even with one feed, the value won't equal the SHA-256 of the zip, so every Maglev server would look out of sync.
- Watchdog can't compute the fingerprint itself. It doesn't have the feed ids, the source URL or path Maglev used, each feed's default agency, the stop consolidation file, or the exact format the rows are combined in before hashing.
- In a multi-agency region, one combined hash can't tell Watchdog which feed is out of date.
The spec doesn't mention staticGtfsHash, so nothing currently stops its meaning from changing under the same field name.
Proposed solution
Phase C step 3 already plans to list the feeds in /metadata and the health endpoint. Each entry in that list should include the SHA-256 of that feed's zip, so clients can compare per feed:
{
"staticGtfsFeeds": [
{ "id": "kcm", "source": "https://example.com/kcm.zip", "sha256": "aac8f1d4..." },
{ "id": "st", "source": "https://example.com/st.zip", "sha256": "5b21e09c..." }
]
}The field names are only an example. What matters is that sha256 is the hash of the zip bytes as downloaded, before gtfstidy or any other processing, which is the same value the import already computes for each feed. Watchdog then compares the set of hashes it downloaded with the set Maglev lists, for one feed or many, and can say which feed is out of date.
For staticGtfsHash itself, either:
- keep it as the raw zip SHA-256 when there is exactly one feed, or
- move the fingerprint to a separate field (e.g.
staticGtfsFingerprint) and document that it isn't a file hash.
Acceptance criteria
/api/v2/metadata.jsonlists every loaded static feed with its id, source, and the SHA-256 of its zip as downloaded.- With one feed, that feed's
sha256equalssha256sumof the zip at its URL. - The meaning of
staticGtfsHashafter multi-feed import is documented in the spec and on the wiki (Document staticGtfsHash and metadata.json on the wiki #1859). - The spec (Rewrite the multi-feed static GTFS spec #1811) mentions the per-feed hashes in Phase C step 3.
References
- Rewrite the multi-feed static GTFS spec #1811: multi-feed static GTFS spec (
docs/multi-agency-static-feeds-spec.md) - Expose the static GTFS hash in metadata #1846, Expose the static GTFS hash for Watchdog sync #1845:
staticGtfsHashin/api/v2/metadata.json - Document staticGtfsHash and metadata.json on the wiki #1859: wiki docs for
staticGtfsHash - Feat/refresh static on agency mismatch watchdog#193: Watchdog's current agency-based check, which the hash check is meant to extend
Activity
Enigma044 commentedon Oct 31, 2026
I'd like to work on this. I've started with the single-feed case: a
staticGtfsFeedslist on metadata and health, built from the stored import metadata, with the existingstaticGtfsHashunchanged. The per-feed list with ids depends on Phase C of #1811, so I'd extend it after that merges.Ahmedhossamdev commentedon Oct 31, 2026
This depends on Phase C of #1811 (multi-feed import). Step 3 of that phase adds the per-feed metadata, lists the feeds in
/metadataand the health endpoint, and switchesimport_metadata.file_hashto the dataset fingerprint. The per-feedsha256should be added there, taken fromimport_feeds.hash, so the code for this issue waits until Phase C step 3.Until then, a single-feed
staticGtfsFeedslist would only repeatstaticGtfsHashand would need rework once feed ids andimport_feedsexist, so let's hold off on it. The spec additions (per-feed hashes in Phase C step 3 and the meaning ofstaticGtfsHashafter the change) can go into #1811 in the meantime.Ahmedhossamdev commentedon Oct 31, 2026
@Enigma044 thanks for opening #1910. I've converted it to draft: the
staticGtfsFeedslist waits for Phase C step 3 of #1811, as noted above, and the gtfstidy hash fix should move to its own issue and PR, coordinated with #1676. Details are in #1910 (comment).Ahmedhossamdev commentedon Oct 31, 2026
@Enigma044 one more thing, and thanks again for the interest. This issue was already assigned to me when #1910 was opened. In future, please check the assignee before starting work, and if an issue is assigned to someone else, ask on the issue first. CONTRIBUTING asks that a PR links an issue a maintainer has triaged or assigned to you. That way two people don't spend time on the same change, and the assignee can say whether the work is ready to start (here it depends on Phase C). If you'd like to take the gtfstidy hash fix, open an issue for it and I'm happy to assign it to you.
Enigma044 commentedon Oct 31, 2026
Thanks, understood. I'll wait for Phase C step 3 for the feed list. I should have checked the assignee first. I've opened a separate issue for the gtfstidy hash fix: #1911. I'll ask for it to be assigned to me before opening a PR.