hub v0.12.0: retire Infra Backup, purge its plaintext secrets, fix backup-deadline email
Phase-1 of SPIKE-infra-backup-2026-06-15. The infra-backup mechanism was dead since slice 8C yet stored plaintext customer secrets at rest (app-secret key, restic password, Cloudflare tokens) — a zero-knowledge violation — and its absence made the daily expected_backup_missed email fire for healthy customers. - Repoint monitor.CheckBackupDeadlines backup half to the agent host-report's PBS snapshots (+vzdump): alarm only on no-backup / >26h stale / verify failed. Keep the db_dump half. No host-report → no backup alarm (liveness owns that). New store.GetLatestHostReportJSON. Tests incl. a companion that fails pre-fix. - Remove the infra-backup endpoints, store methods/types, and operator panel; /recovery now returns config_yaml only. - migrate(): DROP infra_backup_versions/infra_backups + VACUUM (+wal_checkpoint) to physically reclaim the plaintext pages, gated on table existence. Flagged out-of-scope: exposed creds need operator rotation; legacy reports table holds historical plaintext restic_password rows (separate leak, not purged here). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+16
-27
@@ -18,7 +18,7 @@ A lightweight Go service that receives periodic reports and structured events fr
|
||||
│ │ │ ┌─────────────────┐ │
|
||||
│ POST /api/v1/ │ │ │ API Handler │ │
|
||||
│ report │ │ │ (ingest reports, │ │
|
||||
│ infra-backup │◀── config push ────│ │ infra backups, │ │
|
||||
│ host-report │◀── config push ────│ │ host reports, │ │
|
||||
│ notify │ (YAML body) │ │ config push, │ │
|
||||
│ │ │ │ asset serving) │ │
|
||||
│ GET /api/v1/ │ │ └────────┬────────┘ │
|
||||
@@ -27,7 +27,7 @@ A lightweight Go service that receives periodic reports and structured events fr
|
||||
│ │ SQLite Store │ │
|
||||
Operator browser │ │ (reports, │ │
|
||||
┌─────────────────┐ │ │ assets, │ │
|
||||
│ Web Dashboard │◀── HTML pages ──────│ │ infra_backups, │ │
|
||||
│ Web Dashboard │◀── HTML pages ──────│ │ host_reports, │ │
|
||||
│ (hub.felhom.eu) │ (bcrypt auth) │ │ configs, │ │
|
||||
└─────────────────┘ │ │ notifications) │ │
|
||||
│ └─────────────────┘ │
|
||||
@@ -62,44 +62,34 @@ All API endpoints require `Authorization: Bearer <api_key>` (except `/healthz` a
|
||||
|
||||
The `POST /api/v1/report` handler (v0.4.0+) automatically parses the optional `app_telemetry` JSON array from the request body and stores it in `app_telemetry` / `app_log_issues` tables. Old controllers (no `app_telemetry` key) continue to work unchanged.
|
||||
|
||||
### Infrastructure Backup (Disaster Recovery)
|
||||
### Infrastructure Backup — RETIRED (Phase-1, 2026-06-16, hub v0.12.0)
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| `POST` | `/api/v1/infra-backup` | Controller pushes infrastructure snapshot |
|
||||
| `GET` | `/api/v1/infra-backup/{customer_id}` | Fresh controller pulls backup for restore |
|
||||
|
||||
The infra-backup payload contains everything needed to restore a customer deployment:
|
||||
- `controller.yaml` (base64, full config including secrets)
|
||||
- `settings.json` (base64, backup preferences, storage paths)
|
||||
- Disk layout (UUIDs, labels, mount points, fstab options, bind-mount topology)
|
||||
- Deployed stacks manifest (app names, HDD paths, display names)
|
||||
- Restic passwords (primary + cross-drive, for encrypted backup access)
|
||||
|
||||
**Disaster recovery flow:**
|
||||
1. Customer's system drive fails → replaced with fresh Debian install
|
||||
2. `docker-setup.sh` deploys controller with minimal config (domain only)
|
||||
3. Controller enters setup wizard → user chooses restore from local drive or Hub
|
||||
4. For Hub restore: calls `GET /api/v1/recovery/{customer_id}` (gets config + infra backup)
|
||||
5. Controller uses disk UUIDs to auto-mount surviving drives
|
||||
6. Controller restores apps from local backups on those drives
|
||||
The Infra Backup mechanism (`POST/GET /api/v1/infra-backup`, the operator panel, the
|
||||
`infra_backup_versions` / `infra_backups` tables) has been **removed**. It had been dead since
|
||||
slice 8C (the disk-tier backup moved to the host agent), and it stored each version as a **plaintext
|
||||
JSON blob** holding the customer's app-secret encryption key, restic password, and Cloudflare tokens —
|
||||
a zero-knowledge violation. The retirement migration `DROP`s both tables and `VACUUM`s the DB to
|
||||
physically reclaim the plaintext pages. Disaster recovery now rests on the agent's **PBS whole-CT
|
||||
snapshot** (the data bytes) plus the generated `controller.yaml` from the recovery endpoint (the
|
||||
config); a secret-free **DR recipe** is the later DR slice's job. See
|
||||
`documentation/audits/SPIKE-infra-backup-2026-06-15.md`.
|
||||
|
||||
### Recovery (Disaster Recovery)
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| `GET` | `/api/v1/recovery/{customer_id}` | Combined recovery: returns generated controller.yaml + infra backup in one response |
|
||||
| `GET` | `/api/v1/recovery/{customer_id}` | Returns the generated controller.yaml for a customer |
|
||||
|
||||
Auth: `X-Retrieval-Password` header (same per-customer password as config retrieval). Response:
|
||||
```json
|
||||
{
|
||||
"customer_id": "example",
|
||||
"config_yaml": "customer:\n id: example\n ...",
|
||||
"infra_backup": { ... },
|
||||
"has_infra_backup": true
|
||||
"has_infra_backup": false
|
||||
}
|
||||
```
|
||||
If no infra backup exists yet, `infra_backup` is null and `has_infra_backup` is false.
|
||||
The `has_infra_backup` field is retained as `false` so any old client degrades gracefully to the
|
||||
config-only path.
|
||||
|
||||
### Report Response
|
||||
|
||||
@@ -228,7 +218,6 @@ SQLite with WAL mode. Tables:
|
||||
|-------|---------|
|
||||
| `reports` | Full JSON reports with denormalized fields for dashboard queries |
|
||||
| `events` | Structured events from controllers and Hub (type, severity, message, details, source) |
|
||||
| `infra_backups` | Per-customer infrastructure snapshots for disaster recovery |
|
||||
| `customer_notifications` | Email, enabled event types, cooldown hours per customer |
|
||||
| `notification_log` | Send/skip/fail history for notifications with channel (operator/customer) |
|
||||
| `customer_configs` | Pre-configured customer settings, retrieval passwords, per-customer API keys, status (active/blocked) |
|
||||
|
||||
Reference in New Issue
Block a user