Skip to content

Safe update + rollback UX (master) #140

Description

@frahlg

Goal

Build trust in a high-cadence auto-update flow. Operators should be able to:

  1. See what's changing before they click Update (feat(selfupdate): render changelog inline in update modal #139 ✓ — changelog inline).
  2. Have a safety net if they regret it (snapshot + rollback).
  3. Detect regressions quickly (post-update health check).
  4. Audit past updates (history).

High update cadence means every interaction matters — one bad update erodes months of accumulated trust.

Work breakdown

Phase 1 — Safe rollback bundle

  • Snapshot before update — sidecar dumps `state.db` (via `sqlite3 .backup`) + copies `config.yaml` into `snapshots/-pre--to-/` before `compose pull`. Retain last 5. Covered by a follow-up test suite.
  • Rollback-by-tag — API passes `target` through to the sidecar (sidecar already supports it). New endpoint `GET /api/version/releases` lists recent tags from GitHub so the UI can offer a picker.
  • Rollback UI — collapsible section in the update modal. Radio: "Keep current data" / "Restore snapshot from before the target version". Yellow warning for any backward step; red warning + explicit checkbox for > 1 minor version jump.

Phase 2 — Post-update health check

  • Frontend waits 30 s after sidecar reports `done`, pings `/api/status`, banner if any drivers offline or watchdog tripped.
  • Banner offers: Investigate | Roll back to previous snapshot.
  • No auto-rollback — too risky without human context.

Phase 3 — Update history

  • `GET /api/version/history` returns last 10 updates (ts, from→to, snapshot id, outcome).
  • UI "History…" button in modal renders the table.

Phase 4 — Breaking-change highlight

  • Detect `BREAKING CHANGE:` markers in release body (semantic-release emits them).
  • Render with red flag in the modal. Requires extra confirmation click before Update.

Out of scope (for now)

  • Canary/beta channel — premature; reconsider after ~20 deploys.
  • Schema versioning in `state.db` — use snapshot/restore instead of forward/backward migrations; revisit when we're actually burned.
  • Auto-rollback on health-check failure — dangerous without operator context.

Design notes

What to snapshot: only `state.db` + `config.yaml`. Parquet rolloffs are append-only (safe to keep). Lua drivers are checked-in or volume-mounted (not part of state).

Retention: last 5 snapshots. ~10-50 MB each on a warm system.

Rollback flavours:

  • Soft: switch image tag, keep current state. Works when forward-compat holds.
  • Hard: switch image tag + restore snapshot. For when state schema broke.

UI picks via a radio, defaults to soft.

PR sequence

  1. `feat(selfupdate): snapshot before update + rollback by tag` (Phase 1)
  2. `feat(selfupdate): post-update health check banner` (Phase 2)
  3. `feat(selfupdate): update history view` (Phase 3)
  4. `feat(selfupdate): highlight breaking changes in modal` (Phase 4)

Each PR links back here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions