Skip to content

Repository files navigation

OpenSave

OpenSave

Steam Cloud for every game you own.

OpenSave syncs your game saves between devices, peer-to-peer — no Steam required, no accounts, no subscriptions. Point it at a folder, pair your devices, and your saves follow you everywhere.

Release License: MIT Discord Go Platforms

A complete Go rewrite of the original Node.js/Electron app: one small native binary, no runtime to install, and wire-compatible with existing peers.

Install · Quick start · Screenshots · How it works · CLI · Self-host the relay · FAQ · Discord


OpenSave library — tracked games with cover art, branches, and snapshot counts

Why OpenSave

Steam Cloud only covers games bought on Steam — and only when the developer opts in. Everything else (emulators, GOG, Epic, single-player games with no cloud support) is on you: manually copying save folders between your desktop, laptop, and Steam Deck, and hoping you grabbed the newest one.

OpenSave gives every game the Steam Cloud experience:

  • You own it. Saves sync directly between your devices. No account to create, nothing stored on someone else's server.
  • It's automatic. Auto-detects hundreds of games, watches for changes, and syncs the moment a save is written.
  • It's safe. Every change is snapshotted and reversible — a full version history you can roll back, not just the latest copy. Conflicts are detected and resolved without silently clobbering a playthrough.
  • Your devices don't have to be on at the same time. Connect your own Google Drive, Dropbox, OneDrive, WebDAV server or NAS folder, and a save made on one device reaches the next one you open, even with the first switched off. Still no account with us.

Features

  • Auto-detection — scans for saves from Steam, emulators (RetroArch, Dolphin, Ryujinx, Yuzu, Citra, PCSX2, RPCS3, PPSSPP, Cemu, Xenia), Steam-emulator repacks (Goldberg/GSE, CODEX, RUNE, Tenoke, EMPRESS, Online-Fix, CPY, SKIDROW, 3DM, …), Epic, GOG, Unity LocalLow, and Unreal Engine conventions — plus the community-maintained Ludusavi manifest covering save paths for tens of thousands of games, whatever store (or no store) they came from. Heroic, Lutris and Bottles prefixes are found on any drive, SD card included, and a newly installed game is noticed in the background — pointed out, never tracked without asking.
  • Track anything — any folder or single save file, watched live with block-level change detection (SHA-256, 64 KB–2 MB adaptive blocks). Only the blocks that changed are ever transferred.
  • One tile per game, not one per folder — a scan finds the same game in several places as a matter of course, and every result shows its file count, size and last-written date so you can tell the save you play from the one an old install left behind. Folders holding no files are hidden. Where a game's save is genuinely split across sibling folders, they are offered together as one game.
  • A save split across folders is one game — some titles keep progress in one place and settings or mods in another. Add each folder as a save location and all of them sync, snapshot and restore together.
  • Files that shouldn't sync — device-specific settings living beside the save can be excluded per game, written like a .gitignore, or picked from a list of what is actually in the folder. Excluded files are still captured in every snapshot, so a rule can never be the thing that loses one.
  • P2P sync — automatic over LAN (zero-config discovery) or across the internet through a relay room code — no port forwarding. A paired-device model means every connection is explicitly approved.
  • Snapshot history — every change creates a versioned snapshot, compressed where that helps. Roll back a whole save or a single file; branches keep parallel playthroughs (and conflict resolutions) safe.
  • Smart conflict handling — diverged saves are detected by sync lineage, not wall-clock timestamps. Keep yours, keep theirs, or keep both on a new branch.
  • Cloud backup and hand-off — optional mirroring to Google Drive, Dropbox, OneDrive, WebDAV, a webhook, or a local/NAS folder. Each device also reads the others' backups: a newer save that carries on from yours is brought over, and one that would replace progress made here is asked about first. Any OAuth provider can use your own app credentials instead of the built-in ones — required for OneDrive, and the fix for Google Drive's weekly re-login.
  • Cross-device game matching — the same title tracked under different names on two machines (a Steam install here, a differently-named folder there) can be matched by Steam App ID or linked by hand. App-ID matching is opt-in, so two separate copies of a game are never merged without asking.
  • A full command line — opensave does everything the app does, for a Steam Deck in Game Mode or a headless server. See Command line.
  • In-app updates — one-click update from GitHub releases, pull a newer build straight from a paired device, or opensave update from the terminal.
  • Privacy-first — no accounts, no telemetry. As of v2.4.0, saves sent through the relay are encrypted end to end between your own two devices: the relay routes frames it cannot read and writes nothing to disk. You can self-host it so nobody else is on the path at all.

