From 136e1a1d8f089d143e3485f39482969affd51998 Mon Sep 17 00:00:00 2001 From: Steve Cliff Date: Fri, 1 May 2026 19:05:39 +0100 Subject: [PATCH] design: extend v1 to login / add-host / host-detail / job-log + lock components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Five hi-fi screens completing the Phase 1 surface, all in v1's dark operator-console register. v1-login Sparse centred card. Sign-in + first-error variant. No marketing chrome; build version sits in footer so a returning operator can spot agent drift. v1-add-host Focused two-column page (form left, contextual "what happens next" right) — not a modal. Two states: form (state A) and minted-token result with install command (state B). Backed by POST /api/enrollment-tokens (P1-32). v1-host-detail Persistent header (status dot, mono name, tags, primary CTAs, vitals strip) over four sub-tabs (Snapshots / Jobs / Repo / Settings). Snapshots is the default — the thing 90% of operators want when they click a host name. Right rail holds Recent activity, run-now stack, and a danger-zone panel. v1-job-log WS-streamed log view. Three states: running (live progress bar + auto-scroll cursor), succeeded (summary stats + final lines), failed (error panel + tail). Backed by WS /api/jobs/{id}/stream (P1-21 remainder). v1-components The load-bearing reference. 14 sections covering tokens (colour + type scale), status, buttons, form fields, tags, tabs, host row, log viewer, progress bar, stat tile, modal, toast, install snippet, empty-state pattern. Every CSS class is real and copy-able into the Go template build. This locks the visual register before P1-23 onwards. Each Phase 1 template gets a {{define}} matching a section in v1-components. Co-Authored-By: Claude Opus 4.7 (1M context) --- design/v1-add-host.html | 347 ++++++++++++++++++ design/v1-components.html | 703 +++++++++++++++++++++++++++++++++++++ design/v1-host-detail.html | 384 ++++++++++++++++++++ design/v1-job-log.html | 413 ++++++++++++++++++++++ design/v1-login.html | 148 ++++++++ 5 files changed, 1995 insertions(+) create mode 100644 design/v1-add-host.html create mode 100644 design/v1-components.html create mode 100644 design/v1-host-detail.html create mode 100644 design/v1-job-log.html create mode 100644 design/v1-login.html diff --git a/design/v1-add-host.html b/design/v1-add-host.html new file mode 100644 index 0000000..6b4b278 --- /dev/null +++ b/design/v1-add-host.html @@ -0,0 +1,347 @@ + + + + +restic-manager · v1 Add host + + + + + + + + +
+ +
+
v1 · Add host
+

Add a host.

+

+ A focused two-column page, not a modal: the form lives where the cursor + needs it, the contextual help and security footnote live where the eye + naturally drifts. After the form is submitted the same URL renders the + result state — token + install command — so the operator never loses + their place. +

+

+ Backed by POST /api/enrollment-tokens + (P1-32). Repo creds become an AEAD blob bound to the token hash; + ConsumeEnrollmentToken rebinds them + under the new host_id and the WS push lands them on the agent. +

+
+ + +
State A · form
+
+
+ + +
+
+
+
restic-manager
+
v0.1.0-alpha
+
+
+
steve@dcglab
+ +
+
+
+
+
+ +
+
+ + +
+
Dashboard/Add host
+

Add a host

+

+ Mints a one-time enrolment token (TTL 1 hour) and binds the repo credentials to it. + The token can only be used once — generate a fresh one if it expires or you typed + something wrong. +

+
+ + +
+ + +
+ +

Host

+
+ + +
Becomes the host’s display name. Most operators use the box’s actual hostname so logs line up.
+
+ +
+ +
+ prod × + cache × + +
+
Free-form. Used for filtering and grouping on the dashboard.
+
+ +

Restic repository

