diff --git a/CHANGELOG.md b/CHANGELOG.md index f0364ab..79feb51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ ## Changelog +### v0.69.0 — remove dead infra-backup stubs + the unused restic-password report field (2026-06-16) + +Controller half of the Phase-1 Infra Backup retirement (hub v0.12.0; see +`felhom.eu/documentation/audits/SPIKE-infra-backup-2026-06-15.md`). Pure dead-code removal — no +behaviour change (everything removed was already caller-less). + +- **Removed `report.Pusher.PushInfraBackup`** — pushed the infra-backup payload to the now-removed hub + endpoint `POST /api/v1/infra-backup`. Dead since slice 8C; no callers. +- **Removed `notify.Notifier.NotifyBackupCompleted`** (the `backup_completed` event) — no callers + since whole-guest backup moved to the agent in slice 8C. The hub's backup-deadline check now reads + the agent host-report's PBS snapshots instead of this event. `NotifyBackupFailed` and the DB-dump + notifiers are untouched and still used. +- **Removed `report.BackupReport.ResticPassword`** (`json:"restic_password"`) — the live report + builder (`buildBackupReport`) has left it empty since slice 8C, but the field historically leaked + the restic password into the hub's plaintext `reports` store. Confirmed via pushed source (builder + never sets it) **and** live data (current reports carry no `restic_password`) before removal. + ### v0.68.3 — fix Beállítások page endless-refresh loop after a migration (2026-06-15) Found while live-validating the M3 migration: once any data migration finished, the **Beállítások diff --git a/controller/README.md b/controller/README.md index dfcdcfd..a44591a 100644 --- a/controller/README.md +++ b/controller/README.md @@ -963,7 +963,6 @@ The controller pushes structured events to the Hub's `/api/v1/event` endpoint. T | Event Type | Severity | Trigger | |------------|----------|---------| -| `backup_completed` | info | Nightly restic backup succeeds | | `backup_failed` | error | Nightly restic backup fails | | `db_dump_completed` | info | Nightly database dumps succeed | | `db_dump_failed` | error | Nightly database dumps fail | @@ -1242,9 +1241,18 @@ Each report push now includes per-app telemetry data: - `buildAppTelemetrySection()` calls both, then `buildAppTelemetry()` aggregates by stack — summing container metrics, merging issues, capping at 10 per app. Additionally, `buildControllerTelemetry()` creates a special entry for the controller container itself (`app_name: "felhom-controller"`). - Results stored as `[]AppTelemetry` in the `Report` struct field `app_telemetry`. -#### Infrastructure Backup to Hub (`internal/report/infra_backup.go`) +#### Infrastructure Backup to Hub — RETIRED (2026-06-16) -After each backup cycle (including manual Tier 2 triggers via `OnCrossDriveComplete` callback), the controller pushes a full infrastructure snapshot to the Hub for disaster recovery. This snapshot includes: +> **Removed.** The controller no longer pushes any infra-backup to the Hub, and the Hub no longer +> accepts or stores one (hub v0.12.0). The builder (`internal/report/infra_backup.go`) and local +> mirror (`internal/backup/local_infra.go`) were deleted back in slice 8C; the last caller-less stub +> (`Pusher.PushInfraBackup`) and the `backup_completed` event were removed in controller v0.69.0. +> DR now rests on the agent's PBS whole-CT snapshot + the Hub-generated controller.yaml. The text +> below is **historical** and describes the removed mechanism — much of this section (and the +> `local_infra.go` / `setup/scanner.go` / `PullRecovery` / `restore_drives` references elsewhere in +> this README) is stale slice-8C debt. See `felhom.eu/documentation/audits/SPIKE-infra-backup-2026-06-15.md`. + +After each backup cycle (including manual Tier 2 triggers via `OnCrossDriveComplete` callback), the controller pushed a full infrastructure snapshot to the Hub for disaster recovery. This snapshot included: - `controller.yaml` (base64-encoded, full config including secrets) - `settings.json` (base64-encoded, backup prefs, storage paths, cross-drive configs) - Disk layout (UUIDs, labels, mount points, fstab options, bind-mount topology) diff --git a/controller/internal/notify/notifier.go b/controller/internal/notify/notifier.go index 27aa96b..61742c8 100644 --- a/controller/internal/notify/notifier.go +++ b/controller/internal/notify/notifier.go @@ -276,10 +276,10 @@ func (n *Notifier) NotifyBackupFailed(message, errMsg string) { n.PushEvent("backup_failed", "error", message, BackupDetails{Error: errMsg}) } -// NotifyBackupCompleted sends a backup success event. -func (n *Notifier) NotifyBackupCompleted(details BackupDetails) { - n.PushEvent("backup_completed", "info", "Biztonsági mentés elkészült", details) -} +// (NotifyBackupCompleted removed 2026-06-16 — the backup_completed event had no callers +// since slice 8C moved whole-guest backup to the agent. The hub's backup-deadline check +// now reads the agent host-report's PBS snapshots instead of this event. DB-dump events +// below are still emitted and consumed.) // NotifyDBDumpFailed sends a DB dump failure event. func (n *Notifier) NotifyDBDumpFailed(message, errMsg string) { diff --git a/controller/internal/report/pusher.go b/controller/internal/report/pusher.go index 74a4552..8562d88 100644 --- a/controller/internal/report/pusher.go +++ b/controller/internal/report/pusher.go @@ -142,55 +142,8 @@ func (p *Pusher) GetStatus() PushStatus { return p.status } -// PushInfraBackup sends the infrastructure backup payload to the Hub. -// Uses the same retry logic as Push. -func (p *Pusher) PushInfraBackup(data []byte) error { - if !p.enabled { - return nil - } - - url := p.hubURL + "/api/v1/infra-backup" - if p.debug { - p.logger.Printf("[DEBUG] [report] PushInfraBackup: url=%s payload=%d bytes", url, len(data)) - } - - var lastErr error - for attempt := 0; attempt < 3; attempt++ { - if attempt > 0 { - time.Sleep(5 * time.Second) - } - - req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(data)) - if err != nil { - lastErr = err - continue - } - req.Header.Set("Content-Type", "application/json") - if p.apiKey != "" { - req.Header.Set("Authorization", "Bearer "+p.apiKey) - } - - resp, err := p.httpClient.Do(req) - if err != nil { - lastErr = err - continue - } - io.Copy(io.Discard, resp.Body) - resp.Body.Close() - - if resp.StatusCode >= 200 && resp.StatusCode < 300 { - p.logger.Printf("[INFO] [report] Infra backup pushed to Hub (%d bytes)", len(data)) - return nil - } - lastErr = fmt.Errorf("HTTP %d", resp.StatusCode) - if p.debug { - p.logger.Printf("[DEBUG] [report] PushInfraBackup: attempt %d failed — HTTP %d", attempt+1, resp.StatusCode) - } - } - - p.logger.Printf("[WARN] [report] InfraBackup push failed: %v", lastErr) - return fmt.Errorf("infra backup push failed after 3 attempts: %w", lastErr) -} +// (PushInfraBackup removed 2026-06-16 — the infra-backup mechanism was retired hub-side. +// It was dead since slice 8C, had no callers, and pushed plaintext secrets to the hub.) // PushOnce sends a single report regardless of the enabled flag. // Used for one-time notifications (e.g., reporting-disabled on startup). diff --git a/controller/internal/report/types.go b/controller/internal/report/types.go index 088936f..e08dd9f 100644 --- a/controller/internal/report/types.go +++ b/controller/internal/report/types.go @@ -82,7 +82,8 @@ type BackupReport struct { RepoSizeMB int64 `json:"repo_size_mb"` LastIntegrityCheck *time.Time `json:"last_integrity_check,omitempty"` IntegrityOK bool `json:"integrity_ok"` - ResticPassword string `json:"restic_password,omitempty"` + // (ResticPassword removed 2026-06-16 — the live builder never set it post-slice-8C; + // historically it leaked the restic password into the hub's plaintext report store.) } // HealthReport holds the aggregated health status.