23, "wing" => "Operations", "title" => "Last night's backup works", "promise" => "Everything is backed up nightly, a copy leaves the server, and the backup is opened to prove it.", "block" => "Add `backup_check = ` to ratcloud.conf: after each nightly backup root counts that table in the copy and writes it to RC_FACTS/backup.json.", "files" => ["ratcloud.conf"], "show" => function (?array $me): string { $b = fact("backup.json"); if (!$b || !$b["latest"]) return '

No verified backup yet.

'; $l = $b["latest"]; $o = $b["offsite"] ?? null; $rb = $l["readback"][env("RC_ENV")] ?? null; // root reads back each environment's own copy return '"; }, "check" => function (): array { $b = fact("backup.json"); $l = $b["latest"] ?? null; if (!$l) return [false, "RC_FACTS/backup.json has no verified backup"]; $age = (time() - strtotime($l["at"])) / 3600; if ($age > 26) return [false, sprintf("the newest verified backup is %.0f hours old", $age)]; $rb = $l["readback"][env("RC_ENV")] ?? null; $count = $rb["rows"] ?? null; if ($count === null) return [false, "the backup has no guestbook count read back from it" . (isset($rb["error"]) ? ": " . $rb["error"] : "")]; $o = $b["offsite"] ?? null; if (!($o["ok"] ?? false) || time() - strtotime($o["at"]) > 26 * 3600) return [false, "no copy left the server in the last day"]; return [true, sprintf("backup %.0f hours old, %d guestbook rows read back from it, offsite copy %s", $age, $count, $o["stamp"] ?? "")]; }, ];