What data Transit keeps, for how long, how it is backed up and which guarded commands repair each kind of failure.
| Reference | Value |
|---|---|
| Subject and boundary | Retention defaults, backups and restore proof, guarded recovery actions, replay and repair commands, and credential rotation for retained data. Day-to-day monitoring is on ‣. |
| Verified source | settings.py, recovery.py, backups.py, restore-backup-proof.sh, cli.py at main 9733eb26d92533f56cdc8bc46b81b93fbb5987c8, checked November 10, 2026. |
| Support state | Implemented commands. Defaults are source values; a deployment can override them through environment variables. Every mutation below is an operator action against production and needs the owner's explicit approval. |
| Data | Default | Setting |
|---|---|---|
| Silver static datasets | 1 current dataset | STATIC_DATASET_RETENTION_COUNT |
| Bronze static, realtime and i3 objects | 30 days | BRONZE_STATIC_RETENTION_DAYS, BRONZE_REALTIME_RETENTION_DAYS, BRONZE_I3_RETENTION_DAYS |
| Silver realtime | 1 day | SILVER_REALTIME_RETENTION_DAYS |
| Silver closed i3 alert history | 90 days | SILVER_I3_CLOSED_RETENTION_DAYS |
| Gold detail facts | 14 days | GOLD_FACT_RETENTION_DAYS |
| Gold warm rollups | 730 days | GOLD_WARM_ROLLUP_RETENTION_DAYS |
| Logical database backups | 7 copies | BACKUP_RETENTION_COUNT |
Longer reporting horizons come from warm rollups, not facts. Because Silver realtime expires before Gold, some repairs first need a bounded Silver restore from Bronze. A retained aggregate does not guarantee access to the raw inputs needed for a correction. A retention change must update Settings, .env.example, Compose defaults and tests together.
uv run transit-ops retention-proof-report stm --report-path retention-proof.json writes a non-destructive proof. Exit 1 means a dry-run section or static-feed validation is unavailable or invalid. Pruning runs continuously in the pruner service (Silver, Gold) and in the Warm workflow's retention job (i3, warm rollups, Bronze).
scripts/backup-postgres.sh runs backup-database in a one-off worker container from host cron. The cron entry lives on the host, outside the repository; the repository records only its expected daily 09:30 UTC run, in the verify-backup-freshness help text of cli.py, so the schedule and crontab owner are a documentation gap here. The script streams a pg_dump (zstd:3) to the Bronze bucket under backups/postgres. Table data for silver.rt_trip_update_stop_times is excluded by default. list-backups, download-latest-backup and verify-backup-freshness read the backup set; the hourly freshness-probe job runs the last one and opens an alert issue on failure.
bash scripts/restore-backup-proof.sh restores the newest backup into a throwaway local PostgreSQL 16 cluster and checks recorded revision, critical data, exclusions, PostGIS and durability settings.
| Setting | Default | Purpose |
|---|---|---|
RESTORE_WORKDIR |
/tmp/transit-restore-proof |
Must not exist; created privately |
PG_BIN |
/usr/lib/postgresql/16/bin |
PostgreSQL 16 binaries with PostGIS |
RESTORE_PORT, RESTORE_JOBS |
55434, 4 |
Socket port and parallel restore jobs |
RESTORE_DUMP_FILE |
unset | Use a local dump; no download is required |
RESTORE_MIN_FREE_GB |
30 |
Free space required |
RESTORE_EXPECTED_REVISION |
unset | Defaults to the repository's Alembic head |
KEEP_RESTORE_WORKDIR |
0 |
1 keeps the cluster for tests/test_restore_proof_real_db.py |
The script reports download, restore, smoke and total seconds. A successful drill proves that backup under those host conditions; it does not establish production recovery time.
The document's HTML transform injects the shared v0.39.1 boot script, with its CSP hash supplied by the pinned package. Server HTML contains readable section bodies. On pages with at least two eligible sections and the bulk control, normal initialization applies sections-choice or the first-visit collapsed state.
If app code fails or stalls during initialization, boot recovery reveals pending sections on script/runtime errors, unhandled rejections or the five-second deadline. It clears pending closed/inert presentation, hides/disables incomplete bulk controls and marks the fallback so delayed initialization honors readable content. Recovery does not write or clear the saved bulk choice. With JavaScript unavailable, the server bodies provide readable content.
This path concerns section markup, separate from service-worker caches and database recovery. Inspect section-body visibility and focus/accessibility state when diagnosing it. Source support and the live commit receipt do not prove a failure scenario on every device.
The service worker caches shell assets and fonts; navigation remains network-first, with cached /offline used only when the network fetch fails. Its policy bypasses transit data and Range requests, so clearing this shell cache neither repairs published snapshots nor provides offline transit data.
The kill switch is /sw-kill.json. With disabled: true, page registration unregisters existing service workers, deletes the origin's caches and skips registration. The worker checks at activation, on CHECK_KILL and on navigation at most once per five minutes; when disabled it deletes caches, attempts unregister and sends SW_KILLED to clients. Offline clients cannot observe a new flag until they reconnect. Cache deletion and unregister are separate best-effort browser operations, not an instantaneous fleet-wide rollback.
The tracked flag at this revision is disabled: false. A kill-switch change and deployment belong to a separate operator packet; none is performed here. Verify the served flag, registration state, cache removal and network navigation on a connected affected client before claiming recovery. This browser-shell mechanism does not change database retention or restore data.