Screenshots

Scan results — detected saves as a cover-art grid
Scan — finds saves from Steam, emulators, GOG, Epic and repacks, shown as cover art, one click to track.
Cloud Backup — provider selection with Google Drive connected
Cloud backup — mirror snapshots to Drive, Dropbox, OneDrive, WebDAV, or a NAS folder. Optional; P2P needs no cloud.
Devices — a Steam Deck paired and online
Devices — pair once on your network, or anywhere with a room code. No port forwarding, and the relay never stores saves.
Activity — where each game was last played, saved and synced
Activity — where each game was last played, when it was last saved and synced, and how long you have played it.

Install

Platform Download Run
Windows OpenSave.Setup.exe (installer) or portable OpenSave.exe Double-click
Linux opensave-linux-amd64.tar.gz extract, then ./opensave
Steam Deck / SteamOS OpenSave.flatpak see Steam Deck install
macOS (2.4.0 onward) OpenSave-macos.dmg, or opensave-macos-arm64.tar.gz / -amd64 for the CLI open the .dmg and drag OpenSave to Applications — it isn't signed yet, so macOS asks you to allow it under Privacy & Security

Grab the latest from the Releases page.

macOS says OpenSave "is damaged and can't be opened"? That is 2.4.0-beta.2, whose Mac app really was broken: its Info.plist was not valid, and nothing in it was signed. Nothing you can do opens it — download a later build, which macOS asks about instead; allow it under Privacy & Security.

Steam Deck install

Use OpenSave.flatpak, not opensave-linux-amd64.tar.gz. The tarball's desktop app will not start on a stock Deck: SteamOS ships no WebKitGTK, which it needs to draw its window. This trips people up because the tarball's name reads like the Steam Deck build.

The Flatpak is the build that works on a stock Deck — SteamOS ships no WebKitGTK and wipes manually-installed system packages on OS updates; the Flatpak bundles everything and survives updates.

  1. Switch to Desktop Mode (Steam button → Power → Switch to Desktop).
  2. Download OpenSave.flatpak from the Releases page.
  3. Double-click it to install via Discover, or run flatpak install --user OpenSave.flatpak in Konsole.
  4. Launch OpenSave from the application menu. Optional: add it to Steam (right-click → Add to Steam) to open it from Game Mode.

If you only want background syncing and no window, the command line has no such constraint — it needs no WebKitGTK and runs anywhere:

curl -fsSL https://opensave.org/install.sh | sh
opensave scan && opensave service install
sudo loginctl enable-linger $USER

Saves on the SD card are found automatically (/run/media is visible to the app), and Proton game saves are detected inside their compatdata prefixes. The plain Linux tarball also works on the Deck if you install webkit2gtk-4.1 yourself, but SteamOS updates can remove it — the Flatpak is the supported path. A Decky plugin for Game Mode lives in opensave-decky-plugin/.

