RomM stores save files and save states on the server, making it possible to pick up a game on any device right where you left off. Whether you upload saves manually through the UI, push them via the REST API, or use a companion app with automatic sync, all assets land in a single, predictable location under yourDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/rommapp/romm/llms.txt
Use this file to discover all available pages before exploring further.
/romm/assets volume.
Storage Location
Save files, save states, and their associated screenshots are stored inside the/romm/assets Docker volume. The directory layout mirrors your ROM library
structure:
Save files are scoped per user per ROM. Each user’s saves are stored in
their own subdirectory, so different users can maintain independent progress for
the same game without any conflict.
Save File Model
Each save is stored in the database with the following fields (frommodels/assets.py):
| Field | Description |
|---|---|
file_name | Original filename of the save (e.g. myrom.srm) |
file_path | Absolute path to the containing directory |
file_size_bytes | Size of the save file in bytes |
content_hash | MD5 hash used for deduplication and conflict detection |
emulator | Emulator name the save was created by (optional) |
slot | Save slot identifier; datetime-tagged for slot-based history |
is_public | Whether other users can view and download this save |
origin_device_id | ID of the device that first uploaded this save |
State) records share the same base fields, minus slot, content_hash, and origin_device_id.
Uploading Saves and States
- Via the UI
- Via the API
Open a game’s detail page, switch to the Saves or States tab, and use
the upload button to attach a file from your local machine. An optional
screenshot can be uploaded at the same time for a visual preview.
Downloading Saves and States
Listing and Querying
Visibility
Saves and states are private by default. You can make a save or state public so that other users in the same RomM instance can browse and download it:Device Sync Protocol
RomM implements a structured pull/push sync protocol designed for companion apps (such as mobile emulator frontends). The sync cycle has two phases:Negotiate
The client sends its current save inventory to the server. The server compares
content hashes and timestamps, then returns an ordered list of operations.The response contains a
session_id and a list of operations, each with
one of four actions:| Action | Meaning |
|---|---|
upload | Client has a save the server lacks — client should push it |
download | Server has a save the client lacks or that is newer — client should pull it |
conflict | Both sides have changes since the last sync — manual resolution needed |
no_op | Both sides are in sync — nothing to do |
Execute operations
The client works through the operation list:
- For
uploadactions, callPOST /api/saveswith the local file. - For
downloadactions, callGET /api/saves/{save_id}/content. - For
conflictactions, decide locally which copy to keep, then upload or skip.
Sync Modes
Devices can operate in one of three sync modes (defined inmodels/device.py):
| Mode | Value | Description |
|---|---|---|
| API | api | Client-driven negotiate/complete cycle (default for most apps) |
| File Transfer | file_transfer | Direct file-system sync over SSH/rsync |
| Push-Pull | push_pull | Server-initiated scheduled sync triggered by RomM |
Tracking and Untracking
A device can mark individual saves as untracked to opt them out of future sync cycles without deleting them:Scheduled Push-Pull Sync
WhenENABLE_SYNC_PUSH_PULL=true, RomM runs background sync jobs on the schedule
defined by SYNC_PUSH_PULL_CRON. You can also trigger a push-pull sync manually
for a specific device: