controller v0.93.0: NAS Part B off-box backup target (restic-over-SFTP)

Encrypted restic repo over SFTP for the app-data tier (the off-site 3-2-1 leg). A dead
NAS fails fast via -oConnectTimeout (spike Q8), never hangs the runner; secrets are 0600
files (ride DR via PBS whole-CT); init-if-absent, retention forget --prune, restore,
single-flight, per-app toggle + UI. restic re-added to the image.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HxLA1mZurFq9kt8hneFeCs
This commit is contained in:
2026-06-30 15:26:38 +02:00
parent ddeb509d1d
commit 2a7deadc93
11 changed files with 1059 additions and 0 deletions
+16
View File
@@ -415,6 +415,22 @@ func main() {
backupMgr.RunAllTier2()
return nil
})
// Off-box (NAS) restic-SFTP backup (Part B): the off-site leg. A failure (incl. a fail-fast dead-NAS
// error) alerts the operator via the allowlisted backup_failed event. Daily after Tier 2.
backupMgr.SetOffboxNotify(func(dur time.Duration, snapshots int, err error) {
if err != nil {
notifier.NotifyBackupFailed("Off-box (NAS) mentés sikertelen",
"a NAS-ra mentés hibázott ("+dur.Round(time.Second).String()+"): "+err.Error())
}
})
sched.Daily("offbox-backup", "04:15", func(ctx context.Context) error {
t := sett.GetOffboxTarget()
if t == nil || !t.Enabled || t.Schedule != "daily" || !backupMgr.OffboxConfigured() {
return nil // not configured / not scheduled
}
return backupMgr.RunOffboxBackup(ctx)
})
}
// Metrics prune — daily at 04:00