A personal UK rail companion: TfL-style line status, live train tracking, and ticket/Delay-Repay support — with first-class handling of operators whose routes share trunk track, so an incident is only
翻譯準備中
生活風格Rust · TypeScriptGitHub @FasterSpeeding上線中營運者未確認
試用過了嗎? 點一下就好,不需要登入。
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證A personal UK rail companion: TfL-style line status, live train tracking, and ticket/Delay-Repay support — with first-class handling of operators whose routes share trunk track, so an incident is only ever flagged on the lines it actually affects.
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
A personal UK rail companion: line-status aggregation in the TfL-Unified-API
style, individual train tracking, accounts, and ticket/Delay-Repay support.
It has first-class support for operators with multiple parallel
routes that share trunk track (SWR, Southeastern, Northern, etc.) — knowing
the difference between an incident on a shared trunk (which should
propagate to every line using that trunk) and an incident on an *exclusive
segment* (which should not) is this project's original core and still its
real differentiator.檢視完整 README ↗A personal UK rail companion: line-status aggregation in the TfL-Unified-API style, individual train tracking, accounts, and ticket/Delay-Repay support. It has first-class support for operators with multiple parallel routes that share trunk track (SWR, Southeastern, Northern, etc.) — knowing the difference between an incident on a shared trunk (which should propagate to every line using that trunk) and an incident on an exclusive segment (which should not) is this project's original core and still its real differentiator.
crates/ — a 25-crate Rust workspace (see the root Cargo.toml):
api, aggregator, enricher, notifier;poller-* crates: poller-incidents, poller-stations,
poller-tocs, poller-ldbws, poller-tfl, poller-irish-rail-gtfs,
poller-irish-rail-live, poller-nir-stations;movement-relay, trust-consumer,
full-coverage-consumer, trust-backlog-consumer;schedule-ingest, schedule-reference;common, trust-schema, movement-feed, health-http,
schedule-query, trip-planner;line-catalogue-validator CLI.frontend/ — the Next.js web frontend.charts/distant-signal/ — the Helm chart for deploying the whole stack.lines/ — the curated TOML line-definition catalogue, one file per line
(format in lines/SCHEMA.md).scripts/ — CI and maintenance tooling (Python, managed with uv; see
below).See DESIGN.md for the full architecture.
For local development, see docker-compose.yml (its header explains the
local.env / dev.env modes). For a real deployment, see
charts/distant-signal/README.md for the Helm chart.
Local dev runs the ingest paths production runs: an ingest-writer, and
every producer writing Postgres or a Redis stream directly (ingest phase 5,
release B, removed the api's /private/* routes; see the "Ingest" note in
docker-compose.yml's header and docs/ingest-phase5-runbook.md).
The Python tooling is managed with uv:
pyproject.toml pins the lint tools in its lint dependency group,
uv.lock pins the full tree, and .python-version the interpreter. With uv
installed (e.g. mise use -g uv):
uv run scripts/lint-scripts.py # what CI's scripts-lint job runs
uv run scripts/lint-scripts.py --fix # apply shfmt/ruff fixes first
uv run python -m unittest discover -s scripts/tests # the scripts' tests
# Before merging a branch that adds migrations (CI's migration-order job):
uv run scripts/check-migration-order.py "$(git merge-base main HEAD)"
A migration with destructive DDL (a DROP of a table, column, view or
function, a RENAME, a column type change, SET NOT NULL on an existing
column) needs a -- contract: <what> (code stopped using it in <commit>)
line in its leading comments, and ships a release after the code stopped
using the object; check-migration-order.py enforces the header (see its
docstring).
uv run creates .venv/ and installs the lint group on first use. It runs
shellcheck and shfmt over every *.sh, ruff and mypy over scripts/,
actionlint over the workflows, hadolint over the Dockerfiles, and
scripts/lint-containers.py.
What fails CI and what only warns:
dependency-scan.yml (OSV-Scanner over Cargo.lock,
frontend/package-lock.json and uv.lock) blocks on a finding with
CVSS >= 9.0 that has a fixed version, and on a scan that produced no valid
JSON. Every other HIGH/CRITICAL (CVSS >= 7.0) finding warns: a
per-finding annotation and a neutral "security findings (non-blocking)"
check. The daily scheduled run never blocks on findings; it opens or
updates a dependency-vulnerability issue instead. Time-boxed exceptions
live in frontend/osv-scanner.toml.ci.yml's cargo-deny job blocks on cargo deny check bans licenses sources and on a failed advisory-DB fetch. cargo deny check advisories
(RustSec and yanked crates) only warns.Each station on a line belongs to a named segment. When the same segment
name appears across multiple line definitions, the system treats it as a
shared trunk — incidents there propagate to every line using that segment.
The matcher classifies every incident-to-line match by scope:
EXCLUSIVE_SEGMENT — incident's stations all sit on segments unique to
this line. Highest confidence.SHARED_SEGMENT — at least one of the touched segments is shared.
Status propagates to all lines using that segment, with a "shared trunk"
annotation in the reason text.STATION_HIT — line/station overlap but no segment metadata to classify.KEYWORD_ONLY — line is named in the incident text but no station hits.
Capped at Severe Delays.OPERATOR_ONLY — only operator overlap. Capped at Minor Delays, and
suppressed entirely if another line sharing one of its operator codes got
a more precise match for the same incident.Knowledgebase incidents carry no station codes, so the station tiers are fed by the stations an incident's text names, resolved against the station reference data. An incident naming a place is shown only on the lines through it; it is shown operator-wide only when its text is network-wide (industrial action, a reduced timetable) or names no place at all.
The last point matters: it's what stops an incident on the Alton branch
from also flagging South West Main and Portsmouth Direct just because all
three share the SW operator code.
For a TOC like SWR with multiple routes:
swr-south-west-main.toml,
swr-portsmouth-direct.toml, etc.).swr-trunk-waterloo) on all the lines
that share trunk track. Junction stations belong to the shared trunk;
exclusive segments start at the next station.destination_crs_filter (and/or headcode_prefixes) so LDBWS
inference at shared stations counts only the line's own services.match_keywords for any colloquial line names ("Portsmouth Direct",
"Alton line").We use TfL's 0-14 scale verbatim where it applies, then add two NR-specific
values (Recovering = 20, Diverted = 21) outside that range. TfL's own
codes 16-20 (Not Running, Issues Reported, No Issues, Information, Service
Closed) arrived later and are stored as 22-26, because 20 and 21 were
already taken (see common::Severity). The numbers are not ordered by how
bad a status is; compare through common::severity_rank.
reason over anything we infer.min_sample_size).dataQuality on every status.