Sync Behaviour
This page explains the rules Kumo uses to decide what to upload, download, or delete, and how it handles edge cases like conflicts and offline deletions.
Full sync
A full bidirectional sync runs:
- On service startup
- When the network becomes available after being offline
- After reconnecting the WebSocket
For each emulator category the sync proceeds in two passes:
- Pre-scan — all local and remote files are compared and classified before any transfers start. Pending events are inserted into the history immediately so the UI can show what is queued.
- Download pass — remote files that are newer or missing locally are downloaded.
- Upload pass — local files that are newer or missing remotely are uploaded.
File watcher
While the service is running, Kumo watches each save directory for file system events (CLOSE_WRITE, CREATE, MOVED_TO, DELETE, MOVED_FROM). A changed file is uploaded within seconds of the write completing. A deleted file triggers a remote delete.
Real-time push
When another device uploads a save, a background service broadcasts the change to all connected devices. Kumo receives this notification and downloads the new file without waiting for the next full sync.
Upload rules
A file is uploaded when its local modification time is newer than the last recorded sync time in the manifest. Files with disallowed extensions are skipped (see Supported Emulators → Allowed file extensions). ROMs use a separate extension list from saves.
Download rules
A file is downloaded when the remote version is newer than what Kumo last synced. Downloads are never filtered by extension — if the server has it, Kumo downloads it.
For files up to 10 MB, Kumo computes an MD5 hash and compares it against the remote checksum to avoid re-downloading an identical file. For files larger than 10 MB only the modification time is used.
Conflict resolution
A conflict occurs when both the local file and the remote file have been modified since the last sync. Kumo resolves this by:
- Renaming the existing local file to
filename.sync-conflict-YYYYMMDD-HHmmss.ext - Downloading the remote version as the canonical file
Both versions are kept — you can compare them and delete whichever you don't need.
Delete marker logic
Cloud versioning is always enabled. Deleting an object creates a delete marker rather than destroying the data.
When Kumo sees a delete marker for a remote file it applies the following logic:
| Local state | Action |
|---|---|
| File is in manifest and local copy is unchanged since last sync | Delete the local file (remote deletion wins) |
| File is in manifest but local copy has been modified since last sync | Upload the local file (local change wins, ignore the delete marker) |
| File is not in manifest | Upload the local file (treat delete marker as a stale remnant) |
When Kumo detects a file that has been deleted locally:
- It checks whether the file is in the sync manifest (meaning it was previously synced successfully).
- If so, and if the remote copy has not been updated since the last sync, the remote object is deleted.
- If the remote copy has been updated in the meantime, the remote version is downloaded instead (remote wins).
Sync manifest
Every successfully synced file is recorded in a local database table (synced_files) with its storage key and the time it was synced. This manifest is the source of truth for determining whether a missing local file was intentionally deleted or simply never downloaded.
Deleting files while sync is stopped
The file watcher only runs while the sync service is active. If you delete a local save file while sync is stopped, the deletion is not recorded. When sync starts again, Kumo sees the file is absent locally but still present in both the manifest and in the cloud. If the remote copy has not been updated since the last sync, Kumo will correctly delete the remote copy. However, if another device has uploaded a newer version in the meantime, that remote version takes precedence and the file will be re-downloaded, overwriting your deletion.
If you want to permanently remove a file from all devices, make sure sync is running when you delete it, or delete it from the emulator's settings page inside Kumo (which removes both the local and remote copy regardless of sync state).
Storage quota
Kumo enforces a per-account storage limit based on your subscription plan. The current usage is fetched from the cloud (sum of all stored file sizes) and cached for 5 minutes. If an upload would exceed the limit it is skipped and a quota warning is shown in the app. Usage is re-fetched after every upload or delete.
Transfer quota
In addition to storage, each plan has a monthly transfer quota covering the total bytes uploaded and downloaded. Counters are tracked server-side and reset automatically after 30 days. The counters are cached locally for 5 minutes and flushed with an 8-second batch delay. Exceeding the quota skips further transfers without aborting the sync session.
Sync history
Every transfer — upload, download, or delete — is recorded in a synced_file_events table. The Recent Changes screen shows the last 2 hours of activity per emulator and the home screen shows a global feed. The list updates live while sync is running, showing pending and in-progress items before they complete.
Recent Changes progress indicator
The in-progress percentage in Recent Changes is currently emitted only for SAF transfer paths. For non-SAF transfers, the in-progress row may display ... until the transfer completes.