RepoPilotOpen in app →

mtkennerly/ludusavi

Backup tool for PC game saves

Healthy

Healthy across all four use cases

weakest axis
Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 3w ago
  • 4 active contributors
  • MIT licensed
Show all 7 evidence items →
  • CI configured
  • Tests present
  • Small team — 4 contributors active in recent commits
  • Single-maintainer risk — top contributor 95% of recent commits

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/mtkennerly/ludusavi)](https://repopilot.app/r/mtkennerly/ludusavi)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/mtkennerly/ludusavi on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: mtkennerly/ludusavi

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/mtkennerly/ludusavi shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across all four use cases

  • Last commit 3w ago
  • 4 active contributors
  • MIT licensed
  • CI configured
  • Tests present
  • ⚠ Small team — 4 contributors active in recent commits
  • ⚠ Single-maintainer risk — top contributor 95% of recent commits

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live mtkennerly/ludusavi repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/mtkennerly/ludusavi.

What it runs against: a local clone of mtkennerly/ludusavi — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in mtkennerly/ludusavi | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 50 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>mtkennerly/ludusavi</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of mtkennerly/ludusavi. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/mtkennerly/ludusavi.git
#   cd ludusavi
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of mtkennerly/ludusavi and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "mtkennerly/ludusavi(\\.git)?\\b" \\
  && ok "origin remote is mtkennerly/ludusavi" \\
  || miss "origin remote is not mtkennerly/ludusavi (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "src/api.rs" \\
  && ok "src/api.rs" \\
  || miss "missing critical file: src/api.rs"
test -f "src/cli.rs" \\
  && ok "src/cli.rs" \\
  || miss "missing critical file: src/cli.rs"
test -f "src/gui.rs" \\
  && ok "src/gui.rs" \\
  || miss "missing critical file: src/gui.rs"
test -f "src/cloud.rs" \\
  && ok "src/cloud.rs" \\
  || miss "missing critical file: src/cloud.rs"
test -f "Cargo.toml" \\
  && ok "Cargo.toml" \\
  || miss "missing critical file: Cargo.toml"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 50 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~20d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/mtkennerly/ludusavi"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

Ludusavi is a cross-platform game save backup tool written in Rust that automatically discovers and backs up save data for 19,000+ games across Steam, GOG, Epic, Heroic, and Lutris. It supports both GUI (via Iced) and CLI interfaces, handles Windows registry saves, Proton saves on Steam Deck, and provides restoration, validation, and cloud backup capabilities using the Ludusavi Manifest data source. Monolithic Rust binary (src/ structure implied by single [[bin]] in Cargo.toml) with feature-gated app module combining GUI and CLI. Build system uses build.rs for platform-specific manifest compilation. Fluent localization files (295KB) stored separately for translation management via Crowdin. Platform-specific assets in assets/{flatpak,linux,windows} for desktop integration and packaging.

👥Who it's for

PC gamers who want automated, reliable backups of their save data across multiple game stores without manual per-game configuration; also system administrators and modding communities who benefit from batch backup/restore scripting and preservation of game state across device migrations.

🌱Maturity & risk

Production-ready and actively maintained. Version 0.31.0 with established release pipeline (GitHub Workflows in .github/workflows/main.yaml), comprehensive CLI documentation (docs/cli.md), multi-platform support (Windows/Linux/Mac), and Flatpak/Winget distribution. Single maintainer (mtkennerly) with regular updates and active Crowdin translation integration suggests sustained development.

Low-to-moderate risk: primary dependency on Ludusavi Manifest (external data source at github.com/mtkennerly/ludusavi-manifest) means data accuracy relies on PCGamingWiki crowdsourcing; single maintainer creates bus factor risk despite stable feature set; large transitive dependency tree via iced (GUI framework) and reqwest (HTTP) increases supply-chain attack surface, though Cargo.lock is committed for reproducibility.

Active areas of work

Version 0.31.0 active development with ongoing localization (crowdin.yml present), platform-specific improvements (wayland and x11 features in iced), and documentation expansion (docs/help/ contains recent feature documentation: backup-automation.md, backup-retention.md, game-launch-wrapping.md suggest recent feature additions).

🚀Get running

git clone https://github.com/mtkennerly/ludusavi.git && cd ludusavi && cargo build --release. GUI launches via cargo run --features app (default), CLI via cargo run --bin ludusavi -- --help. Requires Rust 2024 edition (see Cargo.toml).

Daily commands: GUI: cargo run --release (default 'app' feature). CLI: cargo run --bin ludusavi -- backup --preview. Dev mode: cargo run (builds with debug symbols). Tests: cargo test. Full build with all platforms: see .github/workflows/main.yaml for matrix strategy.

🗺️Map of the codebase

  • src/api.rs — Core API module that orchestrates backup/restore operations and serves as the bridge between CLI and GUI interfaces.
  • src/cli.rs — Command-line interface entry point; defines how users interact with ludusavi from the terminal.
  • src/gui.rs — Graphical interface entry point using iced; handles cross-platform UI rendering and user interactions.
  • src/cloud.rs — Cloud backup synchronization logic; critical for multi-device backup workflows.
  • Cargo.toml — Project manifest defining all dependencies, features (app/CLI), and build configuration.
  • build.rs — Build script that generates assets, icons, and platform-specific manifest files at compile time.

🧩Components & responsibilities

  • api. — undefined

🛠️How to make changes

Add a new game store integration

  1. Define game library discovery logic in api.rs (scan for game locations, parse store manifests) (src/api.rs)
  2. Add CLI command variant in cli.rs or cli/parse.rs for the new store type (src/cli/parse.rs)
  3. Extend the manifest schema in docs/schema/config.yaml if new save path patterns are needed (docs/schema/config.yaml)

Add a new GUI feature or tab

  1. Create a new state struct and event enum in src/gui/app.rs or a dedicated module (e.g., src/gui/settings.rs) (src/gui/app.rs)
  2. Implement the view() method to return iced Message types and update() to handle state transitions (src/gui/app.rs)
  3. Add UI widgets (buttons, text fields) using patterns from src/gui/button.rs, src/gui/common.rs (src/gui/common.rs)
  4. Add English translation keys to lang/en-US.ftl and other language files (lang/en-US.ftl)

Add a new CLI command

  1. Define a new command variant in the clap-derived Cli struct in src/cli/parse.rs (src/cli/parse.rs)
  2. Implement the handler function in src/cli.rs that calls api.rs methods and formats output (src/cli.rs)
  3. Add JSON schema for input/output in docs/schema/api-input.yaml and api-output.yaml (docs/schema/api-input.yaml)
  4. Document the command in docs/cli.md and update lang/en-US.ftl help strings (docs/cli.md)

Support a new backup destination (cloud or local)

  1. Extend src/cloud.rs with new storage backend trait implementation (e.g., S3, Azure, Dropbox) (src/cloud.rs)
  2. Update api.rs backup/restore orchestration to route to the new backend (src/api.rs)
  3. Add configuration options in docs/schema/config.yaml for credentials and endpoint settings (docs/schema/config.yaml)

🔧Why these technologies

  • Rust — Cross-platform compilation, memory safety without GC, single-binary deployment, and strong backwards compatibility for long-term backup reliability.
  • iced (GUI framework) — Elm-inspired functional reactive model ensures predictable UI state; immediate-mode rendering; elm-derive macros reduce boilerplate.
  • clap (CLI parsing) — Declarative macro-based CLI definition with automatic help generation, tab completion, and validation.
  • Fluent (.ftl files) — Industry-standard format for complex pluralization and localization; tooling support for 25+ languages without code changes.
  • serde (serialization) — Derives handle JSON/YAML serialization for config files and API contracts; zero-copy in many cases.

⚖️Trade-offs already made

  • Single Rust binary vs. modular services

    • Why: Simplifies deployment (no service dependencies) and reduces operational complexity for end-users.
    • Consequence: All functionality runs in one process; scaling horizontally requires separate instances, not ideal for cloud deployment.
  • Manifest-driven game data (ludusavi-manifest repo) vs. built-in database

    • Why: Decouples data updates from software releases; PCGamingWiki contributions flow automatically.
    • Consequence: Requires network fetch or offline manifest; adds latency on first run; manifest size grows with game count.
  • Dual UI (CLI + GUI) in one binary

    • Why: Single codebase for both interfaces; shared business logic (api.rs) avoids duplication.
    • Consequence: Larger binary size; feature flags required to compile CLI-only or GUI-only variants.
  • File-based backup format vs. compressed archive

    • Why: Preserves directory structure and metadata; simpler incremental/dedup logic; human-readable for recovery.
    • Consequence: Higher disk usage for redundant data; slower transfers over slow networks; requires custom cloud sync logic.

🚫Non-goals (don't propose these)

  • Real-time cloud synchronization (not a Dropbox-like sync daemon).
  • Centralized server for multi-user backups (designed for single-user desktop tool).
  • Automatic game patching or version management.
  • Cross-OS cloud transfers without manifest revalidation.
  • Support for games with DRM that forbids save backups.

🪤Traps & gotchas

Ludusavi Manifest fetched at runtime from external repo—if manifest.github.com or the manifest repository is unavailable, game discovery fails silently. Registry operations (Windows) require regashii which only works on Windows; cross-platform testing needed. Proton save detection relies on Steam's directory structure (/compatdata/) which can vary by Proton version. Fluent translation files (.ftl) are parsed at compile-time; syntax errors break build even if untranslated languages. Feature gating means cargo test without --all-features skips GUI tests.

🏗️Architecture

💡Concepts to learn

  • Fluent (Mozilla i18n framework) — Ludusavi uses Fluent for UI localization (295KB of .ftl files managed via Crowdin); understanding its syntax and plural rules is essential for adding multilingual features
  • Windows Registry virtualization (regashii crate) — Game saves stored in HKEY_CURRENT_USER are discovered and backed up via regashii; developers must understand registry hives, value types, and how Proton virtualizes them under Wine
  • Proton compatibility layer save paths — Steam Deck games store saves under /compatdata/; Ludusavi must detect Proton prefixes and translate Windows paths to Linux equivalents, critical for portable game preservation
  • Feature gating in Rust (cargo features) — Ludusavi uses feature flags (default = ["app"], optional iced/clap deps) to ship both GUI and lightweight CLI; understanding conditional compilation prevents over-depending on GUI libraries in library-only builds
  • Glob patterns and path matching (globset/globetter) — Backup exclusions (docs/help/backup-exclusions.md) use glob patterns to filter save files; globset implements efficient bulk matching against file trees
  • Parallel backup operations (rayon) — Backing up 100+ games scales poorly serially; rayon provides work-stealing parallelism across CPU cores, visible in indicatif progress bar integration for concurrent operations
  • Iced GUI framework (Elm-inspired) — Ludusavi's GUI uses Iced's message-driven architecture (similar to Elm); understanding Message enums and State updates is essential for GUI contributions
  • mtkennerly/ludusavi-manifest — The game metadata source that Ludusavi fetches at runtime; contributions to save locations for PCGamingWiki-sourced games flow through here
  • mtkennerly/ludusavi-playnite — Playnite integration extension that embeds Ludusavi backup/restore as a game library action, extending reach to Playnite users
  • heroic-games-launcher/HeroicGamesLauncher — Companion game launcher that Ludusavi integrates with; shares library discovery code paths for Epic/GOG/Lutris games
  • ValvePython/steam — Steam library enumeration library; Ludusavi likely uses or reimplements Steam VDF parsing for save path discovery
  • frendiamu/GameSaveManager — Similar game save backup tool (Java-based alternative); useful for feature comparison and test case generation

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add integration tests for backup/restore operations in docs/schema validation

The repo has comprehensive schema files (docs/schema/api-input.yaml, api-output.yaml, config.yaml) but no visible integration tests validating that actual backup/restore operations conform to these schemas. This would catch regressions when the CLI or API changes, and help new contributors understand the expected data flow.

  • [ ] Create tests/integration_tests.rs to test backup/restore roundtrips
  • [ ] Validate API output against docs/schema/api-output.yaml using schemars
  • [ ] Test config file parsing against docs/schema/config.yaml
  • [ ] Add test fixtures with sample game libraries and save directories
  • [ ] Reference in CONTRIBUTING.md for onboarding

Add GitHub Action workflow for schema validation and documentation sync

The repo maintains separate schema files (docs/schema/*.yaml) and Rust serde structs (using schemars for schema generation). Currently .github/workflows/main.yaml exists but likely doesn't validate schema consistency. A new workflow could ensure generated schemas match committed docs and flag drift.

  • [ ] Create .github/workflows/schema-validation.yaml
  • [ ] Add cargo task to generate schemas from schemars in build.rs or separate binary
  • [ ] Compare generated schemas against committed docs/schema/*.yaml files
  • [ ] Fail CI if schemas drift without explicit update commit
  • [ ] Document in CONTRIBUTING.md how to update schemas

Add comprehensive CLI examples and tests in docs/help/command-line.md

docs/help/command-line.md exists but the docs structure shows many help topics (backup-automation.md, backup-exclusions.md, custom-games.md) that likely reference CLI flags. Examples test suite is minimal (examples/api.rs only). Adding documented CLI examples with expected outputs would help users and catch flag breakage.

  • [ ] Create tests/cli_examples.rs with integration tests for common workflows (backup Steam saves, restore to different path, exclude patterns, etc.)
  • [ ] Enhance docs/help/command-line.md with before/after examples and sample output
  • [ ] Add snapshot tests using insta or similar to catch CLI output changes
  • [ ] Cross-reference examples in feature docs (e.g., backup-exclusions.md shows actual --exclude usage)
  • [ ] Document in CONTRIBUTING.md how to run and update CLI examples

🌿Good first issues

  • Add integration tests for backup/restore round-trip (copy random save data, backup, restore, verify hash) in src/tests/; currently no explicit test directory visible in file list despite mature project
  • Document custom games configuration format with worked examples in docs/help/custom-games.md (file exists but likely incomplete based on help directory pattern)
  • Add bash/zsh tab-completion test coverage in .github/workflows/main.yaml CI matrix (README mentions completion support but no validation visible)

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 3218d8c — Upgrade to Rust edition 2024 (mtkennerly)
  • d58972c — Add library crate example (mtkennerly)
  • 8844d7b — Release v0.31.0 (mtkennerly)
  • 13525ec — Update changelog for Rclone update in Flatpak (mtkennerly)
  • 945ed38 — #602: Reset comment field when switching backups (mtkennerly)
  • 454de09 — Fix lint (mtkennerly)
  • d22b7c3 — Update translations (mtkennerly)
  • 6b0c3ed — #502: Support <game> in root paths (mtkennerly)
  • fe2f048 — Narrow allow rule to only affected module (mtkennerly)
  • c6c1f20 — Avoid double module inclusion to make rust-analyzer work better (mtkennerly)

🔒Security observations

The Ludusavi codebase appears reasonably secure from the visible configuration. The main concerns are: (1) incomplete dependency information preventing full audit, (2) a large feature set in the iced dependency that expands attack surface, and (3) use of bundled SQLite which may lag behind security updates. The application handles sensitive data (game save files), so supply chain security is important. No obvious hardcoded secrets, injection risks, or infrastructure misconfigurations are apparent from the file structure. Recommendations include completing the dependency audit with 'cargo audit', establishing a security disclosure policy, and reviewing optional feature necessity.

  • Medium · Incomplete Dependency List in Cargo.toml — Cargo.toml. The Cargo.toml file provided is truncated (serde_ is incomplete), making it impossible to perform a complete dependency audit. This could hide vulnerable or outdated dependencies. Fix: Provide the complete Cargo.toml file. Run 'cargo audit' to check for known vulnerabilities in all dependencies. Consider using 'cargo-deny' for additional supply chain security checks.
  • Medium · Permissive Feature Set in iced Dependency — Cargo.toml - iced dependency. The 'iced' GUI library is compiled with many optional features enabled (wayland, wgpu, x11, advanced, etc.), expanding the attack surface. Some of these features may not be necessary for all deployments. Fix: Review which iced features are actually required for the application. Disable unnecessary features to reduce dependencies and potential vulnerabilities. Consider using feature flags to provide minimal builds.
  • Medium · Use of Bundled SQLite — Cargo.toml - rusqlite dependency. rusqlite is configured with 'bundled' feature, which compiles SQLite from source. While this ensures consistency, it may not receive security updates as quickly as system-packaged versions. Fix: Consider using the system-provided SQLite library instead of bundled. If bundled is necessary, ensure a process is in place to update the bundled version when SQLite security patches are released. Monitor security advisories for SQLite.
  • Low · No Visible Security Policy or SECURITY.md — Repository root. File structure does not show a SECURITY.md file for responsible vulnerability disclosure. This makes it harder for security researchers to report issues responsibly. Fix: Create a SECURITY.md file documenting how to responsibly report security vulnerabilities (e.g., security@example.com or GitHub Security Advisory).
  • Low · Default Features Include Optional Dependencies — Cargo.toml - [features] section. The default feature includes 'app' which bundles many optional dependencies (clap, dialoguer, flexi_logger, iced, etc.). This increases binary size and dependency complexity. Fix: Consider if all default features are necessary. Provide a lean 'core' or 'lib' feature set with minimal dependencies for library consumers. Document which features are required for different use cases.
  • Low · Missing Cargo.lock in Version Control — Repository root / Cargo.lock. While Cargo.lock is referenced in the file listing, ensure it's committed to the repository for reproducible builds. This is important for security auditing. Fix: Verify Cargo.lock is committed. For binary applications, Cargo.lock ensures all users/CI systems build with identical dependencies. Document dependency management in CONTRIBUTING.md.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · mtkennerly/ludusavi — RepoPilot