+ +
+ +
+ + repo: +
+
Whatever restic -r would accept. Most fleets terminate at a restic/rest-server; s3: and b2: URLs work equally well.
+
+ +
+ + +
For rest-server with htpasswd, this is the per-host user.
+
+ +
+ + +
Encrypted at rest using the server’s AEAD key. Pushed to the agent only over the authenticated WebSocket.
+
+ +
+ + +
+
+ + + + +
+ +
+
+ + +
State B · token minted, install command shown
+
+
+ + +
+
+
+
restic-manager
+
v0.1.0-alpha
+
+
+
steve@dcglab
+ +
+
+
+
+
+ +
+
+ +
+
Dashboard/Add host/prod-redis-01
+
+

Token minted

+ expires in 59m 54s +
+

+ Run the snippet below on the target box. The host will appear on the + dashboard within a few seconds of the agent connecting. +

+
+ +
+ + +
+
+
Install command · paste-and-run
+
+ + +
+
+
curl -fsSL https://restic.lab.example/install.sh | sudo \
+  RM_SERVER=https://restic.lab.example \
+  RM_TOKEN=HdqFbQh8U-I1fb52iP1M8qxvoYS5t9VZ-T-yghr_CzA sh
+
+ + +
+ +
+
Awaiting agent connection
+
+ + prod-redis-01 + — enrolment will mark this online +
+
+
11:42:18.221 server token minted · 1h ttl
+
awaiting POST /api/agents/enroll …
+
+
+ + + +
+ +
+ +
+
+ +
+ + + + + diff --git a/design/v1-components.html b/design/v1-components.html new file mode 100644 index 0000000..3cd0fe4 --- /dev/null +++ b/design/v1-components.html @@ -0,0 +1,703 @@ + + + + +restic-manager · v1 Components + + + + + + + + +
+ + +
+
v1 · Component reference
+

The system, in one place.

+

+ Every reusable piece across the v1 mockups, lifted out and shown beside its + states. If something appears in a screen but not here, it shouldn’t — it’s + either drift or a candidate for promotion. The Go templates (P1-23 + onwards) lean on this file: a partial gets a name in here before it gets + a {{define}} in the templates. +

+

+ Every CSS class on this page is real and copy-able into the Tailwind + build. Anything inline is a one-off and shouldn’t be. +

+
+ + +
+

01Tokenscolours · type · spacing · the alphabet of v1

+ + +
+
Surface, line, ink
+
+
bg
--bg
+
panel
--panel
+
panel-hi
--panel-hi
+
line
--line
+
ink
--ink
+
ink-mid
--ink-mid
+
ink-mute
--ink-mute
+
ink-fade
--ink-fade
+
+
+ + +
+
State + accent
+
+
ok
--ok · success / online
+
warn
--warn · degraded / cache-warning
+
bad
--bad · failed / alert
+
off
--off · offline (neutral)
+
accent
--accent · running, links
+
+
+ + +
+
Type scale
+
+
+ 28 / 500 + Hero / numeric stat + stat tile values +
+
+ 22 / 500 + Page title + h1 in body +
+
+ 18 / 500 + Section header + h2 above panels +
+
+ 13 / 400 + Body & table cells + default for prose +
+
+ 12 / 400 + Helper, captions, secondary detail + .field-help, meta lines +
+
+ 11 / 600 · 0.08em + Section heading · column header + all caps tracking +
+
+
+
+ + +
+

02Statusfive states · only place colour is used as semantic

+
+
+ + online + heartbeat received within last 90 seconds + .dot.dot-online +
+
+ + online · running + a job is in flight on this host — pulse only when active + .dot.dot-online.pulse +
+
+ + degraded + online, but open alerts > 0 — soft amber, not loud + .dot.dot-degraded +
+
+ + offline + no heartbeat for > 90 seconds — neutral, not alarming + .dot.dot-offline +
+
+ + last job failed + distinct from offline — host is up, but its last job did not succeed + .dot.dot-failed +
+
+
+ + +
+

03Buttonsone primary per page · everything else is the neutral secondary

+
+
+ + + + + + +
+
+
Primary · the one verb the page is about. Use at most once per page.
+
Secondary · everything else. Run-now per row, all panel actions.
+
Ghost · inline / “View →” affordances inside cards and rows.
+
Danger · destructive verbs only. Always pair with a confirmation modal for irreversible ones.
+
Disabled · 0.4 opacity, not-allowed cursor, no hover.
+
Sizes · default 12px. .btn-lg for the page-level primary (slightly bigger affordance).
+
+
+
+ + +
+

