From 666af41f46fb73ed93229ab11cf54399e089c8a9 Mon Sep 17 00:00:00 2001 From: Steve Cliff Date: Sat, 2 May 2026 20:54:14 +0100 Subject: [PATCH] design: v4 wireframes for P2 redesign (sources / schedules / repo) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hi-fi mock of the four pages affected by the redesign: * /hosts/{id}/sources — list of source groups with per-row meta line (includes/excludes count, retention summary, usage, snapshot count) and Run-now / Edit / Delete actions. Tweaks toggle flips between fresh-host (default empty group, Run-now + Delete disabled) and multi-group states. * /hosts/{id}/sources/{gid}/edit — name (snapshot tag), includes/ excludes textareas, retention as a 3×2 grid of keep-* cells, retry-on-offline, inline conflict banner above retention when granularity↔cadence mismatch detected. * /hosts/{id}/schedules — slim list (status / cron / source-tags / actions) plus new-schedule form (cron with quick-pick chips, source-group multi-select via clickable check pickers, enabled toggle). * /hosts/{id}/repo — connection (URL/user/password/cert pin), bandwidth caps, maintenance rows (forget daily / prune weekly / check monthly with 5% subset), danger zone re-init. Footer carries the retention-conflict detection spec (granularity vs cadence mismatch). Visual language matches v1: --accent cyan, JetBrains Mono for IDs/cron, btn tokens, sub-tab nav, hairline panels. Co-Authored-By: Claude Opus 4.7 (1M context) --- design/v4-sources-redesign.html | 933 ++++++++++++++++++++++++++++++++ 1 file changed, 933 insertions(+) create mode 100644 design/v4-sources-redesign.html diff --git a/design/v4-sources-redesign.html b/design/v4-sources-redesign.html new file mode 100644 index 0000000..bffeeb8 --- /dev/null +++ b/design/v4-sources-redesign.html @@ -0,0 +1,933 @@ + + + + +restic-manager · v4 · Sources / Schedules / Repo redesign + + + + + + + + +
+ +
+
v4 · Sources / Schedules / Repo redesign
+

Schedules say when. Sources say what. Repo says itself.

+

+ Phase 2's first cut surfaced every restic verb as a schedule kind, and made the operator pick paths twice — once on the host, once on the schedule. That's how restic CLI thinks; it's not how an operator thinks. This redesign collapses the model around three nouns the operator actually has in their head: +

+

+ Source groups own the "what" — a named bundle of include/exclude paths plus a retention policy. Default group default is created at host enrolment, ready to fill in. Schedules own the "when" — a cron expression pointing at one or more groups. Cron fires → one restic backup per group, each tagged with the group name so retention works cleanly. Repo maintenance (forget / prune / check) lives on the host detail's Repo tab with sensible default cadences — operators don't compose those by hand. +

+

+ Pages mocked: /hosts/dev/sources · /hosts/dev/sources/<id>/edit · /hosts/dev/schedules · /hosts/dev/repo. Run-now lives on individual source-group rows; manual-schedule kind is gone. The Sources stage has a small Tweaks toggle to flip between fresh-host and multi-group states. +

+
+ + +
+
Stage 1/hosts/dev/sources · default group present (fresh-ish host)
+ +
+ Tweaks + · + default only + multi-group +
+ +
+ +
+
+
restic-manager
+
v0.2.0-alpha
+
+
+
steve@dcglab
+ +
+
+
+ +
+ + +
+
Dashboard/dev/sources
+
+
+
+ +

dev

+
homelab
+
+
+ linux/amd64 + · + online · last heartbeat 3s ago +
+
+ +
+ + +
+
Snapshots 14
+
Sources 3
+
Schedules 2
+
Repo
+
Jobs
+
Settings
+
+
+ + +
+ +

+ Each source group is a named bundle of paths plus the rule for how long its snapshots stick around. Schedules point at one or more groups — one restic backup runs per group, tagged by name so forget can apply retention cleanly. +

