Skip to content

Dry-run preview

--dry-run is your seatbelt. It computes and prints the exact plan the daemon would execute, as JSON, and then exits — without uploading, downloading, deleting, or writing the index. Use it before the first real sync of a new folder, after changing roots, or whenever behavior surprises you.

Terminal window
proton-syncd \
--local-root /tmp/proton-sync-demo \
--remote-root /Drive/RemoteFolder \
--dry-run

Dry-run still contacts Proton Drive through the proton-drive CLI (to list the remote), so authentication and remote permissions must already work. It does not bind the IPC socket, take the daemon lock, or run any side effect.

{
"summary": {
"total": 1,
"uploads": 0,
"downloads": 1,
"remote_directories_created": 0,
"local_directories_created": 0,
"local_moves": 0,
"remote_moves": 0,
"auto_links": 0,
"conflicts": 0,
"type_conflicts": 0,
"remote_deletes": 0,
"local_deletes": 0,
"purges": 0,
"skipped_unsupported": 0,
"destructive_actions": 0
},
"plan": [
{
"path": "notes.txt",
"destination_path": null,
"action": "download",
"entity_kind": "file",
"conflict_path": null,
"remote_id": "remote-file-id"
}
]
}

Every field is always present, so scripts can rely on a fixed shape. Unused summary counters are 0, and the optional plan fields (destination_path, conflict_path, remote_id) are null when they don’t apply.

  • action — one of upload, download, create_remote_directory, create_local_directory, move_local, move_remote, auto_link, conflict, type_conflict, remote_delete, local_delete, purge, skip_unsupported. See what each means.
  • entity_kind"file" or "directory".
  • destination_path — populated only for the two move actions.
  • conflict_path — populated only for a conflict, naming the .proton-cloud sidecar.
  • remote_id — the Proton Drive node id, when known.

The number that matters: destructive_actions

Section titled “The number that matters: destructive_actions”

destructive_actions is the sum of remote_deletes, local_deletes, and purges — the count worth a second look before you run for real. On a first sync of a fresh pair it should be 0, because the first pass is a non-destructive merge.

Note the two “destructive” senses the tooling distinguishes:

  • Display-destructiveremote_delete, local_delete, and purge. These are tinted red and sorted first in the app’s plan view, and counted in destructive_actions.
  • Gated — only remote_delete and local_delete. A purge is index-only cleanup, so a purge-only plan is never gated by the delete-approval guard or by the app’s typed-DELETE confirmation.

The app’s Plan preview screen runs the same dry-run and renders it as a summary grid plus one row per action, destructive rows floated to the top. When the plan contains a real delete, Apply is inert until you type DELETE to arm it, and the confirmation names the files at risk. See Screens.

One honest caveat the app surfaces: the dry-run and the real reconcile are separate invocations, so a plan applied a moment later can differ from the one you reviewed if something changed in between. Re-run the preview if you want a fresh check.

Dry-run respects your --include/--exclude filters, so it’s the right way to confirm a selective-sync rule set before enabling regular sync.