This document covers the one-shot save sync workflow added to romm-cli for RomM sync endpoints.
romm-cli sync uses sync endpoints introduced in RomM 4.9.0-alpha.2 (pre-release), including:
POST /api/sync/negotiatePOST /api/sync/sessions/{session_id}/completeGET /api/sync/sessionsGET /api/sync/sessions/{session_id}POST /api/sync/devices/{device_id}/push-pull
It also uses device and save endpoints:
POST/GET /api/devicesPOST /api/savesGET /api/saves/{id}/content
Register an API-mode device:
romm-cli sync device register --name "My Handheld" --sync-mode apiInspect devices:
romm-cli sync device list
romm-cli sync device get <device-id>Negotiate operations from a manifest without changing files:
romm-cli sync plan --device-id <device-id> --manifest ./sync-manifest.jsonRun negotiated operations and complete the sync session:
romm-cli sync run --device-id <device-id> --manifest ./sync-manifest.jsonChoose conflict behavior:
romm-cli sync run --device-id <device-id> --manifest ./sync-manifest.json --conflict fail
romm-cli sync run --device-id <device-id> --manifest ./sync-manifest.json --conflict skip--conflict fail is the default.
romm-cli sync sessions list
romm-cli sync sessions list --device-id <device-id>
romm-cli sync sessions get <session-id>
romm-cli sync push-pull <device-id>Manifest file is JSON:
{
"saves": [
{
"rom_id": 123,
"path": "saves/zelda.srm",
"file_name": "zelda.srm",
"slot": null,
"emulator": "retroarch"
}
]
}Rules:
rom_idandpathare required.pathmay be absolute or relative to the manifest file directory.file_nameis optional; if omitted, the filename frompathis used.slotandemulatorare optional.
sync plan and sync run both:
- Read all manifest save files.
- Compute local metadata per file:
updated_at(UTC RFC3339)file_size_bytescontent_hash
- Call
POST /api/sync/negotiate.
sync run then executes operations:
upload:POST /api/saveswithrom_id,device_id, andsession_id.download:GET /api/saves/{id}/contentand writes to--download-dir(or manifest directory).no_op: no file change.conflict: fail or skip based on--conflict.
After processing operations, sync run always calls:
POST /api/sync/sessions/{session_id}/complete
with operations_completed and operations_failed.
To match RomM server behavior:
- normal files: MD5 of file bytes
- zip files: MD5 of a deterministic string built from sorted zip entries as
name:entry_md5, joined by newlines
This keeps negotiate comparisons consistent for save files that are zip archives.
When you download a save from game detail (D), romm-cli writes files using the same per-console layout as ROM downloads:
- Base: Settings → Saves → Save Dir (or
{download_dir}/saveswhen unset) - Default per console:
{save_dir}/{platform-slug}/{game-name}/ - Custom override:
save_sync.platform_dirsinconfig.json(absolute path per platform ID)
Configure overrides in Settings → Saves → Save console paths. CLI sync plan / sync run still use explicit paths from your manifest file.