Other handhelds / Arch-based distros (CachyOS, Bazzite-likes): if your distro is not immutable (CachyOS isn't), the plain Linux tarball with your distro's webkit2gtk-4.1 package is the best install — it survives updates and uses your native graphics stack. The Flatpak is for immutable systems like stock SteamOS.

Troubleshooting:

  • "runtime org.gnome.Platform … not found" during install — your flatpak user installation doesn't have Flathub configured yet: flatpak remote-add --if-not-exists --user flathub https://dl.flathub.org/repo/flathub.flatpakrepo then install the bundle again.
  • The app must be launched in Desktop Mode (or added to Steam to run from Game Mode) — running it from a bare terminal session shows no window.

Upgrading from the original (JS) OpenSave? Your data migrates automatically on first launch — tracked games, snapshots, pairings, and cloud settings are imported from ~/.opensave/opensave-db.json (kept as a backup, never deleted). Go and JS devices can pair and sync with each other during the transition.

Quick start

  1. Launch OpenSave on your first device. It scans for installed games and shows one cover-art tile per game, with what each folder holds and when it was last written.
  2. Track a game — click a tile, or add any folder / save file manually. A game found in several places says found in N folders; open that to see which is which.
  3. Pair a second device. On the same network, the other device appears automatically under Devices — approve the request. Remote? One device creates a room code under Internet Sync; the other joins with it.
  4. Play. When a save changes, OpenSave snapshots it and syncs it to every paired device. There's nothing else to do.

Need to undo something? Open a game's history and roll back a snapshot — the whole save or a single file.

New to this? The Getting Started guide walks through the whole thing from a fresh install, in plain language, with what to do when something looks wrong.

How it works

   Device A                        Device B
 ┌──────────┐   LAN (auto-discovery)   ┌──────────┐
 │ watcher  │◀───────────────────────▶│ watcher  │
 │ snapshot │                          │ snapshot │
 │  delta   │   WAN via relay room     │  delta   │
 └────┬─────┘   (TLS to the relay)     └────┬─────┘
      │            ┌───────────┐            │
      └───────────▶│   relay   │◀───────────┘
                   │ (routes,  │
                   │  no data) │
                   └───────────┘
  1. Watch — a filesystem watcher notices a save was written (safe-write and file-lock aware, so it never grabs a half-flushed file).
  2. Delta — the save is chunked into content-defined blocks and SHA-256 hashed. A manifest diff finds exactly which blocks changed.
  3. Snapshot — the new state is recorded as an immutable, versioned snapshot on a branch.
  4. Sync — only the changed blocks travel to paired peers, over LAN when possible or through a stateless relay room otherwise. Lineage metadata lets the receiver detect a genuine conflict versus a fast-forward.

Command line

opensave is a complete client, not a companion to the app: auto-detect saves, pair devices, sync, resolve conflicts, manage snapshots and branches, back up to the cloud, and run as a background service. A headless box — a NAS, a home server, or a Steam Deck that lives in Game Mode — never needs the desktop app.

No account, no token, no server to sign up to.

The OpenSave CLI status panel: the OpenSave wordmark in white and purple, then the version, whether the daemon is running, the device name, tracked games, paired devices, relay and cloud backup status, followed by suggested next commands.

Run opensave on its own and it tells you what is happening right now, and what to do next.

→ Full CLI guide — the daemon model, which machine each command belongs on, and worked sequences for pairing, internet sync, running headless, snapshots, split saves and scripting. The tables below are the summary; that is the walkthrough. There is also one on the website.

Install

Linux & Steam Deck

curl -fsSL https://opensave.org/install.sh | sh

Windows (PowerShell)

irm https://opensave.org/install.ps1 | iex

Installs to your user folder — no root, no admin — puts it on your PATH, and shows the status panel when it is done. Downloads are verified against the SHA256SUMS published with each release; piping a script into a shell is enough trust on its own.

Three names, one program: opensave, os as a short alias, and opensave-cli (the name the Steam Deck plugin and the Linux packages use internally). If something on your system already answers to os, the installer leaves it alone and says so.

To choose where it lands or pin a version:

OPENSAVE_INSTALL_DIR=/usr/local/bin OPENSAVE_VERSION=v2.2.0 sh install.sh

To remove it, the same script reverses exactly what it and opensave service install put down — binaries, aliases, the service, the autostart entry, the PATH line — and leaves your settings and backups where they are:

curl -fsSL https://opensave.org/install.sh | sh -s -- --uninstall

Add --purge to delete ~/.opensave as well; it says what it is about to delete, counts your backup files, and asks you to type a confirmation first.

On Windows, opensave install --uninstall does the same for what install.ps1 put down — the binary, the os and opensave-cli shims, and the PATH entry — and never touches your data. The desktop app's own uninstaller offers to run it for you.

Or build it: go build -o opensave ./cmd/opensave-cli

Keeping it current

opensave update            # replace this binary with the latest release
opensave update --check    # just report whether a newer one exists

Pre-releases are never offered automatically — install those yourself from the releases page.

Getting started

opensave scan                          # what is on this machine
opensave add "Elden Ring" ~/.local/share/EldenRing
opensave daemon start &                # the sync service
opensave pair 192.168.1.42             # pair another device on the LAN
opensave sync --all

Different networks instead of a LAN? Run opensave relay join <code> with the same made-up code on both devices — no port forwarding. As of v2.4.0 the relay passes data it cannot read: saves are encrypted end to end between your two devices, and nothing is stored on the way.

Run it permanently

opensave service install
systemctl --user enable --now opensave-daemon
sudo loginctl enable-linger $USER     # Steam Deck: survive Game Mode switches

That last line matters on a Deck. Without it SteamOS stops your background services the moment you switch to Game Mode — which is exactly when you want syncing to be happening.

Command reference

Every command accepts --json for scripting.

Games

Command What it does
scan [--all] Auto-detect saves: Steam libraries, Proton and Wine prefixes, emulators, 20k+ titles via the Ludusavi manifest. Grouped one game at a time, with each folder's size and last-written date. Empty folders are hidden; --all shows them
add <name> <path> Track a save folder or file
add <number> Track the nth result from the last scan
remove <gameId> Stop tracking. Save files and snapshots stay on disk
untrack-all --yes Stop tracking everything (snapshots are kept)
game <gameId> set <key> <value> Per-game settings: path, app-id, exe-path, cover-url, auto-sync, max-snapshots
locations <gameId> [add|remove] A game's extra save folders, for a save split across more than one place
ignore <gameId> [add|remove|clear|test] Files that shouldn't sync, written like a .gitignore. test answers "would this sync?"
launch <gameId> Start the game
status Tracked games, branches, peers

Sync & devices

Command What it does
sync [<gameId>|--all] Sync now; everything by default
peers Paired devices, devices found on this network, pending requests
pair <host[:port]> Ask a device on the LAN to pair
pair requests Show incoming requests, and which device sent them
pair approve|reject <peerId> Answer one
unpair <peerId> Drop a paired device
probe <host[:port]> Check whether a device answers — works before pairing
forget <peerId> Remove a stale device record
relay join <code> Sync across networks — same code on each device
relay status|leave Show or leave the relay room
conflicts Saves that diverged and are waiting on a decision
resolve <gameId> <choice> keep-both (safest), keep-local, keep-remote

History

Command What it does
snapshot <gameId> [comment] Snapshot the current save
snapshots <gameId> List snapshots, newest first
rollback <gameId> <snapId> Restore a snapshot
branch <gameId> <name> Create a branch for a parallel playthrough
checkout <gameId> <name> Switch branch
branch-delete <gameId> <name> Delete a branch and its snapshots
snapshot-delete <gameId> <snapId> Delete one snapshot
prune [--apply-default] Apply snapshot retention limits now
files <gameId> <snapId> [path] List a snapshot's contents, or restore a single file from it
export <gameId> <dir> Copy the save out exactly as the game wrote it — no archive, no wrapper
backup export|import <file.sscb> Portable backup archive

Cloud backup

Command What it does
cloud status Provider and connection state
cloud browse Everything stored in the cloud
cloud list <gameId> Cloud snapshots for one game
cloud push <gameId> Upload local snapshots
cloud restore <gameId> <file> Pull one back
cloud delete <gameId> --yes Remove a game's cloud copies

Google Drive, Dropbox and OneDrive need a browser to grant consent, so those are connected once in the desktop app. WebDAV, webhook and local/NAS providers work entirely from the terminal.

Configuration

Command What it does
config [list] Show settings
config set <key> <value> device-name, match-by-app-id, snapshot-limit, relay-url
scanpath list|add|remove <path> Extra folders for auto-scan to check
exclude list|add|remove <path> Folders auto-scan should skip
link <gameId> <otherId> Treat two tracked games as the same game
unlink <aliasId> / links <gameId> Undo a link / show linked ids

Service

Command What it does
daemon start [--port N] Run the daemon in the foreground
daemon status / daemon stop Check on, or stop, a daemon started by the CLI
service install|uninstall|status Manage the systemd --user unit (Linux)
completion bash|zsh|fish Shell completion script
upnp <port> [--delete] Forward a router port via UPnP
update [--check] Update this binary from the latest release
version Print the version

Scripting

opensave daemon status --json | jq .gameCount
opensave snapshots elden-ring --json | jq -r '.[0].id'
opensave conflicts --json | jq 'keys'

# Scan results carry what is in each folder and which rows are one game.
# Everything found in more than one place:
opensave scan --json | jq -r 'group_by(.groupId)[] | select(length > 1) | .[0].name'
# Track the freshest folder of every game found, by its listed number:
opensave scan --json | jq -r 'to_entries[] | select(.value.role=="primary") | .key + 1'

scan --json lists results in the same order the printed listing numbers them, so index n is what add n tracks. Each row carries fileCount, totalBytes, latestMtime and measured, plus groupId and role (primary, location, alternative, inside, only) saying which rows are one game and what each folder is. measured: false means the folder could not be read — not that it is empty.

Failures exit non-zero and, with --json, print {"error": "..."}.

Full details: man opensave (shipped in the Linux tarball), or packaging/man/opensave.1.

The daemon exposes a local REST + WebSocket API (P2P on port 8383) that the desktop UI, the CLI and the Steam Deck plugin all drive, so anything the app can do is scriptable.

Build from source

# Desktop app (needs Go 1.26+, Node 18+, and the Wails CLI)
go install github.com/wailsapp/wails/v2/cmd/wails@latest
cd cmd/opensave-app && wails build

# Headless daemon + CLI
go build ./cmd/opensave-cli

# Relay server (self-host)
go build ./cmd/opensave-relay

Run the test suite:

go test ./... -timeout 2700s     # everything
go test ./e2e/... -timeout 2700s # end-to-end pairing & sync tests only

The timeout is not optional. e2e drives real daemons over real HTTP with real file watching, and a large part of its ten minutes is spent deliberately waiting for background work to settle. That is comfortably past Go's default of ten minutes per package, and overrunning it prints a goroutine dump that reads like a crash rather than a clock running out. CI uses the same 2700s.

Self-hosting the relay

The relay is stateless — it brokers room codes and proxies OAuth, and writes no save to disk. As of v2.4.0, what it forwards is encrypted end to end: saves are sealed with a key only your two devices hold, so a relay operator cannot read what passes through. What a relay can still see is that two devices are talking, roughly how much data moves, and which games by id. Ours is wss://relay.opensave.org; run your own to keep even that to yourself:

./opensave-relay                     # listens on :8386
PORT=10000 ./opensave-relay          # custom port
docker build -f relay/Dockerfile .   # or as a container

Point Internet Sync → Relay server (self-hostable) at your instance, on each device, then join the same room code on all of them. opensave upnp 8386 forwards the port on UPnP-capable routers.

The relay itself never joins a room — it has no such command, and nothing to configure beyond the port. Rooms come into existence when your devices ask for them. Full walkthrough, including TLS and the reverse-proxy settings WebSockets need: docs/RELAY.md.

Architecture

cmd/opensave-app       Wails desktop app (daemon embedded + Svelte UI)
cmd/opensave-cli       Headless daemon & CLI
cmd/opensave-relay     Stateless WAN relay (room broker + OAuth proxy)
internal/
  store                SQLite persistence + legacy JSON import
  delta                Block hashing, manifest diff, patching
  snapshot             ZIP snapshots, branches, retention
  watcher              Save-change detection (safe-write aware, lock guard)
  p2p                  Discovery, pairing, sync engine, LAN/WAN transports
  cloud                Backup providers + PKCE OAuth
  presets              Game / emulator / store save-location detection
  api                  Local REST + WebSocket dashboard API
  daemon               Long-running service orchestration
  sysintegration       Tray, notifications, autostart
opensave-decky-plugin  Steam Deck Game Mode plugin (Decky Loader)

The daemon speaks the same REST/WebSocket API and P2P wire protocol as the original JS app, so old and new versions interoperate during a rollout.

Data & privacy

Everything lives under ~/.opensave/:

Path What
opensave.db SQLite store — tracked games, snapshots, pairings, settings
snapshots/ Versioned save snapshots
opensave.log Activity log for diagnostics
opensave-db.json Legacy JS database (kept as an import backup)

No accounts, no telemetry, no analytics. See PRIVACY.md for the full statement.

FAQ

Do I need a server or an account? No. Devices sync directly. The optional relay only matters for syncing across the internet, and you can self-host it.

Is my data encrypted in transit? Over the relay, yes — end to end as of v2.4.0: save data is sealed with a key derived from the two devices' own keys when they paired, so neither the relay nor anyone else in your room can read it. Both devices need v2.4.0 or later, and an internet pairing made on an earlier version has no key to use — unpair and pair those two again to protect them. The app shows each device's state under Devices.

LAN sync is direct, involves no relay, and is not encrypted: anything on the same network can read a save as it transfers. Treat a network you do not control as one that can see your saves.

What if two devices change the same save while offline? OpenSave detects the divergence by sync lineage and asks you to keep yours, theirs, or both (on a new branch). It never silently overwrites.

Does it work with non-Steam or emulated games? Yes. If it writes a save to disk, OpenSave can track it — Steam, emulators, GOG, Epic, and repacks are auto-detected; anything else you can add by path.

Can old (JS) and new (Go) versions talk to each other? Yes, during the transition. They share the same wire protocol and your data migrates automatically.

Contributing

Issues and pull requests are welcome. Please run go test ./... -timeout 2700s before opening a PR (see Build from source for why the timeout is needed), and keep changes focused. For larger features, open an issue first so we can align on approach.

Documentation

  • Getting Started — start here if OpenSave is new to you. The whole thing from a fresh install, in plain language
  • User Guide — the reference: every feature, what each setting does, troubleshooting
  • Command line — the CLI end to end: the daemon model, which machine each command runs on, task-by-task sequences, scripting
  • Running your own relay — self-hosting, TLS, and the reverse-proxy settings WebSockets need
  • Changelog — release notes
  • Privacy — what OpenSave does and doesn't do with your data

License

MIT — retains the original author's copyright and credits the Go rewrite.

Releases

Packages

Contributors

Languages