BackupMedic 1.0.0 Fleet backup-job health against an agreed RPO ============================================= WHAT IT IS BackupMedic answers the question a team lead has to answer on Monday morning: across every machine, when did each backup job LAST SUCCEED, which ones are silently failing, and which have breached the recovery point objective (RPO) we promised? Backup jobs append one line per run to a shared history file. BackupMedic reads that history and reports, per machine+job: - the last successful run and how old it is - whether that age breaches the RPO - the current consecutive-failure streak - the success rate over the last N runs - the trend in backup size, because a job whose output suddenly shrinks is a classic silent failure It is not a backup tool. It never copies, moves or deletes a file. The only thing it writes is a single appended line to the history file you name, and only when you run "record". THE HONEST LIMIT: BACKUPMEDIC BELIEVES THE JOB BackupMedic tracks what backup jobs REPORT. It cannot verify that a backup is actually restorable. A job can write an empty archive, a truncated image, an encrypted blob whose key was lost, or a perfect copy of already-corrupt data, report "ok" with a plausible byte count, and BackupMedic will call it HEALTHY. Nothing in this tool opens a backup, hashes it, mounts it or restores it. That is a deliberate boundary, not an oversight: the history file is the only input, so BackupMedic works for any job on any machine that can append a line. But it means BackupMedic is a smoke alarm, not a fire inspection. Pair it with something that actually looks at the data: - RestoreGuard / RescueVault - make the backups and prove a restore works - SafeMirror - audits whether a given file really has enough good copies, on enough distinct media, with one of them offsite A sane routine: BackupMedic tells you the job ran and did not shrink; a real periodic restore test tells you the result is worth having. Only the second one proves you have a backup. The SILENT-SHRINK verdict is the closest BackupMedic gets, and it is a heuristic on reported byte counts, nothing more. HOW RECORDS ARE COLLECTED: A SHARED FILE, NOT A NETWORK There is no agent, no server, no port, no push. BackupMedic does not phone home, does not listen, and does not reach out to any machine. Every machine's backup script calls "backupmedic record" at the end of a run and appends one line to a history file that all the machines can write to and that you can read: a file share, a synced folder, a NAS path, a mounted volume. How that file gets shared is your infrastructure's problem, deliberately. The history is append-only by construction. "record" opens the file in append mode and writes exactly one line; it never rewrites, reorders or deletes what is already there. (If the file does not end in a newline, it appends one first so the previous last record stays a whole line - existing bytes are still never changed.) Every other command opens the file read-only. INSTALL Copy the binary for your platform out of dist/ and put it on your PATH: dist/backupmedic-linux-amd64 dist/backupmedic-darwin-amd64 dist/backupmedic-darwin-arm64 dist/backupmedic-windows-amd64.exe No installer, no runtime, no dependencies. Build from source with: go build -o backupmedic . COMMANDS backupmedic record --machine --job --status ok|failed|partial --bytes --history [--started ] [--finished ] [--note "..."] Append one immutable run record. This is what a backup script calls when it finishes. --bytes is required; use 0 when the run wrote nothing. --finished defaults to now, --started defaults to --finished. backupmedic health --history [--rpo 24h] [--window 10] [--size-drop-pct 40] [--fail-streak 1] [--asof ] [--json] The per-job table and the reasons behind every verdict. Exits 2 if any job is not HEALTHY. backupmedic history --history [--machine X] [--job Y] [--last N] [--json] The raw run records, filterable. backupmedic summary --history [--rpo 24h] [--asof ] [--json] The one-screen fleet answer: healthy vs breaching, worst offender, and total protected bytes counting the most recent successful run of each job. backupmedic help | -h | --help usage, exit 0 backupmedic version version, exit 0 VERDICTS (worst first) NEVER-SUCCEEDED The job has run but has never reported ok. There is nothing to restore from at all. FAILING The last --fail-streak runs did not fully succeed. A partial run counts as not succeeded. Default streak is 1, i.e. the most recent run did not fully succeed. LATE The last success is older than the RPO. The promise you made about how much data you can lose is already broken. SILENT-SHRINK The newest successful run is more than --size-drop-pct smaller than the previous successful run. HEALTHY Succeeded inside the RPO, no failure streak, no size drop. A job's verdict is the worst rule it trips, but "health" prints EVERY reason it tripped, so a FAILING job that is also past its RPO says so on the next line. HISTORY FILE FORMAT One JSON object per line (JSONL), append only: {"machine":"nas01","job":"nightly","status":"ok","bytes":83886080, "started":"2026-08-11T01:00:00Z","finished":"2026-08-11T01:12:00Z"} Timestamps are RFC3339 and keep the offset they were written with. Comparisons are made on the INSTANT, so 2026-08-11T03:00:00+02:00 and 2026-08-11T01:00:00Z are the same moment and sort as the same moment, whatever their text looks like. Runs are ordered by finish instant, not by position in the file, so records that arrive late from a machine with a slow share still land in the right place in that job's history. Blank lines are ignored. A line that is not valid JSON, carries an unknown field, has an unknown status, a negative byte count, an unparseable timestamp, or finishes before it starts, is SKIPPED WITH A REASON and reported - every other run in the file is still evaluated. One corrupt line never blinds you to the rest of the fleet. Skipped lines do not by themselves change the exit code: they are a data problem, not a job health verdict. THE --asof FLAG "health" and "summary" measure ages against the current clock by default. Pass --asof to evaluate the fleet as of a chosen instant instead: records that finish after that instant are excluded. This is for reproducible reports ("what did Monday 09:00 look like?") and for testing a policy change against history you already have. EXIT CODES 0 every job is HEALTHY, or the command completed with nothing to judge 1 usage error, unreadable history, bad flag value 2 at least one job is not HEALTHY That makes it usable straight from cron or a monitoring check: backupmedic health --history /srv/backups/history.jsonl --rpo 24h || \ mail -s "backups breaching RPO" ops@example.com EXAMPLES # at the end of a backup script backupmedic record --machine nas01 --job nightly --status ok \ --bytes "$(stat -c %s /backups/nightly.tar.zst)" \ --history /srv/backups/history.jsonl # and when it fails backupmedic record --machine nas01 --job nightly --status failed --bytes 0 \ --note "rsync exit 23" --history /srv/backups/history.jsonl # Monday morning backupmedic summary --history /srv/backups/history.jsonl backupmedic health --history /srv/backups/history.jsonl --rpo 24h # a weekly job judged against a weekly promise backupmedic health --history /srv/backups/history.jsonl --rpo 168h # what happened on that laptop backupmedic history --history /srv/backups/history.jsonl \ --machine laptop7 --last 10 ROADMAP 1. Verified restores. Today BackupMedic takes the job's word for it. The next step is a verify record type: a job (or RestoreGuard) actually restores a sample from a backup, and BackupMedic tracks time-since-last-VERIFIED- restore as a second, harder clock alongside time-since-last-success. 2. Networked reporting. An optional lightweight collector so machines that cannot share a filesystem can still report, without turning this into a service you have to babysit. The shared file stays the default. 3. Alerting on breach. Fire once when a job crosses from HEALTHY to LATE, FAILING or SILENT-SHRINK, with a hold-down so a flapping job does not page anyone twice an hour. Today the exit code is the whole alerting story. None of these change the honest limit above: until item 1 lands, a HEALTHY verdict means "the job said it worked", not "you can get your data back".