SafeMirror 1.0.0 - 3-2-1 backup rule auditor ============================================ SafeMirror answers one question about the backups you ALREADY have: "is every file I care about really protected?" You declare a source tree and the backup locations you believe protect it. SafeMirror then reads every file, compares it to its counterpart in each location by SHA-256, and reports PER FILE how many good copies exist, on how many distinct media, and whether any of them is offsite - then names exactly which files break the rule and why. It is strictly read-only. It never creates, copies, moves or deletes a backup. WHAT THIS TOOL IS NOT --------------------- - It is NOT a backup program. It does not create backups, mirror trees, schedule jobs, snapshot volumes or restore anything. If you need to MAKE backups, use the tools built for that: RescueVault / RestoreGuard - hardlink-based incremental backup with GFS retention SyncProof - one source pushed to many destinations, each verified independently MirrorFlow - two-way sync between two trees SafeMirror is the auditor that tells you whether what those tools (or a human with a USB disk) produced actually satisfies 3-2-1. - It CANNOT verify "offsite" or "medium". Both are user declarations in the config file. If you label an external USB disk that lives on the same desk as "offsite: true", SafeMirror will believe you and report a green audit that is a lie. The tool has no way to see where a directory physically is or what hardware backs it; it only sees a path. - It does not talk to any cloud API. A "cloud" location must already be mounted or synced into the local filesystem as a normal directory path (rclone mount, an OS drive client's local folder, an SMB/NFS mount, ...). If that folder is a placeholder/stub-file cache rather than real content, SafeMirror hashes whatever the filesystem gives it. - It does not detect files that exist in a backup but NOT in the source (extra/orphaned backup files). The source defines the file list. - It does not follow symlinks, and does not audit devices, sockets or FIFOs. Non-regular entries under the source are skipped and counted in a note. - It does not read file permissions, ownership, xattrs or timestamps into the verdict. Content equality is decided by SHA-256 alone. - There is no --apply flag, because nothing in this tool writes. Every command opens files for reading only. COMMANDS -------- safemirror audit --config [--rule 3-2-1] [--json] safemirror locations --config [--json] safemirror explain --config [--json] safemirror config --example safemirror help | -h | --help safemirror version audit Walks the source, hashes every regular file, looks for the same relative path in every declared location, hashes that too, and classifies the file. Prints a per-file table, the failing rule component for every violation, totals per verdict, the worst offenders by bytes at risk, and coverage (files fully protected, percentage, bytes at risk). locations One row per declared backup location: declared medium, declared offsite flag, reachability, and how many source files it holds, matches, holds stale, or is missing - plus the bytes it holds for those files. explain Everything known about ONE file: its SHA-256, the path and status of every copy, a PASS/FAIL line for each of the three rule components, the failing components in words, and the verdict. Use it when the audit verdict for a file surprises you. config --example Prints a valid, ready-to-edit config on stdout. CONFIG FILE ----------- { "source": "/home/you/Documents", "locations": [ {"name": "nas-main", "path": "/mnt/nas/documents", "medium": "nas", "offsite": false}, {"name": "usb-hdd", "path": "/media/you/hdd/documents", "medium": "hdd", "offsite": false}, {"name": "offsite-cloud", "path": "/mnt/cloud-drive/documents", "medium": "cloud", "offsite": true} ] } source Directory whose files are the thing being protected. Required. locations At least one. Each needs a unique name, a unique path, a medium and an offsite flag. medium One of: hdd, ssd, nas, optical, cloud, tape. offsite true/false, your assertion that this copy lives somewhere a fire, flood or burglary at the source would not reach. Paths may be relative; they are resolved against the current directory. A location may not point at the source directory itself. THE RULE, AND HOW COPIES ARE COUNTED ------------------------------------ --rule A-B-C means: at least A good copies, on at least B distinct media, with at least C of them offsite. The default is 3-2-1. * THE SOURCE COUNTS AS GOOD COPY #1. So --rule 3-2-1 is satisfied by the source plus two matching backup copies. * DISTINCT MEDIA AND OFFSITE COPIES ARE COUNTED OVER BACKUP LOCATIONS ONLY. The source has no declared medium and is never treated as offsite, because in the failure scenarios 3-2-1 exists to survive, the source is the thing that just died. * Two locations that declare the same medium count as ONE distinct medium. Two USB hard disks are one medium; a hard disk and a cloud folder are two. * Only a copy whose SHA-256 equals the source's counts. A file that is present but different is stale and contributes nothing - not to the copy count, not to media, not to offsite. * A location that cannot be reached (path missing, not a directory) contributes no copies at all. The audit still runs and still reports the other locations; unreachable locations are flagged and force exit 2. Other rules parse and genuinely change the verdict: 2-2-1 is a laxer "two copies, two media, one offsite"; 1-1-0 asks only that a single backup copy exists anywhere. Components must be non-negative integers 0..64. VERDICTS -------- PROTECTED Meets every component of the rule. MISSING The file is in NO backup location at all. STALE The rule fails and at least one location holds a copy whose content differs from the source. UNDER-REPLICATED Too few good copies. SINGLE-MEDIUM Enough copies, but all of them on one declared medium. NO-OFFSITE Enough copies on enough media, but none offsite. Exactly one verdict per file. When more than one component fails, the verdict shown is the first match in the order MISSING, STALE, UNDER-REPLICATED, SINGLE-MEDIUM, NO-OFFSITE; every failing component is still listed underneath it (and in the JSON "reasons" array), so nothing is hidden by the headline label. A file can be PROTECTED while some location holds a stale copy of it - if the rule is already satisfied by other, good copies. That case is never silent: the audit table's STALE column and the file's "notes" say which location drifted. EXIT CODES ---------- 0 audit clean, or the command completed with no rule violation 1 usage error, bad or unreadable config, source missing, bad --rule 2 at least one file violates the rule, or a location is unreachable EXAMPLES -------- safemirror config --example > backup.json # edit backup.json, then: safemirror audit --config backup.json safemirror audit --config backup.json --rule 2-2-1 safemirror audit --config backup.json --json | jq '.coverage' safemirror locations --config backup.json safemirror explain work/spec.md --config backup.json PERFORMANCE NOTES ----------------- Every audit is a full re-hash of the source and of every copy found: there is no cache and no size/mtime shortcut. This is deliberate - a size-and-date comparison cannot see silent corruption or an edited file that kept its length - but it means runtime scales with total bytes, not with file count. Work is spread over up to 8 workers (number of CPUs, capped). ROADMAP (NOT IMPLEMENTED TODAY) ------------------------------- - Creating the copies the audit says are missing: a "safemirror repair" that copies under-replicated files to the location that would fix the rule, dry run by default and requiring --apply. - Native cloud object storage (S3, B2, Azure) so an offsite copy no longer has to be a mounted path, including cheap remote-side checksum comparison instead of downloading every object. - Scheduled audits with alerting: run nightly, keep a history of coverage, and notify when coverage drops or a location goes unreachable. - A hash cache keyed on size+mtime so repeat audits of a large unchanged tree finish in seconds, with a --paranoid flag to force full re-hashing. - Verification of the declarations the tool currently has to trust: device identity for "medium", and geo/host checks for "offsite". - Auditing files present in a backup but absent from the source, so accidental deletions in the source can be spotted. BUILD ----- go build -o safemirror . (Go 1.24+, standard library only, no third-party dependencies)