04Form fieldslabels above · helper below · 0.005em letter-spacing

+
+ +
+
+ + +
Helper text. Plain prose.
+
+
+ + +
Use .field.mono for URLs, IDs, anything machine-shaped.
+
+
+ +
+ + repo: +
+
+
+ + +
+
+ +
+
+ + +
Border becomes accent on focus.
+
+
+ + +
repo_url must look like rest: / s3: / b2:
+
+
+ +
+ prod × + cache × + +
+
Free-form tags. Comma or Enter to commit.
+
+
+ +
+
+ + +
+

05Tags & chipslabels for hosts · status pills · removable in form

+
+
+ prod + db + homelab + storage + edge + test + prod × +
+
+ succeeded + running + failed + cancelled + expires in 59m +
+
+
+ + +
+

06Tabsprimary nav (accent underline) · sub-nav (ink underline)

+
+ +
+
+ +
+
+ + +
+

07Host rowthe dashboard's load-bearing row · degraded/failed/offline get a left edge

+
+
+
+
Host
+
OS · arch
+
Last backup
+
Repo size
+
Snapshots
+
Alerts
+
Tags
+
+
+
+
+
healthy-host
+
linux/amd64
+
succeeded · 5m ago
+
87 GB
+
2,103
+
+
prod
+
+
+
+
+
degraded-host
+
linux/amd64
+
succeeded · 1h ago
+
128 GB
+
1,402
+
3
+
prod
+
+
+
+
+
failed-host
+
linux/amd64
+
failed · 47m ago
+
97 GB
+
2,847
+
1
+
ci
+
+
+
+
+
offline-host
+
linux/amd64
+
last seen 2d ago
+
64 GB
+
127
+
1
+
dev
+
offline
+
+
+
+ + +
+

08Log viewerlive or replayed · ts · stream · payload · color reserved for events & stderr

+
+
11:43:21.039EVENT{"message_type":"status","percent_done":0.000,"total_files":21402,"files_done":0}
+
11:43:21.412OUTscan finished in 0.371s
+
11:43:25.812ERRwarn: file changed during read: /var/lib/postgresql/13/main/pg_wal/000000010000007800000042
+
11:43:31.625EVENT{"message_type":"status","percent_done":0.380,"files_done":8124,"bytes_done":1503870976}
+
11:43:32.122···
+
+
+ + +
+

09Progress barrunning · complete · failed

+
+
+
running · 38%42 MB/s · ETA 2m 14s
+
+
+
+
complete · 100%finished in 5m 21s
+
+
+
+
failed · 0%repo locked
+
+
+
+
+ + +
+

10Stat tileused in summary strips · fleet, host header, job summary

+
+
+
+
Hosts
+
12 total
+
10 online · 1 degraded
+
+
+
Backed up
+
4.9 TB
+
23,649 snapshots
+
+
+
Last 24h
+
147 jobs
+
144 ok · 2 failed
+
+
+
Open alerts
+
5
+
oldest 3h
+
+
+
+
+ + +
+

11Modalused for confirms · destructive actions · short prompts

+ +
+ + +
+

12Toastsuccess and error variants · auto-dismiss after 4s

+
+
+ +
+
Token minted
+
install command shown below — expires in 1h
+
+ × +
+
+ +
+
Couldn’t reach the agent
+
prod-cache-01 didn’t respond within 5s. Try again or check the WS connection.
+
+ × +
+
+
+ + +
+

13Install snippetload-bearing affordance on Add host · Empty state · Settings → Agents

+
+
+ install command · 59m left +
+ + +
+
+
curl -fsSL https://restic.lab.example/install.sh | sudo \
+  RM_SERVER=https://restic.lab.example \
+  RM_TOKEN=HdqFbQh8U-I1fb52iP1M8qxvoYS5t9VZ-T-yghr_CzA sh
+
+
+ + +
+

14Empty-state patternnothing-yet screens · centred prompt + the affordance that fixes it

+
+

Nothing here yet.

+

+ Empty states always pair a one-sentence explanation with a single + primary affordance. Don’t fill the space with stats or graphics — + the void is the message. +