+ + +
+ +
+
+
+ default +
+
+ 2 includes · 1 exclude · keep last 7, daily 14, weekly 4 +
+
+ used by 1 schedule · last run succeeded · 12m ago · 52 snapshots +
+
+
+ + + +
+
+ + +
+
+
+ databases +
+
+ 2 includes · 1 exclude · keep last 7, hourly 24, daily 14, weekly 6, monthly 6 +
+
+ used by 2 schedules · last run succeeded · 2h ago · 68 snapshots +
+
+
+ + + +
+
+ + +
+
+
+ photos + keep-hourly · no sub-daily schedule +
+
+ 1 include · 0 excludes · keep hourly 24, monthly 24 +
+
+ used by 1 schedule · last run succeeded · 6h ago · 7 snapshots +
+
+
+ + + +
+
+
+ + + + + +
+ default can't be deleted while it's the only group on the host. + · + Run-now on a row dispatches one immediate backup using that group's paths and tag. +
+
+
+
+ + +
+
Stage 2/hosts/dev/sources/01KQ.../edit · editing databases
+
+ +
+
restic-manager
+
steve@dcglab
+
+ +
+
+ Dashboard/dev/sources/databases +
+ +

Edit source group · databases

+

+ What this group covers and how long its snapshots are worth keeping. Snapshots produced for this group carry the tag databases — change the name with care: existing snapshots keep the old tag and won't get retained by a renamed group's policy. +

+ +
+ +
+ +

Identity

+
+ + +
Used as the snapshot tag. Lowercase, no spaces; matches what restic forget --tag sees.
+
+ +

Paths

+
+ + +
What restic backup walks. Agent runs as root with CAP_DAC_READ_SEARCH, so any readable path is fair game.
+
+
+ + +
Passed straight through as --exclude args.
+
+ +

Retention applied nightly · all blank = keep everything

+ + +
+
+
+ keep-hourly is set, but no schedule pointing at this group fires more often than once a day. + The hourly bucket will never have snapshots to retain — restic forget treats the value as a no-op. + Either drop keep-hourly or add a sub-daily schedule. + Finest schedule interval: 24h · keep-hourly requires < 1h. +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ Translates to restic forget --tag databases --keep-last 7 --keep-hourly 24 --keep-daily 14 --keep-weekly 6 --keep-monthly 6. Forget runs nightly per host (cadence on the Repo tab); pruning the freed data is admin-only and weekly. +
+ +

Retry on offline cron-fired runs only

+
+
+ + +
+
+ + +
+
+
+ Each retry doubles the wait. Manual run-now ignores this — it just fails immediately if the agent is offline, since the operator's eyes are on the page. +
+ +
+ + + +
+
+ + + +
+
+
+
+ + +
+
Stage 3/hosts/dev/schedules · simplified — when only
+
+ +
+
restic-manager
+
steve@dcglab
+
+ +
+
Dashboard/dev/schedules
+
+
+
+ +

dev

+ schedule version 3 · agent in sync +
+
+ +
+
+
Snapshots
+
Sources
+
Schedules 3
+
Repo
+
Jobs
+
Settings
+
+
+ +
+

+ A schedule is just a cron expression pointing at one or more source groups. When it fires, the agent runs a separate restic backup per chosen group — independent jobs, independent snapshots, independent retention. Failure of one group doesn't fail the others. +

+ +
+
+
Status
+
Cron
+
Sources
+
+
+ + +
+
enabled
+
@hourly
+
+ databases +
+
+ + + +
+
+ + +
+
enabled
+
0 3 * * *
+
+ default + photos + databases +
+
+ + + +
+
+ + +
+
paused
+
0 3 * * 0
+
+ photos +
+
+ + +
+
+
+
+ + +
+
— New schedule form —
+ +
+
+ +
+

When

+ + +
+ 0 3 * * * + @hourly + 0 */6 * * * + 0 3 * * 0 + 0 3 1 * * +
+
+ Standard 5-field cron with descriptors. Server validates with the same parser the agent uses to fire — what saves here is what runs. +
+ +

What — pick one or more source groups

+
+
default2 paths · keep last 7, daily 14
+
databases2 paths · keep last 7, daily 14, weekly 6
+
photos1 path · keep monthly 24
+
+
+ Each picked group runs as a separate restic backup with its own tag — its own snapshot, its own retention. Pick multiple to fire them all on the same cron tick. +
+ +

Status

