Session-gated MinIO save sync with AppImage GUI, CLI edit/session flow, and Gitea release helper. Co-authored-by: Cursor <[email protected]>
241 lines
9.3 KiB
Markdown
241 lines
9.3 KiB
Markdown
---
|
||
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/<game-id>/
|
||
live/ # SSOT current slot (one winner)
|
||
history/
|
||
<device-id>/ # 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/<id>/` to `retired/<id>-<date>/`, 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/<device>/<timestamp>/` commits **or** local-only tarballs with only `live/` pushed (cleaner; history survives offline)
|
||
- Prefer **Git LFS** for binary saves; optional signed tags `live/<game-id>` 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/<id>/live/<relative-save-path>
|
||
games/<id>/history/<device>/<iso-ts>/<relative-save-path>
|
||
games/<id>/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)?
|