--- name: SyncGames Path Drafts overview: Build SyncGames on Path 3 (NAS MinIO + Python agent on Linux, light Android UI app). WAN access via Cloudflare Tunnel to paid domain. Path 1 is fallback. Planning markdown archived in-repo for posterity. todos: - id: scaffold-repo content: Create SyncGames/ skeleton including docs/planning/ archive of all plan markdowns status: in_progress - id: archive-plans content: Copy Cursor plan + path drafts into SyncGames/docs/planning/ for posterity status: pending - id: nas-minio-cloudflare content: Document MinIO on NAS behind existing NGINX + Cloudflare Tunnel hostname, TLS, Access/auth, S3 path-style, nginx anti-grief settings status: pending - id: agent-core content: Implement Python CLI/daemon - lease, pull-live, push-live, list-history, restore, add/remove game status: pending - id: safety-gates content: Hash gate, atomic promote, version prune (N=3-5), refuse mid-write / mid-lease operations status: pending - id: linux-automation content: systemd user units + process/Steam AppID watchers for auto start/end session (WAN endpoint same as laptop) status: pending - id: android-ui content: Light Kotlin/Compose Android app - Start/End/History/Restore/Status talking MinIO over Cloudflare HTTPS status: pending - id: protocol-spec content: Written protocol doc so Android and Python stay in lockstep on meta.json, leases, keys status: pending - id: path1-fallback-notes content: Document Path 1 fallback trigger criteria and migration steps from MinIO SSOT tree status: pending - id: first-games content: Seed configs for DS1/2/3, Elden Ring Seamless, Eden/Yuzu platform profiles status: pending isProject: false --- # SyncGames — Path 3 Implementation Plan ## Locked decisions | Decision | Choice | |----------|--------| | Primary architecture | **Path 3** — MinIO SSOT on NAS + session agents | | Linux agent | **Python** CLI/daemon | | Android client | **Light UI app** (Kotlin + Jetpack Compose) — not Termux as primary | | Remote access | **WAN via Cloudflare** (paid domain) — not LAN-only, not VPN | | Fallback | **Path 1** — Hardened Syncthing on SSOT tree only | | Planning artifacts | **Kept in-repo** under `SyncGames/docs/planning/` | Path 2 (Gitea) remains shelved unless explicitly reopened. --- ## Planning docs for posterity On scaffold, archive all planning markdown into the repo so it survives outside Cursor’s plan UI: ``` SyncGames/docs/planning/ 00-README.md # index of planning docs 01-path-drafts.md # original 3-path comparison 02-path3-implementation.md # this implementation plan (canonical copy) syncgames_path_drafts_2b07b18f.plan.md # Cursor plan export snapshot ``` Update the archive whenever the plan materializes major revisions. Operational docs (`architecture.md`, `nas-minio.md`, etc.) stay separate under `docs/` as living guides. --- ## Session model (unchanged) ```mermaid stateDiagram-v2 [*] --> Idle Idle --> Pulling: start_session Pulling --> Ready: SSOT_copied_to_WIP Ready --> Playing: game_running Playing --> Pushing: end_session Pushing --> Idle: SSOT_updated_and_versioned ``` - **Pull** only at session start; **push** only at session end - One **lease** per game; second device blocked until release/expiry - **Hash gate**: push rejected if WIP parent ≠ current live unless force-restore - Per-device **history** keeps last N snapshots (default 5); live is a single slot - Never sync the game’s native save directory continuously --- ## Path 3 architecture (WAN) ```mermaid flowchart TB subgraph clients [Clients_on_WAN] PC[Linux_PC_Python] Laptop[Laptop_Python] Phone[Android_Compose_app] end subgraph cf [Cloudflare] DNS[Paid_domain_DNS] Tunnel[Cloudflare_Tunnel] Access[Optional_CF_Access] end subgraph nas [NAS] Nginx[NGINX_reverse_proxy] MinIO[MinIO_S3_API] end PC --> DNS Laptop --> DNS Phone --> DNS DNS --> Access Access --> Tunnel Tunnel --> Nginx Nginx --> MinIO ``` All devices (desktop, laptop, phone) use the **same public HTTPS endpoint**, e.g. `https://syncgames-s3.example.com`. No LAN assumption, no VPN requirement. **Ingress chain (matches your NAS pattern):** Cloudflare → Tunnel → **existing NGINX** → MinIO. NGINX stays the single reverse-proxy front door for all services; we do not bypass it. ### NGINX + MinIO — will it cause grief? **Verdict:** No, if we treat MinIO as an S3 API vhost (not a generic web app) and apply the known proxy checklist. Most LOPE-adjacent failures from reverse proxies are **truncated uploads** or **broken SigV4 signatures** — both avoidable. | Risk | Mitigation | |------|------------| | SigV4 `Host` mismatch | Public hostname must be what clients sign; NGINX must forward that Host (or MinIO must be configured for the same domain). Do not rewrite Host to `127.0.0.1`. | | Truncated / buffered PUTs | `proxy_request_buffering off`; `client_max_body_size` large enough (e.g. 512m+); `proxy_http_version 1.1` | | Expect: 100-continue quirks | `proxy_set_header Expect $http_expect;` or strip carefully per MinIO docs — test with a real multipart upload | | Underscore / odd S3 headers | `ignore_invalid_headers off` on the MinIO server block | | Chunked encoding / redirect loops | Prefer path-style addressing to one API hostname; avoid redirecting API → console | | Console vs API confusion | Expose **S3 API only** on `syncgames-s3.`; keep MinIO Console on a separate optional vhost or LAN-only | | Cloudflare body limits | Same as before — game saves are usually fine; document if huge dumps fail | | Double TLS / WebSockets | Console needs Upgrade headers; **API path used by SyncGames does not need WebSockets** | **Recommended NGINX shape (to ship in `docs/nas-minio-cloudflare.md`):** dedicated `server_name syncgames-s3.`; `location /` → `http://127.0.0.1:9000` (MinIO API); forwarding `Authorization`, `X-Amz-*`, `Content-Type`, `Content-Length`, `X-Forwarded-Proto https`; no `proxy_buffering` on uploads; no auth_basic in front of S3 (breaks SDK) — use Cloudflare Access and/or MinIO keys instead. Agents point at `https://syncgames-s3.` with path-style S3. `syncgames doctor` will include a PUT/GET round-trip probe to catch NGINX misconfig early. ### Cloudflare exposure (chosen approach) 1. Run **MinIO** on NAS private bind (e.g. localhost:9000) 2. Add **NGINX** vhost for the SyncGames S3 hostname (same pattern as your other services) 3. Point **cloudflared** at NGINX (or at the existing tunnel ingress that already hits NGINX), not directly at MinIO, so all services stay consistent 4. Map `syncgames-s3.` on the paid domain 5. Prefer **Cloudflare Access** in front (defense-in-depth); MinIO access key / secret still required 6. Document path-style addressing for boto3 + Android AWS SDK 7. Note CF upload size limits; fallback guidance if an emulator dump exceeds them Laptop and phone never need to be on home LAN. --- ### Object key layout ``` games//live/ games//history/// games//meta.json ``` `meta.json` fields (minimum): schema version, live checksums, parent hash, lease holder device id, lease expires_at, versions_to_keep, updated_at. ### Ops 1. **start_session**: acquire lease → download `live/` → install to native save path(s) → record WIP parent hash locally 2. **end_session**: wait until game not writing → copy native → WIP → upload history prefix → promote to `live/` → update `meta.json` → release lease → prune old history beyond N 3. **restore**: user picks history object prefix → copies to `live/` (explicit only) 4. **add / remove game**: declarative TOML + bucket prefix init / archive-to-`retired/` ### Shared protocol spec `docs/protocol.md` is the source of truth for key layout, lease rules, and hash gates so **Python and Android stay compatible** without sharing a runtime. ### Agent CLI surface (Linux) ```bash syncgames add --name "..." --platform steam --paths "..." --versions 5 syncgames remove syncgames start syncgames end syncgames status [] syncgames history syncgames restore --from / syncgames doctor ``` --- ## Repo skeleton ``` SyncGames/ README.md docs/ planning/ # posterity: path drafts + plan snapshots architecture.md protocol.md add-game.md nas-minio-cloudflare.md fallback-path1.md config/ devices.toml agent.toml # Cloudflare HTTPS endpoint, bucket, device-id games/*.toml templates/game.toml agent/ # Python package (Linux PC + laptop) android/ # Kotlin/Compose light UI app systemd/ syncgames-agent.service syncgames-watch@.service ``` --- ## Android light UI — yes, in scope for v1 **Answer:** Yes. Implement Android as a **light dedicated UI app**, not Termux-first. **Stack:** Kotlin + Jetpack Compose + AWS S3 / MinIO-compatible SDK over HTTPS to the Cloudflare hostname. **Screens (minimal):** - Game list + lease/status badge - **Start session** (pull + lease + write into configured save dirs) - **End session** (push + history + release) - **History** + **Restore** (explicit confirm) - Settings: endpoint URL, keys (prefer Android Keystore), device id, game path pickers via Storage Access Framework **Deployability:** sideload APK or simple GitHub/Gitea release; configure once with Cloudflare URL + credentials; play flow is button → play → button. **Why not Termux as primary:** harder to hand to “just use it” and weaker UX for safeties (confirm restore, show lease holder). Termux may remain a documented escape hatch only. Linux still owns `add`/`remove` game and systemd automation; Android focuses on session buttons + restore for emulator titles on phone. --- ## Linux automation - systemd user service runs the Python agent - Per-game watcher: Steam AppID and/or process name from game TOML - `start` / `end` on process lifecycle - Endpoint in `agent.toml` is always the Cloudflare WAN URL (same as laptop/phone) for consistent behavior away from home --- ## Path 1 fallback triggers - Cloudflare/MinIO WAN path too unreliable or blocked by CF limits - Android app file-access friction exceeds Syncthing-Fork convenience **and** you accept SSOT-dir-only Syncthing rules **Migration:** export MinIO `live/` + `history/` to a folder tree; Syncthing that tree only; keep Python session agent for native ↔ SSOT copies. --- ## Seed game configs (after agents work) - Dark Souls 1 / 2 / 3 — Steam / Proton + Seamless Coop paths - Elden Ring — Seamless Coop paths - Eden / Yuzu — Android + Linux emulator save profiles --- ## Out of scope for v1 - Path 2 Gitea - Windows-native agent - Automatic binary merge - Continuous sync of live game directories - VPN / LAN-only client modes as a requirement (WAN Cloudflare is the path) --- ## Paths 1–2 for posterity Full Path 1 and Path 2 writeups are archived under `docs/planning/01-path-drafts.md` when the repo is scaffolded — not only in chat history.