Server operations
The race server (party/, a Cloudflare Worker with the D1 database drag-racer) holds every account, garage and judged race. This page is what keeps it safe and what to do when it is not.
Backups: D1 Time Travel
Section titled “Backups: D1 Time Travel”D1 keeps a restore point for every minute, always on and free: 7 days on Workers Free, 30 days on Workers Paid (docs: https://developers.cloudflare.com/d1/reference/time-travel/, read 2026-11-12). There is no backup job of our own.
A restore overwrites the live database in place, so put the server in maintenance first:
npm --prefix party run maintenance -- on "Down for maintenance until 14:00 UTC."cd partynpx wrangler d1 time-travel info drag-racer --timestamp 2026-11-12T09:00:00Z # check the pointnpx wrangler d1 time-travel restore drag-racer --timestamp 2026-11-12T09:00:00Znpm run maintenance -- offKeep the bookmark the restore prints: restoring to it (--bookmark) undoes the restore. Time Travel does not cover a deleted database after 7 days or a mistake found later than its window; an off-site copy would, and is left for after launch.
The restore drill
Section titled “The restore drill”npm --prefix party run db:drill (party/scripts/restoreDrill.ts) proves the data can be rebuilt: it exports drag-racer to SQL, imports it into a new database drag-racer-restore-test, compares the row count of every table and deletes the copy. It only reads the live database. Run it before a launch, after a migration that reshapes tables, and every few months. First run: 2026-11-12, 15 tables, all equal.
Error alerts: Sentry
Section titled “Error alerts: Sentry”party/src/index.ts wraps the worker and both Durable Objects (RaceRoom, RoomIndex) with @sentry/cloudflare. Uncaught errors go to Sentry, which mails Ruben and pushes to its phone app. Without the SENTRY_DSN secret (local wrangler dev, the checks) nothing is sent and nothing else changes.
Set up 2026-11-12: project neon-drag-server in org stackly-at (EU region), its DSN as the SENTRY_DSN secret, alerts for new issues by mail (Sentry’s default rule). A test error arrived the same evening.
Uptime check: Sentry Uptime
Section titled “Uptime check: Sentry Uptime”GET /api/health (party/src/health.ts) answers 200 when the worker runs and D1 answers SELECT 1, 503 when D1 does not. Errors only reach Sentry from code that runs; a worker that does not answer at all (a bad deploy, a Cloudflare outage, D1 down) throws nothing, so an outside check calls this route. Sentry’s free plan includes one uptime monitor (https://sentry.io/pricing/, read 2026-11-13): it calls https://drag-racer-party.drag-racer-party.workers.dev/api/health and opens an issue, which mails Ruben, when it fails. The health route stays open in maintenance mode, so a planned stop does not page anyone.
Maintenance mode
Section titled “Maintenance mode”npm --prefix party run maintenance -- on "Down for maintenance until 14:00 UTC."npm --prefix party run maintenance -- offparty/scripts/maintenance.ts sets or deletes the MAINTENANCE secret; Wrangler rolls out a new version with it in seconds, no code deploy. While it is set, maintenanceResponse() (party/src/maintenance.ts) answers every /api call and room with status 503 and { maintenance: message }, before any route runs. Open: /api/health, AdMob’s reward callback (/api/admob/ssv, so a reward watched just before is not lost) and CORS preflights. The message is shown to players: write it through rsp:write-content.
In the game, core/maintenance.ts reads each answer (accountApi.ts for the API, net/roomIndex.ts for the room list). A maintenance answer throws, so every caller takes its offline path, and the message replaces the offline text:
- the first time it is seen, a toast shows it on the current screen (
boot/maintenanceNotice.ts); - the career hub’s cover reads “Maintenance” with the message (
hub/view/offlineScreen.ts); - a judged race (career, quick, weekly) does not start: the player goes back to the race’s home screen with the message (
race/judgedStart.ts), instead of racing a race that cannot count; - shops, the inbox and ad rewards answer with the message instead of “No connection” (
noConnectionText()inplayer/account/garageRequest.ts); - the online room list and joining show it instead of “Cannot reach the race server” (
unreachableText()innet/joinRoom.ts).
Test runs, the garage and the story flashback keep working. Any answer that is not a maintenance 503 ends it. This is not the force update path (core/serverStop.ts, Online races): that one stops the game, maintenance only pauses what needs the server. While the server is in maintenance an outdated app gets the maintenance answer first and the update screen afterwards.
Local check: npx wrangler dev --port 8790 --var "MAINTENANCE:Down for maintenance." in party/, then VITE_PARTY_HOST=ws://localhost:8790 npm run shot -- --scene Race ends on the career hub with the message.