+ +
+
+ + +
+ end of v1 component reference · 14 sections · ~all the pieces the Phase 1 templates need +
+ +
+ + + diff --git a/design/v1-host-detail.html b/design/v1-host-detail.html new file mode 100644 index 0000000..f508ad7 --- /dev/null +++ b/design/v1-host-detail.html @@ -0,0 +1,384 @@ + + + + +restic-manager · v1 Host detail + + + + + + + + +
+ +
+
v1 · Host detail
+

One host, every angle.

+

+ Reached by clicking any host name on the dashboard. Persistent header + carries the host’s identity and key vitals; below, four tabs + (Snapshots / Jobs / Repo / Settings) pivot the rest of the page + without losing context. Snapshots is the default — it’s the + thing 90% of operators want to see when they click a host name. +

+

+ The right-rail action stack stays present across all four tabs so + “Run backup now” and “Edit credentials” are always one click away. The + snapshot rows themselves are clickable — they lead to the restore wizard + (P3-01) when that lands. +

+
+ +
+
+ + +
+
+
+
restic-manager
+
v0.1.0-alpha
+
+
+
steve@dcglab
+ +
+
+
+
+
+ +
+
+ + +
+
Dashboard/prod-db-01
+ +
+
+
+ +

prod-db-01

+
proddb
+
+
+ linux/amd64 + · + agent v0.1.0 + · + restic 0.17.3 + · + last seen 3s ago +
+
+
+ + + +
+
+ + +
+
+
Last backup
+
succeeded · 3m ago
+
1.4 GB transferred · 38s
+
+
+
Repo size
+
412 GB
+
dedup ratio 6.4×
+
+
+
Snapshots
+
1,847
+
oldest 18 months ago
+
+
+
Repo health
+
unlocked · ok
+
last restic check 4d ago
+
+
+ + +
+
Snapshots 1,847
+
Jobs 47 in 24h
+
Repo
+
Settings
+
+
+ + +
+ + +
+
+
+

Snapshots

+
showing 12 of 1,847
+
+
+ + + +
+
+ +
+
+
Snapshot id
+
Time
+
Paths
+
Size
+
Files
+
+
+ + +
+
91bbc80d
+
2026-05-01 11:43:21
+
/var/lib/postgresql · /etc/postgresql
+
1.4 GB
+
12,418
+
+
+
+
7a3c1f88
+
2026-05-01 11:13:09
+
/var/lib/postgresql · /etc/postgresql
+
1.4 GB
+
12,417
+
+
+
+
f50e2bbc
+
2026-05-01 10:43:02
+
/var/lib/postgresql · /etc/postgresql
+
1.4 GB
+
12,415
+
+
+
+
2d916ae4
+
2026-05-01 10:12:47
+
/var/lib/postgresql · /etc/postgresql
+
1.4 GB
+
12,415
+
+
+
+
b0c4e1f2
+
2026-05-01 09:42:18
+
/var/lib/postgresql · /etc/postgresql
+
1.4 GB
+
12,414
+
+
+
+
a8801c3f
+
2026-05-01 09:11:55
+
/var/lib/postgresql · /etc/postgresql
+
1.4 GB
+
12,414
+
+
+
+
e91f4d72
+
2026-05-01 08:42:01
+
/var/lib/postgresql · /etc/postgresql
+
1.4 GB
+
12,412
+
+
+
+
3d44a9e8
+
2026-05-01 08:11:33
+
/var/lib/postgresql · /etc/postgresql
+
1.4 GB
+
12,412
+
+
+
+
4f8c0c11
+
2026-05-01 07:42:08
+
/var/lib/postgresql · /etc/postgresql
+
1.4 GB
+
12,410
+
+
+
+
c113c3d2
+
2026-05-01 07:11:42
+
/var/lib/postgresql · /etc/postgresql
+
1.4 GB
+
12,410
+
+
+
+
9be1aa0d
+
2026-05-01 06:42:11
+
/var/lib/postgresql · /etc/postgresql
+
1.4 GB
+
12,408
+
+
+
+
2eaf9c50
+
2026-05-01 06:12:49
+
/var/lib/postgresql · /etc/postgresql
+
1.4 GB
+
12,408
+
+
+
+ + +
+ showing snapshots 1–12 of 1,847 +
+ + +
+
+
+ + + + +
+ +
+
+ +
+ + + diff --git a/design/v1-job-log.html b/design/v1-job-log.html new file mode 100644 index 0000000..eed8c9c --- /dev/null +++ b/design/v1-job-log.html @@ -0,0 +1,413 @@ + + + + +restic-manager · v1 Job log + + + + + + + + +
+ +
+
v1 · Live job log
+

