--- name: SyncGames Path Drafts overview: Greenfield SyncGames in a new `SyncGames/` directory. Below are three deployable architectures that all enforce the same session model (pull SSOT → play WIP → push SSOT) while differing in transport/storage. Pick one path before implementation. todos: [] isProject: false --- # SyncGames — Three Architecture Paths ## Non-negotiable session model (all paths) Every viable design shares the same state machine. Continuous bidirectional sync of **live** save directories is what caused your prior LOPE; none of these paths do that. ```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 note right of Pulling Refuse start if another device holds lease or SSOT is dirty end note note right of Pushing Snapshot WIP into device history then promote to live SSOT slot end note ``` **Shared layout (conceptual):** ``` SyncGames/ games// live/ # SSOT current slot (one winner) history/ / # last N snapshots per device (default 5) meta.json # hash, mtime, device, session lease, schema config/ games/*.toml # declarative game defs devices.toml ``` **Shared guardrails:** | Rule | Behavior | |------|----------| | Direction | Pull only at session start; push only at session end | | Lease | One active session per game; second device blocked until lease expires/release | | Hash gate | Reject push if local WIP hash != expected parent (stale WIP) unless explicit `--force-restore` | | Version retention | Keep N snapshots per device (configurable 3–5); prune oldest after successful new snapshot | | Never delete live | Promote via atomic rename/copy; old live moves into history first | | Game offline | Automation watches process exit / Steam AppID, never mid-write | **Add-game workflow (identical UX across paths):** ```bash syncgames add \ --name "Elden Ring (Seamless)" \ --platform steam \ --paths "~/.steam/.../SeamlessCoop/*.co2" \ --versions 5 ``` **Remove-game:** archive `games//` to `retired/-/`, stop watchers, revoke folder/remote access. --- ## Path 1 — Hardened Syncthing + Session Orchestrator **Idea:** Syncthing only syncs the **SSOT tree** (`live/` + `history/` + `meta`), never the game’s native save directory. A local agent copies between native save path ↔ WIP ↔ SSOT around sessions. ```mermaid flowchart LR NativeSave[Game_native_save_path] WIP[Local_WIP_staging] SSOT[SSOT_folder_Syncthing] OtherDev[Other_devices] NativeSave -->|"end_session copy"| WIP WIP -->|"snapshot + promote"| SSOT SSOT <-->|"Syncthing send-receive on SSOT only"| OtherDev SSOT -->|"start_session copy"| WIP WIP -->|"start_session install"| NativeSave ``` **Syncthing config hardening:** - Folder type: normal send-receive **only** on `SyncGames/games/` (or per-game subfolders) - Enable **Simple File Versioning** (Keep Versions ≥ 5) as a second safety net behind custom history - `.stignore` excludes `*.tmp`, lock files, WIP staging outside the synced tree - Do **not** sync Proton/prefix save dirs directly - Optional always-on home server / NAS as Receive-favoring mirror with staggered versioning **Automation:** Linux systemd user units + process watchers (Steam AppID / executable). Android: Syncthing-Fork for SSOT folder + Termux/foreground service or a thin companion app for “Start session / End session” when full auto is harder. | Pros | Cons | |------|------| | Fast LAN sync you already know | Must never point Syncthing at live saves (discipline + tooling) | | Mature Android client for files | Peer conflict resolution still exists if lease protocol fails | | Low custom infra | Orchestrator is still custom software | **Best if:** You want maximum automation across Linux + Android with minimal self-hosted backend. --- ## Path 2 — Custom Agent + Gitea (Git LFS / raw releases) **Idea:** Your Gitea instance is SSOT. Each game is a small repo (or one monorepo with per-game dirs). Saves live as LFS objects or release assets. Agent does pull/checkout → WIP, and commit+push / release upload on end. ```mermaid flowchart LR Agent[syncgames_agent] WIP[Local_WIP] Gitea[Gitea_repo_LFS] Hist[Device_history_local] Agent -->|"git pull / download live tag"| WIP WIP -->|"play"| Game[Game] Game --> WIP WIP -->|"snapshot"| Hist Agent -->|"commit live + push"| Gitea ``` **Hardening vs naïve git:** - Treat `live/` as a single tracked blob set; never merge binary conflicts — lease + parent-hash check abort the push - Store history as `history///` commits **or** local-only tarballs with only `live/` pushed (cleaner; history survives offline) - Prefer **Git LFS** for binary saves; optional signed tags `live/` as the authoritative pointer - Manual “Sync Now” buttons remain available; automation calls the same API **Android:** Kotlin/Compose or Flutter thin client using Gitea HTTP API + LFS (avoid full git on phone if painful). | Pros | Cons | |------|------| | Explicit, auditable history in Gitea UI | Git/LFS friction for large/frequently-changing binaries | | Works over WAN without Syncthing mesh | Needs always-reachable Gitea | | Matches “button press = state change” mental model | Automated push still needs the same lease logic you fear forgetting | **Best if:** You already run Gitea and want human-readable restore via web UI more than raw speed. --- ## Path 3 — Custom Agent + Object Store SSOT (MinIO / S3 / rclone remote) **Idea:** Skip git entirely. SSOT is versioned object storage you control (self-hosted MinIO on your LAN/homelab, or any S3-compatible bucket). Agent implements `pull-live`, `push-live`, `list-history`, `restore` against object keys. ```mermaid flowchart TB subgraph devices [Devices] PC[Linux_PC_agent] Laptop[Laptop_agent] Phone[Android_agent] end subgraph ssot [SSOT_MinIO] LiveKey["games/er/live/*"] HistKeys["games/er/history/device/ts/*"] Meta["games/er/meta.json + lease"] end PC --> ssot Laptop --> ssot Phone --> ssot ``` **Object layout:** ``` games//live/ games//history/// games//meta.json # checksums, parent, lease holder, expires_at ``` **Ops model:** - Push: upload WIP → new history prefix → checksum → atomic update of `live/` + `meta.json` (lease required) - Pull: verify lease available → download `live/` → install into native path → mark WIP parent hash - Restore: copy chosen history prefix → `live/` (explicit user action) - Transport: MinIO SDK, or `rclone` wrapped so you can swap backends later **Automation same as Path 1** (session hooks); storage is just S3 semantics instead of Syncthing/Git. | Pros | Cons | |------|------| | Cleanest model for binary SSOT + versioned keys | Needs MinIO (or cloud S3) reachable from phone | | No git binary-conflict footguns | You own the agent end-to-end | | Easy prune (delete old prefixes) | Slightly more DIY than Syncthing for LAN presence | **Best if:** You want the strongest conceptual match to “one live slot + per-device history” without Syncthing’s merge semantics or Git’s binary awkwardness. --- ## Comparison against your tenets | Tenet | Path 1 Syncthing+Agent | Path 2 Gitea+Agent | Path 3 MinIO+Agent | |------|------------------------|--------------------|--------------------| | Safety / controlled direction | High if SSOT-only synced | High (hash+lease) | Highest (explicit keys+lease) | | Automation | Excellent on Linux; good on Android | Good; WAN-friendly | Excellent; WAN-friendly | | Easy add/remove game | CLI generates folder + Syncthing API | CLI creates repo/paths | CLI creates key prefixes | | LOPE recovery | device history + `.stversions` | git history / local history | object history prefixes | | Android friction | Lowest (Syncthing exists) | Medium (custom app) | Medium (custom app / rclone) | | Your past LOPE risk | Medium — only if someone resyncs live dirs | Low | Low | --- ## Recommended default (if you want a pick) **Path 3 (MinIO + session agent)** as the core SSOT design — it maps 1:1 to live slot + per-device history, avoids Syncthing merge and Git LFS pain, and still supports full automation via process hooks. **Optional hybrid later:** Path 1’s Syncthing can mirror a MinIO bucket backup, or Path 3’s `history/` can be additionally restic-backed for offsite retention. That is additive hardening, not required for v1. --- ## Proposed SyncGames repo skeleton (whichever path) ``` SyncGames/ README.md docs/architecture.md docs/add-game.md config/games/ # declarative TOML per title config/devices.toml agent/ # Python or Go CLI + daemon templates/game.toml systemd/ # user units for watchers ``` Initial titles in config templates: Dark Souls 1/2/3, Elden Ring (Seamless Coop paths), Eden/Yuzu NAND/save dirs as separate platform profiles. --- ## Decision needed before implementation Reply with which path to build first (**1**, **2**, or **3**). Also confirm: 1. Is a always-on homelab box available for MinIO/Gitea/Syncthing hub? 2. Primary phone OS for sync — Android only? 3. Prefer agent language: **Python** (fast to ship) or **Go** (single static binary)?