Sync, copy and deletion safety
sync is the reason rs-suno exists: it keeps a local directory as a faithful
mirror of your Suno library, and it does so without ever putting your files at
risk. This chapter explains what a run does and the rules that make deletion
safe.
The mirror model
copyis additive. It downloads new clips and updates existing files, but it never deletes anything.syncis a full mirror. It does everythingcopydoes, and it also removes local files whose clips are no longer in your library.
Both verbs share the same selection and the same incremental engine. The only difference is whether local files may be removed.
What a run does
Each run works in three stages:
- Select. Enumerate the library, liked feed, and playlists, then apply any
--limitor--sincefilter. - Plan. Compare the desired state against a manifest of what is already on disk, and decide a set of actions.
- Execute. Apply the actions: download, re-encode, retag, rename, write
artwork, and (for
sync) delete.
A --dry-run, or the check command, stops after the plan and prints what it
would do, touching nothing.
Incremental by default
rs-suno keeps a manifest beside the destination and only does work that is
needed:
- Skip unchanged. A clip whose metadata hash, artwork hash, and file size all match the manifest is left alone.
- Retag and re-art in place. When only tags or artwork changed, the file is updated in place. The audio is not downloaded again.
- Rename in place. When only the target path changed (for example a retitled clip), the existing file is moved, not re-downloaded.
- Re-encode on format change. Changing
--formatreplaces the file by re-encoding, without pre-deleting the old one. - Re-download missing or empty files. A clip whose local file is absent, or is zero bytes, is treated as missing and downloaded again.
This makes repeat runs fast and cheap, which is what makes frequent scheduled runs practical.
Deletion safety
Deletion is the one irreversible action, so it is hedged with several independent rules. All of them must agree before a single file is removed.
Delete only what has truly left every source
A file is a candidate for deletion only when its clip is absent from every mirror source feeding that destination. A clip that is still present in any source is kept. In addition:
copyalways wins. A clip held by acopysource is never deleted, even if asyncsource no longer lists it.- Private clips are preserved. A clip marked private is never deleted.
- Trashed counts as removed. A clip you have trashed in Suno is treated as
gone and its local file is removed (unless a
copysource or the private rule preserves it).
The fully-enumerated gate
rs-suno will not delete anything unless the listing it is comparing against was
fully enumerated: the feed drained completely, with no transport error and
no truncation, and no narrowing filter was applied. In practice this means:
- A network or listing error disables deletion for that run.
--limitand--sincenarrow the listing, so a run using either never deletes. Use them freely for quick top-ups without any deletion risk.
A missing clip in a partial or filtered listing might still exist upstream, so it is never read as a deletion.
The mass-deletion abort
As a final backstop, a run aborts before deleting when the listing looks catastrophically wrong:
- An empty listing that would delete your whole library is refused.
- A delete that would remove at least half of a non-trivial library is refused.
Either abort exits with the safety code (7) and removes nothing. If you really
do intend a mass deletion, confirm it explicitly with --min-newest 0 --yes. A
stored min_newest = 0 or a habitual --yes alone will not disarm the
empty-listing guard.
The confirmation prompt
When a sync would delete files and you did not pass --yes:
- On an interactive terminal, it lists the files and asks
Proceed? [y/N]. Anything other thanyoryesaborts with no changes. - Without a terminal (a pipe, cron, or CI), it refuses and tells you to pass
--yesor usecopy.
suno sync will delete 3 local file(s) that are no longer in the source:
me/Weather/me-Old Draft [b3c4d5e6].flac
...
Proceed? [y/N]
Tidying up
After removing files, sync prunes any directories left empty, so the tree does
not accumulate stale folders. The destination root itself is always kept.
Robustness
Beyond deletion, several rules protect an in-progress run:
- One run at a time. A
syncorcopytakes an exclusive lock (.suno.lock) on the destination, so two runs cannot corrupt the same library. - Atomic writes. Files are written to a temporary sibling and renamed into place, so an interrupted write never leaves a half-written file.
- Size verification. A download whose byte count does not match what the server promised is rejected as a truncated transfer and retried.
- Rate-limit backoff. A
429response is retried with exponential backoff that honours the server’sRetry-Afterheader. - Resumable. Progress is recorded as it happens, so an interrupted run simply continues on the next run. This is what makes unattended cron or systemd runs safe.
Failure handling
Failures are classified so one bad clip never derails a whole run:
- Authentication failure stops that account cleanly and re-authenticates on the next run.
- Transient failure (a timeout, a
5xx, a rate limit) is retried up to--retriestimes, then recorded and skipped. - A single clip’s failure never aborts the run. Other clips still download, and the failure is reported in the summary and log.
What a run leaves behind
Alongside the mirrored audio, a run keeps a few dotfiles at the destination:
| File | Purpose |
|---|---|
.suno-manifest.json | The record of what is on disk, used for incremental runs. |
.suno-lineage.json | The durable archive of resolved remix and edit lineage. |
.suno-last-run | Timestamp used by --since last-run. |
.suno-audit.log | Append-only log of every deletion and rename. |
.suno-failures.log | Append-only log of clips that failed after all retries. |
.suno.lock | Present only while a run is active. |
The audit and failure logs are not written during a --dry-run or check.
Recipes
# Full mirror to the configured root, prompting before any deletion:
suno sync
# Full mirror, unattended (approve deletions up front):
suno sync --yes
# Fast top-up of just the last week, with no deletion risk:
suno sync --since 7d
# See exactly what a mirror would change, changing nothing:
suno check --exit-code