Watching restic chug.

+

+ The screen an operator stares at when something is in flight. Header + identifies the job (kind · host · status). A live progress bar shows the + bytes-to-go signal restic emits. The log itself is the focus: monospace, + tight line-height, color reserved for the few lines that matter — events + and stderr. +

+

+ Backed by WS /api/jobs/{id}/stream + (P1-21 remainder). Auto-scrolls until the operator scrolls away; a + “follow” pill appears when they’ve scrolled up so they can re-attach. +

+
+ + +
State A · running
+
+
+ + +
+
+
+
restic-manager
+
v0.1.0-alpha
+
+
+
steve@dcglab
+ +
+
+
+
+
+ +
+
+ + +
+
Dashboard/prod-db-01/job 01KQH…E59B
+ +
+
+
+ +

backup · prod-db-01

+ running +
+
+ job 01KQH7DZJ8M5N3DH277E59B + · + started 3m 18s ago by steve@dcglab +
+
+
+ + +
+
+ + +
+
+
+ 38% + · + 1.4 GB of 3.7 GB + · + 8,124 files + of 21,402 +
+
+ 42 MB/s · ETA 2m 14s +
+
+
+
+
+
+
+ + +
+
+
+

Stream

+ following · auto-scroll on · 1,247 lines +
+
+ + + +
+
+ +
+
11:43:21.039EVENT{"message_type":"status","percent_done":0.000,"total_files":21402,"files_done":0,"total_bytes":3958374400,"bytes_done":0}
+
11:43:21.412OUTscan finished in 0.371s
+
11:43:21.504EVENT{"message_type":"status","percent_done":0.001,"total_files":21402,"files_done":12,"bytes_done":1048576}
+
11:43:22.512EVENT{"message_type":"status","percent_done":0.020,"files_done":418,"bytes_done":81256000}
+
11:43:23.521EVENT{"message_type":"status","percent_done":0.062,"files_done":1287,"bytes_done":253640000}
+
11:43:24.530EVENT{"message_type":"status","percent_done":0.108,"files_done":2289,"bytes_done":444121600}
+
11:43:25.541EVENT{"message_type":"status","percent_done":0.155,"files_done":3267,"bytes_done":637534720}
+
11:43:25.812ERRwarn: file changed during read: /var/lib/postgresql/13/main/pg_wal/000000010000007800000042
+
11:43:26.554EVENT{"message_type":"status","percent_done":0.198,"files_done":4187,"bytes_done":815874048}
+
11:43:27.566EVENT{"message_type":"status","percent_done":0.241,"files_done":5108,"bytes_done":993951744}
+
11:43:28.580EVENT{"message_type":"status","percent_done":0.284,"files_done":6029,"bytes_done":1172029440}
+
11:43:29.594EVENT{"message_type":"status","percent_done":0.327,"files_done":6948,"bytes_done":1349838336}
+
11:43:30.609EVENT{"message_type":"status","percent_done":0.358,"files_done":7596,"bytes_done":1475174400}
+
11:43:31.625EVENT{"message_type":"status","percent_done":0.380,"files_done":8124,"bytes_done":1503870976}
+
11:43:32.122···
+
+
+ +
+
+ + +
State B · completed (succeeded)
+
+
+ +
+
+
+
restic-manager
+
v0.1.0-alpha
+
+
+
steve@dcglab
+ +
+
+
+
+
+ +
+
+ +
+
Dashboard/prod-db-01/job 01KQH…E59B
+
+
+
+ +

backup · prod-db-01

