# MoveGuard (CLI prototype) Working prototype of the MoveGuard concept: a checksum-verified file MOVE that copies a file, re-reads and hash-verifies the freshly written destination copy, and only then deletes the source — resumable, and parallelized across workers. It is the "Pro" sibling of CopySure, which only ever copies (source is always left untouched). MoveGuard's job is riskier by nature — it removes data — so the entire point of this build is the safety invariant: a source file is never deleted until its destination copy has been independently proven byte-identical by SHA-256, and if verification ever fails, the source is left completely alone while the tool moves on to the rest of the batch. Explorer integration and sync rules from the full concept are on the roadmap — see `../plan.md` for the full product plan; this build is the move/verify engine only. ## Build from source Requires Go 1.24+, no external dependencies. go build -o moveguard . Cross-compile for another platform: GOOS=windows GOARCH=amd64 go build -o moveguard.exe . GOOS=darwin GOARCH=arm64 go build -o moveguard . ## Usage moveguard move [--workers N] [--resume] [--dry-run] `move` walks `` (a single file or a directory tree) and, for each file: 1. Copies it to a temp name at the destination (`.moveguard-tmp`). 2. Re-reads and re-hashes the freshly written destination file with SHA-256 and compares it against the source's hash — the copy syscall's reported success is never trusted on its own. 3. Only if the hashes match: renames the temp file to its final name, then deletes the source file. 4. If the hashes do NOT match: the source file is left completely untouched, the corrupted temp copy is left at the destination as evidence, an error is reported for that file, and the tool moves on to the rest of the batch rather than aborting. A SOURCE FILE IS ONLY EVER DELETED AFTER ITS DESTINATION COPY HAS BEEN HASH-VERIFIED BYTE-IDENTICAL. This is the whole point of the tool. `--resume` lets you safely re-run a move that was interrupted partway through. For each source file, if the destination already has a file with a matching SHA-256 hash, the copy step is skipped and the tool goes straight to (re-)verifying and deleting the source — so a job that was killed mid-run (some files already fully moved, some copied but not yet deleted, some untouched) can be re-run to completion without wasted re-copies and without ever risking a source delete on an unverified file. Files whose move already fully completed in a prior run (source already gone) are simply not found when the source tree is walked again, so they are neither reprocessed nor reported as errors. `--workers N` (default 4) processes files in parallel. ## --dry-run moveguard move --dry-run Lists every file the move would take, its size and the exact destination path it would go to, and reports the problems the real run would hit — then stops. It copies nothing, creates no folders, renames nothing and deletes nothing. It exists because `move` deletes originals. Every other tool in the suite can be tried out on a copy of the job to see what it does; this one cannot, so the only two things that could otherwise be offered were a run that destroys data with no preview, or a "preview" that really performed the move. The second is worse than none at all, because it is believed. What it checks, per file, is what the real run asks at the moment it would act rather than a guess made from what the file looks like: * The source opens for reading. The real run has to read the file right through to hash it, so one it cannot open is one it cannot move. * Nothing is already at the destination path. A file already there is silently written over by the real run, so this is reported — as a warning if the bytes differ, and as an already-finished copy if they match and `--resume` was given. A *directory* sitting on a file's destination path is reported as a failure, because the real run's final rename cannot replace one. * The destination can actually be written to. Missing folders are not a problem — the real run creates them — so what is checked is the nearest folder on the path that does exist. See the note below. * A file where a folder needs to go, and a destination whose drive does not exist, are both reported rather than discovered halfway through. Exit status is 0 when the move looks clean and 2 when the report contains anything worth reading first, so `--dry-run` can be used as a check in a script. A source that cannot be walked at all exits 1, exactly as the real run does. `--resume` and `--workers N` both keep their meanings under `--dry-run`: files are inspected in parallel, and under `--resume` a destination copy whose hash already matches is reported as work already done instead of as something in the way. ### The one thing the dry run writes Whether a folder will accept a new file cannot be read off the folder. On Windows the permissions that actually decide are access control lists, which a stat cannot see, and a folder can look perfectly writable and refuse the first write. A dry run that reported "fine" and left the real run to fail would be worse than no dry run, so instead of guessing, the check tries: it creates one empty file named `.moveguard-write-check--`, closes it and removes it again immediately. The create is exclusive, so it can never touch a file of yours, it is done once per destination folder however many files are headed there, and if the removal ever fails that is reported as a problem rather than passed over. Nothing else is ever written, and no file of yours is read for anything but its own hash. ## Prebuilt binaries See ../downloads/ for prebuilt binaries (Windows/macOS/Linux) and CHECKSUMS.txt for their SHA-256 hashes. These are unsigned indie builds — Windows SmartScreen and macOS Gatekeeper will warn on first run, which is expected until a code-signing certificate is in place.