# validateBackup

Validates that a backup server holds a faithful copy of an active server's data, by comparing files inside a ZFS snapshot shared by both machines — existence, size, and mtime for every file, plus a cryptographic digest for a configurable sample (or every file, in full mode). Built because a ZFS replication pipeline (`replicate`/`sneakernet`) can silently drift or corrupt data without ever producing a visible error; this tool is the independent check that the two sides still actually agree. See [Documentation/ProblemDefinition.md](Documentation/ProblemDefinition.md) for the full design rationale and [Documentation/validateBackup.md](Documentation/validateBackup.md) for a function-level reference.

## Contents

| File | Purpose |
|---|---|
| `validateBackup` | The script. Run on the backup server; reaches the active server over ssh. |
| `validateBackup.datastructure` | Commented default configuration, used to generate `validateBackup.conf.yaml` on first run. |
| `USAGE.md` | Every command-line option and configuration key. |
| `Documentation/validateBackup.md` | Reference documentation: architecture, phases, function inventory, known limitations. |
| `Documentation/TESTING.md` | Manual test plan for a FreeBSD test host. |
| `CHANGELOG.md` | Per-stage change history. |

## Requirements

Runs on the backup server as root (or a user with `zfs`/`zpool` read access and ssh access to the active host). Both the active and backup hosts must be FreeBSD, using only base-system `find`, `stat`, a digest command (`sha256`/`md5`/`cksum`), `sort`, and `xargs`. The active host must be reachable over passwordless ssh (a key in `~/.ssh/authorized_keys`, `BatchMode=yes` is always used so a run never blocks on a password prompt).

## Basic usage

Required: `activeHost` and at least one entry under `datasets` in the config file — there is no way to invoke this usefully with command-line-only parameters, since dataset mapping is inherently a config-file concern. See `USAGE.md` for the full parameter and configuration reference.

```
# First run: generates validateBackup.conf.yaml from validateBackup.datastructure. Edit that
# file (activeHost, activePrefix/backupPrefix, datasets) before running for real.
./validateBackup --help

# See exactly what would be checked - snapshot resolution, scrub status, and the commands that
# would run - without reading any file data.
./validateBackup --dry-run

# A normal sampled validation run, as cron would invoke it.
./validateBackup

# A full pass: digest every file instead of a random sample. Slow - reads every byte on both
# pools - so this is the quarterly option, not the default.
./validateBackup --full
```

## Cron example

Intended to run unattended. A `monthly` and `quarterly` `sessionType` profile are already defined in the sample config (`sessionType.monthly` sets normal sampling, `sessionType.quarterly` is equivalent to `--full`) — select one by name rather than repeating flags in the crontab:

```
# Sampled validation, first Sunday of every month at 3am
0 3 1-7 * *   [ "$(date +\%u)" = 7 ] && /path/to/validateBackup --sessionType monthly

# Full validation, first Sunday of January/April/July/October at 3am
0 3 1-7 1,4,7,10 *   [ "$(date +\%u)" = 7 ] && /path/to/validateBackup --sessionType quarterly
```

For quiet, log-friendly unattended runs, set `verbosity: 1` (the default) rather than a higher debugging level, and leave `displayLogsOnConsole` at its default (`1`) — cron already discards a script's stdout/stderr unless it's non-empty or `MAILTO` is set, so console output does not by itself generate cron mail. The tool reports outcome by email/report-drive via its own `report` config, independent of cron's own mail handling; a monthly run that finds nothing produces `Result: CLEAN` and exit code `0`. Do not add `--verbosity` above `1` to a cron entry — that is a debugging aid, not an operational setting, and increases log volume substantially. A **lock file** (`<script path>.lock` by default) prevents two runs from overlapping; a second invocation that finds a live lock logs a note and exits `0` without sending a report, so an occasional cron overlap is silent rather than noisy.

## A note on autoscrub

`autoscrub` (default **off**) is the one operation in this tool that mutates system state: when enabled, it starts a `zpool scrub` on a stale pool and waits for it to finish — potentially many hours — before validating. Read [Documentation/ProblemDefinition.md](Documentation/ProblemDefinition.md)'s "Active-server safety" section before turning it on in a cron job, since a scrub competes with production I/O on the active server for its whole duration.