+ succeeded +
+
+ job 01KQH7DZJ8M5N3DH277E59B + · + finished 11:48:42 (5m 21s) +
+
+
+ + +
+
+ + +
+
+
Bytes added
+
142 MB
+
of 3.7 GB processed
+
+
+
Files
+
21,402
+
187 new · 42 changed
+
+
+
Throughput
+
42 MB/s
+
avg over 5m 21s
+
+
+
Snapshot
+
91bbc80d
+
added to repo
+
+
+
+ + +
+
+
+

Stream · complete

+ 2,418 lines +
+
+ + +
+
+ +
+
11:48:38.044EVENT{"message_type":"status","percent_done":0.987,"files_done":21127,"bytes_done":3905990656}
+
11:48:39.058EVENT{"message_type":"status","percent_done":0.998,"files_done":21358,"bytes_done":3950182400}
+
11:48:40.064OUTprocessed 21402 files, 3.689 GiB in 5:19
+
11:48:41.022OUTsnapshot 91bbc80d saved
+
11:48:42.108EVENT{"message_type":"summary","files_new":187,"files_changed":42,"files_unmodified":21173,"data_added":148908544,"total_files_processed":21402,"total_bytes_processed":3958374400,"snapshot_id":"91bbc80d4a17ed718462a26f3e6ad72d0cde7aa9fbf0629efaac1eaa943f5665","total_duration":319.234}
+
11:48:42.337ENDrestic exited 0 · success
+
+
+ +
+
+ + +
State C · failed
+
+
+ +
+
+
+
restic-manager
+
v0.1.0-alpha
+
+
+
steve@dcglab
+ +
+
+
+
+
+ +
+
+ +
+
Dashboard/build-runner/job 01KQH…9F8C
+ +
+
+
+ +

backup · build-runner

+ failed +
+
+ job 01KQH3FX9F8C… + · + finished 10:53:18 (1.4s) + · + exit code 1 +
+
+
+ + +
+
+ + +
+
Failure
+

+ unable to acquire lock — the repo at rest:https://restic.unraid.lab/build-runner/ is locked by another operation. +

+
+ Most likely a stale lock from a previous run that didn't clean up. Run unlock on this host's repo, then retry the backup. +
+
+
+ +
+
+
10:53:17.001OUTopening repository at rest:https://restic.unraid.lab/build-runner/
+
10:53:17.388OUTrepository f4b81ec7 opened
+
10:53:17.602OUTcreated new cache in /var/lib/restic-manager/cache
+
10:53:18.211ERRFatal: unable to create lock in backend: repository is already locked exclusively by PID 12047 on build-runner by root (UID 0, GID 0) lock was created at 2026-05-01 10:39:14 (14m18s ago) storage ID a4f1b3d8
+
10:53:18.298ENDrestic exited 1 · failed
+
+
+ +
+
+ +
+ + + + + diff --git a/design/v1-login.html b/design/v1-login.html new file mode 100644 index 0000000..da6e3f1 --- /dev/null +++ b/design/v1-login.html @@ -0,0 +1,148 @@ + + + + +restic-manager · v1 Login + + + + + + + +
+ +
+
v1 · Login
+

Sign in.

+

+ The first chrome an operator meets. Everything irrelevant to the act of + signing in is removed: no marketing, no “sign up”, no animated background. + The form lives where the cursor needs it; the build version sits in the + footer so a returning operator can spot a mismatch with the agents. +

+

+ First-run operators land on the empty /bootstrap + page instead — see the dedicated mockup. A regular sign-in always has at + least one user already minted. +

+
+ +
+
+ + +
+ + +
+
restic-manager
+
+ +

Sign in to continue

+ +
+
+ + +
+
+ + +
+ + +
+ +
+

+ Forgot your password? An admin can reset it from + Settings → Users. + There’s no recovery email — this is self-hosted infrastructure. +

+
+
+ + +
+ restic-manager v0.1.0-alpha + · + build f9c2351 + · + docs + · + source +
+ +
+
+ + +
+

Error variant

+
+
+

Sign in to continue

+
+ Invalid username or password. +
+
+ + +
+
+ + +
+ +
+
+
+ +
+ +