# Architecture XIV Vault is one engine with two front ends. Everything that touches backups lives in `XivVault.Core`; the CLI and the desktop app only present it. ``` ┌──────────────────────────┐ │ XivVault.Core │ │ discovery · backup │ │ validation · restore │ │ retention · scheduling │ │ diagnostics · status │ └────────────┬─────────────┘ ┌──────────────┴──────────────┐ ┌────────┴────────┐ ┌─────────┴─────────┐ │ XivVault.Cli │ │ XivVault.Desktop │ │ Spectre.Console │ │ Avalonia · MVVM │ └─────────────────┘ └───────────────────┘ ``` Both front ends call `services.AddXivVaultCore()` and resolve the same services from Microsoft.Extensions.DependencyInjection. ## Projects | Project | Role | |---|---| | `src/XivVault.Core` | The engine. No UI code. | | `src/XivVault.Cli` | `xiv-vault.exe`: commands, output formatting, exit codes. | | `src/XivVault.Desktop` | `XIV-Vault.exe` (the `XivVault` assembly, renamed when published): views, view models, theme. Also the headless `--scheduled-backup` entry point, Velopack's install and uninstall hooks, and updates. | | `tests/XivVault.Tests` | Core, CLI and view model tests. All run in temp folders. | | `tools/XivVault.Screenshots` | Renders every desktop screen to PNG with Avalonia's headless Skia renderer, against a fake XIVLauncher folder. With `--demo`, records the README's GIF: a real backup and restore of the fake profile, slowed so the progress shows, encoded with ffmpeg. | ## Core services | Service | Responsibility | |---|---| | `IConfigStore` / `ConfigStore` | `%LOCALAPPDATA%\XIV Vault\config.json`: destination, retention, UI layout option, compression, XIVLauncher override, schedule preferences. | | `IStateStore` / `StateStore` | `state.json`: cached verification results and the last scheduled run. Losing it is harmless. | | `IXivLauncherLocator` / `WindowsXivLauncherLocator` | Finds the XIVLauncher folder: explicit override, then known paths (`%AppData%\XIVLauncher`, `XIVLauncherCN`), then layout detection (`dalamudUserData`), then validation. A folder only counts if it holds XIVLauncher or Dalamud files. | | `PortableStateScanner` | Lists the allowlisted files in a Dalamud data folder. Metadata only. | | `BackupAllowlist`, `ArchivePaths` | The allowlist, and the rules for safe archive paths. | | `BackupService` | Writes `*.zip.tmp`, verifies it, renames it, applies retention. | | `ArchiveValidator` | Decides whether an archive can be trusted: manifest, schema, paths, allowlist, every hash. | | `BackupCatalog` | Lists archives in a folder from their manifests, or by name while they are online only; verifies the latest one, or any one on request, and caches the result; downloads and deletes them. | | `RetentionService` | Keeps the newest N regular backups and the newest 3 pre-restore snapshots, separately. Damaged archives are neither counted nor deleted. Online-only archives are never downloaded: they don't count, and are deleted unopened once N verified backups are newer. | | `RestoreService` | Preview, safety checks and the restore itself, of everything or of a `RestoreSelection`: some plugins, and Dalamud settings as one choice. | | `GameProcessGuard` | Detects XIVLauncher and FFXIV processes. | | `WindowsTaskScheduler`, `ScheduleService` | The `XIV Vault Scheduled Backup` task through `schtasks /xml`, kept in step with the saved preferences. | | `ScheduledBackupRunner` | What the task runs: waits for FFXIV, backs up, records the result. | | `StatusService` | The protection summary the Overview and `xiv-vault status` show. | | `DiagnosticsService` | The four health-check groups and the shareable report. | `OperationLock` is a named mutex per data folder. Backups, restores and retention take it, so the scheduled task and the app never work on the backup folder at the same time. Platform access goes through small interfaces so tests can replace it: `IAppEnvironment` (folders and user), `IProcessInspector`, `ICommandRunner` (schtasks), `IFileAvailability` (whether a file is online only), `INotifier` (Windows notifications) and `TimeProvider`. `XIV_VAULT_DATA_DIR` redirects config, state and logs, which the smoke test uses. ## Backup flow 1. Load config, locate XIVLauncher, scan the allowlisted files. 2. Create the destination folder; failure there is "destination unavailable" (exit 7). 3. Stream each file into `xiv-vault-