Skip to content

Cloud save backup — design (draft)

Status: DRAFT. The design was presented to Chris 2026-10-04 and is awaiting his explicit approval. Next: get the yes, finish this spec (self-review), then writing-plans. Execute AFTER docs/superpowers/plans/2026-10-04-progress-reset.md, which this builds on (progress dialog, MetaStore.wipe_progress).

The progress-reset spec’s backups (meta.backup-<BUILD>.json, the meta.undo.json undo slot) live in the app’s private storage on the player’s device. Deleting the app deletes them, support can’t reach them, and on tvOS they sit in the purgeable cache dir (the NSUserDefaults bridge mirrors only meta.json). Chris: “we need to allow backups to be saved and restored to” (off-device).

  • Home: our own Worker + restore code (chosen over iCloud, which would be Apple-only and needs native fork code, and over Nakama, which needs ops and is overkill for backups).
  • Built account-shaped so a future login (Sign in with Apple / Game Center / Google) attaches to the same player id without migrating anything. Chris asked whether this sets up login + a persistent world. Answer: login yes; a persistent world no (it needs authoritative server state, e.g. Durable Objects or a game server, and per CLAUDE.md that direction lives in ~/Claude/sovereign). Nakama (open-source game server, official Godot 4 client, Apple linking, storage, authoritative multiplayer) is the thing to evaluate if sovereign goes multiplayer.
  • Cadence: auto-upload after a run that changed the save (throttled, at most once every 10 min), always before any reset, plus on demand.
  • Consent: on by default, with a “Cloud backup” toggle in the progress dialog; off deletes the server copy. Independent of the Diagnostics toggle.
  • tvOS entry: an on-screen D-pad code picker (8 chars from a 32-symbol unambiguous set, no O/0 or I/1), the same UI on every platform.

Backend: new saves/ Worker bullet-heaven-saves + its own D1 (repo pattern: one Worker + D1 per concern, like telemetry/, control/; workers.dev URL).

  • Tables: players (id, restore_code_hash, created, last_seen), devices (token_hash → player), save_snapshots (player, build, schema, kind auto|pre_reset|manual, blob ≤ 64 KB, created), auth_identities (player, provider, subject; empty now, for future login).
  • Routes: POST /v1/players → {player_id, token, restore_code}; PUT /v1/saves (bearer token) keeps the latest auto + the last 10 pre_reset/manual; POST /v1/restore {code} → new device token + snapshot list; GET /v1/saves/:id; DELETE /v1/players/me.
  • Per-IP rate limit on restore. A cron deletes players inactive for 12 months (matching the telemetry promise). CORS for the web build.

Client: net/cloud_backup.gd, offline-tolerant with one pending upload (latest only). Player id, device token, restore code and the toggle live in meta.json (covered by the tvOS bridge) and join the progress-reset KEPT fields.

UI (progress dialog additions): the Cloud backup On/Off toggle; “Your restore code: DC-XXXX-XXXX” (re-showable); “Restore from code” → code picker → snapshot list (date, gold) → confirm. A snapshot whose schema this build can’t read is greyed out (“needs a different game version”). New installs are forced into the tutorial, so the progress row also appears in the tutorial pause menu, offering only “Restore from code”.

Privacy: a new kind of data. ASC label gains User Content > Gameplay Content (not linked, not tracking); the privacy page gets a backup section with retention; the new tables join the retention purge.

Needs Chris’s same-day OK: creating/deploying the Worker + D1, the privacy page deploy, the ASC label edit.