+ + +
+ + +
+
+ + + +
+
+
+
+
+ + +
+
Stage 4/hosts/dev/repo · connection · maintenance · danger zone
+
+ +
+
restic-manager
+
steve@dcglab
+
+ +
+ +
+
+
+ +

dev

+
+
+
+
+
Snapshots
+
Sources
+
Schedules
+
Repo
+
Jobs
+
Settings
+
+
+ +
+ +
+ +

Connection

+
+
+
+
Repo URL
+
rest:http://192.168.0.99:8000/dev/
+
+
+
Username
+
dev
+
+
+
Password
+
•••••••••••••••• stored, never displayed
+
+
+
Cert pin
+
HTTP-only behind reverse proxy
+
+
+
+ + +
+
+ + +

Bandwidth · host-wide

+
+
+
+ + +
+
+ + +
+
+
+ Applies to every backup, restore, and prune job for this host. Configure per-host because the network constraint usually is the host (its uplink). Per-source is rare enough we don't surface it. +
+
+ + +

Maintenance · auto-managed

+
+ +
+
forget
+
+
Cadence
+
daily · 03:00
+
+
+
Last run
+
succeeded · 12h ago
+
+
+ Per source group, using each group's retention policy. +
+
+ enabled + +
+
+ +
+
prune
+
+
Cadence
+
weekly · Sun 04:00
+
+
+
Last run
+
succeeded · 4d ago
+
+
+ Reclaims storage made dead by forget. Heavy — runs weekly only. +
+
+ enabled + +
+
+ +
+
check
+
+
Cadence
+
monthly · 1st 05:00
+
+
+
Last run
+
succeeded · 22d ago
+
+
+ --read-data-subset 5% · spreads full coverage over ~20 months. +
+
+ enabled + +
+
+
+ + +

Danger zone

+
+
+
+
Re-initialise repo
+

+ Tries to DELETE the rest-server's copy of this repo, then runs restic init against the empty path. Most rest-server setups run with --append-only and refuse the DELETE — in that case the page shows guided cleanup steps instead of attempting anything destructive. +

+

+ All snapshots are lost; this host's schedule version stays the same and the agent's secrets.enc is reused. +

+
+ +
+
+
+ + + + +
+
+
+ + +
+
Retention-conflict detection · spec
+

+ One server-side check: granularity ↔ cadence mismatch. A retention dimension is "orphaned" when the finest schedule interval pointing at the group can't produce snapshots in that bucket. +

+

Detection (per source group):

+
    +
  1. Collect every enabled schedule on this host whose source-group set includes this group.
  2. +
  3. Parse each schedule's cron via robfig/cron/v3. Compute the smallest interval between consecutive fires.
  4. +
  5. Take the minimum across all schedules — call it finestInterval. If there are zero enabled schedules, finestInterval = ∞ (the group has no fires; conflict surfaces as "0 schedules" in the meta line, not as a pill).
  6. +
  7. For each non-nil keep-* dimension on the group's retention, compare: +
      +
    • keep-hourly requires finestInterval < 1h
    • +
    • keep-daily requires finestInterval < 24h
    • +
    • keep-weekly requires finestInterval < 7d
    • +
    • keep-monthly requires finestInterval < 31d
    • +
    • keep-yearly requires finestInterval < 365d
    • +
    • keep-last always satisfied — it counts snapshots, no time semantics.
    • +
    +
  8. +
  9. Any failed comparison → group has a conflict; the worst (finest-grained) failed dimension drives the pill text.
  10. +
+

+ Re-evaluate on every schedule CRUD and every source-group CRUD; cache the result on the group row (conflict_dimension nullable string) so the list view doesn't recompute on each render. +

+ +
+ +
v4 · open review questions
+
    +
  • Source-edit "How this fits" right rail — keep it as inline education, or trim once the model is familiar?
  • +
  • Schedules form picker — checkbox list of groups (current) vs a chips-based multi-select. Checkboxes scale fine to ≤10 groups.
  • +
  • Repo tab maintenance row — 5-column grid, fine on desktop, will need stacking ≤ ~960 px.
  • +
  • Re-init copy: trailing ellipsis on the button to telegraph the confirmation modal.
  • +
+
+
+ + + + + +