copy files from one location to another
- JavaScript 100%
| src | ||
| tests | ||
| .gitignore | ||
| config.example.json | ||
| package.json | ||
| README.md | ||
Xfer Files
A macOS-first copy tool that mirrors files from a source drive to a destination drive with priority-aware copying and explicit include/exclude rules.
Features
- Resumable copies that can be started, stopped, and resumed.
- Concurrent scan, hash, copy, verification, and repair stages so discovery and transfer progress can move at the same time.
- A central in-memory coordinator serves live dashboard state and batches recovery checkpoints to reduce metadata writes on the destination drive.
- High-priority folder selection for Desktop, Documents, Downloads, Pictures, Projects, and more.
- Hash-aware skip logic that leaves matching files alone and recopies same-size mismatches safely.
- Streaming-safe hashing and copying that avoid loading very large files into memory.
- Mirror-style copy layout under the destination root with a reserved state folder.
- A lightweight web UI with file progress, byte progress, throughput, queue depth, ETA, and a transfer-rate graph.
- Selectable Node.js or system-rsync transfer engines from the dashboard.
Getting started
- Install dependencies:
npm install - Copy the example config:
cp config.example.json config.local.json - Edit
config.local.jsonto point at your source folders and external backup drive. - Start the web dashboard:
npm start - Open the dashboard and use its start/stop toggle button.
- Use Recycle service after changing code to checkpoint and stop all workers, restart the Node process, and leave the backup ready for a manual start.
- View current status with
npm run backup:status
The CLI can start rsync mode directly with npm run backup:start -- --rsync.
Configuration
sourceRootshould point to the macOS home directory or a specific folder to back up.destinationRootshould point to an external USB drive such as/Volumes/BackupDrive/xfer-files.transferEnginesets the dashboard default tonodeorrsync; it can still be changed before each run.rsyncChecksumdefaults totrue, making rsync compare file content and apply metadata-only timestamp changes when hashes match. Set it tofalseonly if size/mtime quick checks are preferred over correctness.includecontrols which top-level folders are included.chunkedCopyThresholdMbcontrols when files switch to chunked, resumable copying. The default is50.copyConcurrencycontrols how many copy workers run at once. The default is4.parallelCopyMaxSizeMblimits the extra parallel workers to smaller files. The default is4; keeping this low avoids seek contention on spinning disks.highPrioritycontrols which folders are copied first.
Notes
- The example config is stored in
config.example.jsonand is safe to commit. - Your local overrides live in
config.local.json, which is ignored by git. - Backup state is stored under
.xfer-state/state.jsonin the destination root. - Per-file metadata is stored under
.xfer-statein the destination root. - Pending verification and repair work is persisted in
.xfer-state/verification-queue.jsonand.xfer-state/repair-queue.jsonso it survives restarts. - Routine state, queue, and chunk-manifest checkpoints are batched over five-second windows. A controlled stop or service recycle forces the latest state to disk before workers exit.
- Rsync mode uses the system
rsync, preserves interrupted data under.xfer-state/rsync-partial, and maps its per-file progress into the dashboard. It uses rsync's native size/mtime comparison rather than the Node engine's SHA-256 verification and repair pipeline. - Rsync progress uses the file and byte totals retained from the previous scan. Files rsync does not need to transfer are reconciled as skipped when the run finishes.
- When a completed Node scan has a saved pending queue, rsync consumes that catalog with
--files-from=-instead of recursively rediscovering the source tree. The dashboard shows a timed rsync-indexing phase until the first file record arrives. - Node copies preserve source access and modification times. Before the first rsync run after this upgrade, existing SHA-256 metadata is used for a one-time timestamp reconciliation recorded in
.xfer-state/timestamps-v1.json. - Standard Node and rsync APIs preserve modification/access timestamps but cannot reset the macOS creation/birth timestamp.
- Backup errors are appended to
.xfer-state/errors.ndjsonin the destination root.