design: extend v1 to login / add-host / host-detail / job-log + lock components

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) <noreply@anthropic.com>
This commit is contained in:
2026-05-01 19:05:39 +01:00
parent cca525a04d
commit 8b7b1479a1
5 changed files with 1995 additions and 0 deletions
+413
View File
@@ -0,0 +1,413 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>restic-manager · v1 Job log</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
:root {
--bg: oklch(0.17 0.006 250); --panel: oklch(0.20 0.007 250); --panel-hi: oklch(0.23 0.008 250);
--line: oklch(0.27 0.010 250); --line-soft: oklch(0.23 0.008 250);
--ink: oklch(0.96 0.005 250); --ink-mid: oklch(0.78 0.005 250);
--ink-mute: oklch(0.58 0.006 250); --ink-fade: oklch(0.42 0.006 250);
--ok: oklch(0.78 0.14 155); --warn: oklch(0.82 0.13 80); --bad: oklch(0.70 0.20 25);
--accent: oklch(0.82 0.12 195);
}
html, body { background: var(--bg); color: var(--ink); }
body { font-family: 'Inter', system-ui, sans-serif; }
.mono { font-family: 'JetBrains Mono', monospace; font-variant-numeric: tabular-nums; }
.text-pretty { text-wrap: pretty; }
::selection { background: color-mix(in oklch, var(--accent), transparent 70%); }
.panel { background: var(--panel); border: 1px solid var(--line-soft); }
.hairline { box-shadow: inset 0 -1px 0 var(--line-soft); }
.btn {
font-size: 12px; font-weight: 500; padding: 6px 11px; border-radius: 5px;
background: transparent; border: 1px solid var(--line); color: var(--ink-mid);
transition: all 120ms ease; cursor: pointer;
}
.btn:hover { background: var(--panel-hi); color: var(--ink); }
.btn-primary { color: oklch(0.18 0.01 195); background: var(--accent); border-color: var(--accent); }
.btn-primary:hover { filter: brightness(1.08); }
.btn-ghost { border-color: transparent; }
.btn-ghost:hover { background: var(--panel-hi); border-color: transparent; }
.btn-danger { color: var(--bad); border-color: color-mix(in oklch, var(--bad), transparent 70%); }
.nav-tab { font-size: 13px; padding: 18px 0; color: var(--ink-mute); border-bottom: 2px solid transparent; margin-right: 28px; cursor: pointer; }
.nav-tab.active { color: var(--ink); border-color: var(--accent); }
.doc { max-width: 1280px; margin: 0 auto; padding: 0 32px; }
.philosophy { padding: 56px 0 32px; border-bottom: 1px solid var(--line-soft); }
.philosophy h1 { font-size: 22px; font-weight: 600; letter-spacing: -0.01em; }
.philosophy p { color: var(--ink-mid); max-width: 680px; margin-top: 14px; line-height: 1.65; text-wrap: pretty; }
.philosophy .meta { color: var(--ink-fade); font-size: 12px; margin-top: 14px; }
.stage-frame { margin: 48px -32px; border-top: 1px solid var(--line-soft); border-bottom: 1px solid var(--line-soft); }
.stage-label { padding: 16px 32px 0; font-size: 11px; color: var(--ink-fade); letter-spacing: 0.18em; text-transform: uppercase; }
.crumbs { font-size: 12px; color: var(--ink-mute); }
.crumbs a { color: var(--ink-mute); text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--line); }
.crumbs .sep { color: var(--ink-fade); margin: 0 8px; }
/* progress bar */
.progress-track {
background: var(--bg); border: 1px solid var(--line-soft);
height: 6px; border-radius: 9999px; overflow: hidden;
}
.progress-fill {
height: 100%; background: var(--accent);
border-radius: 9999px;
transition: width 250ms ease;
}
/* log viewer */
.log {
background: var(--bg);
border: 1px solid var(--line-soft);
border-radius: 7px;
font-family: 'JetBrains Mono', monospace;
font-size: 12px; line-height: 1.7;
overflow: hidden;
}
.log-line {
display: grid;
grid-template-columns: 14ch 8ch 1fr;
column-gap: 14px;
padding: 1px 16px;
align-items: baseline;
}
.log-line:first-child { padding-top: 14px; }
.log-line.ts { color: var(--ink-fade); }
.log-stream-stdout { color: var(--ink-mid); }
.log-stream-stderr { color: oklch(0.78 0.13 50); }
.log-stream-event { color: var(--accent); }
.log-stream-tag { font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-fade); }
.log-payload-ok { color: var(--ink); }
.log-payload-warn { color: var(--warn); }
.log-payload-err { color: var(--bad); }
</style>
</head>
<body>
<div class="doc">
<header class="philosophy">
<div class="text-xs uppercase tracking-[0.18em] text-[color:var(--ink-fade)] mb-3">v1 · Live job log</div>
<h1>Watching restic chug.</h1>
<p>
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.
</p>
<p class="meta">
Backed by <span class="mono" style="color: var(--ink-mid);">WS /api/jobs/{id}/stream</span>
(P1-21 remainder). Auto-scrolls until the operator scrolls away; a
“follow” pill appears when theyve scrolled up so they can re-attach.
</p>
</header>
<!-- Stage 1: running -->
<div class="stage-label">State A · running</div>
<div class="stage-frame">
<div style="background: var(--bg);">
<!-- chrome -->
<div class="hairline" style="background: var(--bg);">
<div class="doc flex items-center justify-between" style="padding: 16px 0;">
<div class="flex items-center gap-3">
<div class="mono" style="font-size:13px; color: var(--ink); font-weight:500;">restic-manager</div>
<div class="mono" style="font-size:11px; color: var(--ink-fade);">v0.1.0-alpha</div>
</div>
<div class="flex items-center gap-5">
<div class="mono" style="font-size:12px; color: var(--ink-mute);">steve@dcglab</div>
<button class="btn btn-ghost">Sign out</button>
</div>
</div>
</div>
<div class="hairline" style="background: var(--bg);">
<div class="doc flex items-end">
<nav class="flex items-end">
<div class="nav-tab active">Dashboard</div>
<div class="nav-tab">Repos</div>
<div class="nav-tab">Alerts <span class="mono ml-1.5" style="font-size:11px; color: var(--bad);">5</span></div>
<div class="nav-tab">Audit</div>
<div class="nav-tab">Settings</div>
</nav>
</div>
</div>
<!-- job header -->
<div class="doc" style="padding: 28px 32px 0;">
<div class="crumbs"><a>Dashboard</a><span class="sep">/</span><a>prod-db-01</a><span class="sep">/</span><span style="color: var(--ink-mid);">job 01KQH…E59B</span></div>
<div class="flex items-start justify-between" style="margin-top: 14px;">
<div>
<div class="flex items-center gap-3">
<span style="width: 9px; height: 9px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 4px color-mix(in oklch, var(--accent), transparent 80%); animation: pulse 2.4s ease-in-out infinite;"></span>
<h1 style="font-size: 22px; font-weight: 500; letter-spacing: -0.01em;">backup <span style="color: var(--ink-fade);">·</span> <span class="mono" style="color: var(--ink); font-weight: 500;">prod-db-01</span></h1>
<span class="mono" style="font-size: 11px; padding: 3px 8px; background: color-mix(in oklch, var(--accent), transparent 88%); color: var(--accent); border: 1px solid color-mix(in oklch, var(--accent), transparent 70%); border-radius: 3px;">running</span>
</div>
<div class="flex items-center gap-3" style="margin-top: 10px; font-size: 12.5px; color: var(--ink-mute);">
<span>job <span class="mono" style="color: var(--ink-mid);">01KQH7DZJ8M5N3DH277E59B</span></span>
<span style="color: var(--ink-fade);">·</span>
<span>started <span class="mono" style="color: var(--ink-mid);">3m 18s ago</span> by <span class="mono" style="color: var(--ink-mid);">steve@dcglab</span></span>
</div>
</div>
<div class="flex items-center gap-2">
<button class="btn">Pin to top</button>
<button class="btn btn-danger">Cancel job</button>
</div>
</div>
<!-- progress bar -->
<div style="margin-top: 24px; padding-bottom: 24px;">
<div class="flex items-center justify-between mb-2.5">
<div class="flex items-center gap-3 text-sm">
<span class="mono" style="color: var(--ink); font-weight: 500;">38%</span>
<span style="color: var(--ink-mute);">·</span>
<span class="mono" style="color: var(--ink-mid);">1.4 GB</span> <span style="color: var(--ink-mute);">of <span class="mono" style="color: var(--ink-mid);">3.7 GB</span></span>
<span style="color: var(--ink-mute);">·</span>
<span class="mono" style="color: var(--ink-mid);">8,124 files</span>
<span style="color: var(--ink-mute);">of <span class="mono" style="color: var(--ink-mid);">21,402</span></span>
</div>
<div class="text-sm" style="color: var(--ink-mute);">
<span class="mono" style="color: var(--ink-mid);">42 MB/s</span> · ETA <span class="mono" style="color: var(--ink-mid);">2m 14s</span>
</div>
</div>
<div class="progress-track">
<div class="progress-fill" style="width: 38%;"></div>
</div>
</div>
</div>
<!-- log viewer -->
<div class="doc" style="padding: 0 32px 56px;">
<div class="flex items-center justify-between" style="margin-bottom: 12px;">
<div class="flex items-center gap-3">
<h2 style="font-size: 13px; font-weight: 600; letter-spacing: 0.01em;">Stream</h2>
<span style="font-size: 11.5px; color: var(--ink-fade);">following · auto-scroll on · 1,247 lines</span>
</div>
<div class="flex items-center gap-2">
<button class="btn">Filter ▾</button>
<button class="btn">Pause stream</button>
<button class="btn">Download .log</button>
</div>
</div>
<div class="log">
<div class="log-line"><span class="ts">11:43:21.039</span><span class="log-stream-tag">EVENT</span><span class="log-payload-ok"><span class="log-stream-event">{"message_type":"status","percent_done":0.000,"total_files":21402,"files_done":0,"total_bytes":3958374400,"bytes_done":0}</span></span></div>
<div class="log-line"><span class="ts">11:43:21.412</span><span class="log-stream-tag">OUT</span><span class="log-stream-stdout">scan finished in 0.371s</span></div>
<div class="log-line"><span class="ts">11:43:21.504</span><span class="log-stream-tag">EVENT</span><span class="log-stream-event">{"message_type":"status","percent_done":0.001,"total_files":21402,"files_done":12,"bytes_done":1048576}</span></div>
<div class="log-line"><span class="ts">11:43:22.512</span><span class="log-stream-tag">EVENT</span><span class="log-stream-event">{"message_type":"status","percent_done":0.020,"files_done":418,"bytes_done":81256000}</span></div>
<div class="log-line"><span class="ts">11:43:23.521</span><span class="log-stream-tag">EVENT</span><span class="log-stream-event">{"message_type":"status","percent_done":0.062,"files_done":1287,"bytes_done":253640000}</span></div>
<div class="log-line"><span class="ts">11:43:24.530</span><span class="log-stream-tag">EVENT</span><span class="log-stream-event">{"message_type":"status","percent_done":0.108,"files_done":2289,"bytes_done":444121600}</span></div>
<div class="log-line"><span class="ts">11:43:25.541</span><span class="log-stream-tag">EVENT</span><span class="log-stream-event">{"message_type":"status","percent_done":0.155,"files_done":3267,"bytes_done":637534720}</span></div>
<div class="log-line"><span class="ts">11:43:25.812</span><span class="log-stream-tag">ERR</span><span class="log-stream-stderr"><span class="log-payload-warn">warn: file changed during read: /var/lib/postgresql/13/main/pg_wal/000000010000007800000042</span></span></div>
<div class="log-line"><span class="ts">11:43:26.554</span><span class="log-stream-tag">EVENT</span><span class="log-stream-event">{"message_type":"status","percent_done":0.198,"files_done":4187,"bytes_done":815874048}</span></div>
<div class="log-line"><span class="ts">11:43:27.566</span><span class="log-stream-tag">EVENT</span><span class="log-stream-event">{"message_type":"status","percent_done":0.241,"files_done":5108,"bytes_done":993951744}</span></div>
<div class="log-line"><span class="ts">11:43:28.580</span><span class="log-stream-tag">EVENT</span><span class="log-stream-event">{"message_type":"status","percent_done":0.284,"files_done":6029,"bytes_done":1172029440}</span></div>
<div class="log-line"><span class="ts">11:43:29.594</span><span class="log-stream-tag">EVENT</span><span class="log-stream-event">{"message_type":"status","percent_done":0.327,"files_done":6948,"bytes_done":1349838336}</span></div>
<div class="log-line"><span class="ts">11:43:30.609</span><span class="log-stream-tag">EVENT</span><span class="log-stream-event">{"message_type":"status","percent_done":0.358,"files_done":7596,"bytes_done":1475174400}</span></div>
<div class="log-line"><span class="ts">11:43:31.625</span><span class="log-stream-tag">EVENT</span><span class="log-stream-event">{"message_type":"status","percent_done":0.380,"files_done":8124,"bytes_done":1503870976}</span></div>
<div class="log-line" style="padding-bottom: 14px;"><span class="ts" style="color: var(--accent);">11:43:32.122</span><span class="log-stream-tag" style="color: var(--accent);">···</span><span style="color: var(--accent);"></span></div>
</div>
</div>
</div>
</div>
<!-- Stage 2: completed (success) -->
<div class="stage-label">State B · completed (succeeded)</div>
<div class="stage-frame">
<div style="background: var(--bg);">
<div class="hairline" style="background: var(--bg);">
<div class="doc flex items-center justify-between" style="padding: 16px 0;">
<div class="flex items-center gap-3">
<div class="mono" style="font-size:13px; color: var(--ink); font-weight:500;">restic-manager</div>
<div class="mono" style="font-size:11px; color: var(--ink-fade);">v0.1.0-alpha</div>
</div>
<div class="flex items-center gap-5">
<div class="mono" style="font-size:12px; color: var(--ink-mute);">steve@dcglab</div>
<button class="btn btn-ghost">Sign out</button>
</div>
</div>
</div>
<div class="hairline" style="background: var(--bg);">
<div class="doc flex items-end">
<nav class="flex items-end">
<div class="nav-tab active">Dashboard</div>
<div class="nav-tab">Repos</div>
<div class="nav-tab">Alerts <span class="mono ml-1.5" style="font-size:11px; color: var(--bad);">5</span></div>
<div class="nav-tab">Audit</div>
<div class="nav-tab">Settings</div>
</nav>
</div>
</div>
<div class="doc" style="padding: 28px 32px 0;">
<div class="crumbs"><a>Dashboard</a><span class="sep">/</span><a>prod-db-01</a><span class="sep">/</span><span style="color: var(--ink-mid);">job 01KQH…E59B</span></div>
<div class="flex items-start justify-between" style="margin-top: 14px;">
<div>
<div class="flex items-center gap-3">
<span style="width: 9px; height: 9px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 0 4px color-mix(in oklch, var(--ok), transparent 80%);"></span>
<h1 style="font-size: 22px; font-weight: 500; letter-spacing: -0.01em;">backup <span style="color: var(--ink-fade);">·</span> <span class="mono" style="color: var(--ink); font-weight: 500;">prod-db-01</span></h1>
<span class="mono" style="font-size: 11px; padding: 3px 8px; background: color-mix(in oklch, var(--ok), transparent 88%); color: var(--ok); border: 1px solid color-mix(in oklch, var(--ok), transparent 70%); border-radius: 3px;">succeeded</span>
</div>
<div class="flex items-center gap-3" style="margin-top: 10px; font-size: 12.5px; color: var(--ink-mute);">
<span>job <span class="mono" style="color: var(--ink-mid);">01KQH7DZJ8M5N3DH277E59B</span></span>
<span style="color: var(--ink-fade);">·</span>
<span>finished <span class="mono" style="color: var(--ink-mid);">11:48:42 (5m 21s)</span></span>
</div>
</div>
<div class="flex items-center gap-2">
<button class="btn">Run again</button>
<button class="btn">Download .log</button>
</div>
</div>
<!-- summary stats -->
<div class="grid grid-cols-12 gap-6" style="margin-top: 24px; padding: 18px 0; border-top: 1px solid var(--line-soft); border-bottom: 1px solid var(--line-soft);">
<div class="col-span-3">
<div style="font-size: 11px; color: var(--ink-fade); text-transform: uppercase; letter-spacing: 0.08em;">Bytes added</div>
<div class="mono" style="font-size: 18px; color: var(--ink); margin-top: 4px;">142 <span style="font-size: 12px; color: var(--ink-mute);">MB</span></div>
<div style="font-size: 11.5px; color: var(--ink-mute); margin-top: 2px;">of <span class="mono">3.7 GB</span> processed</div>
</div>
<div class="col-span-3">
<div style="font-size: 11px; color: var(--ink-fade); text-transform: uppercase; letter-spacing: 0.08em;">Files</div>
<div class="mono" style="font-size: 18px; color: var(--ink); margin-top: 4px;">21,402</div>
<div style="font-size: 11.5px; color: var(--ink-mute); margin-top: 2px;"><span class="mono">187</span> new · <span class="mono">42</span> changed</div>
</div>
<div class="col-span-3">
<div style="font-size: 11px; color: var(--ink-fade); text-transform: uppercase; letter-spacing: 0.08em;">Throughput</div>
<div class="mono" style="font-size: 18px; color: var(--ink); margin-top: 4px;">42 <span style="font-size: 12px; color: var(--ink-mute);">MB/s</span></div>
<div style="font-size: 11.5px; color: var(--ink-mute); margin-top: 2px;">avg over 5m 21s</div>
</div>
<div class="col-span-3">
<div style="font-size: 11px; color: var(--ink-fade); text-transform: uppercase; letter-spacing: 0.08em;">Snapshot</div>
<div class="mono" style="font-size: 18px; color: var(--ink); margin-top: 4px;">91bbc80d</div>
<div style="font-size: 11.5px; color: var(--ink-mute); margin-top: 2px;">added to repo</div>
</div>
</div>
</div>
<!-- log tail (final lines) -->
<div class="doc" style="padding: 24px 32px 56px;">
<div class="flex items-center justify-between" style="margin-bottom: 12px;">
<div class="flex items-center gap-3">
<h2 style="font-size: 13px; font-weight: 600; letter-spacing: 0.01em;">Stream <span style="font-weight: 400; color: var(--ink-fade);">· complete</span></h2>
<span style="font-size: 11.5px; color: var(--ink-fade);">2,418 lines</span>
</div>
<div class="flex items-center gap-2">
<button class="btn">Show all lines</button>
<button class="btn">Filter ▾</button>
</div>
</div>
<div class="log">
<div class="log-line"><span class="ts">11:48:38.044</span><span class="log-stream-tag">EVENT</span><span class="log-stream-event">{"message_type":"status","percent_done":0.987,"files_done":21127,"bytes_done":3905990656}</span></div>
<div class="log-line"><span class="ts">11:48:39.058</span><span class="log-stream-tag">EVENT</span><span class="log-stream-event">{"message_type":"status","percent_done":0.998,"files_done":21358,"bytes_done":3950182400}</span></div>
<div class="log-line"><span class="ts">11:48:40.064</span><span class="log-stream-tag">OUT</span><span class="log-stream-stdout">processed 21402 files, 3.689 GiB in 5:19</span></div>
<div class="log-line"><span class="ts">11:48:41.022</span><span class="log-stream-tag">OUT</span><span class="log-stream-stdout">snapshot 91bbc80d saved</span></div>
<div class="log-line"><span class="ts">11:48:42.108</span><span class="log-stream-tag">EVENT</span><span class="log-payload-ok"><span class="log-stream-event">{"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}</span></span></div>
<div class="log-line" style="padding-bottom: 14px;"><span class="ts">11:48:42.337</span><span class="log-stream-tag" style="color: var(--ok);">END</span><span style="color: var(--ok);">restic exited 0 · success</span></div>
</div>
</div>
</div>
</div>
<!-- Stage 3: failed -->
<div class="stage-label">State C · failed</div>
<div class="stage-frame">
<div style="background: var(--bg);">
<div class="hairline" style="background: var(--bg);">
<div class="doc flex items-center justify-between" style="padding: 16px 0;">
<div class="flex items-center gap-3">
<div class="mono" style="font-size:13px; color: var(--ink); font-weight:500;">restic-manager</div>
<div class="mono" style="font-size:11px; color: var(--ink-fade);">v0.1.0-alpha</div>
</div>
<div class="flex items-center gap-5">
<div class="mono" style="font-size:12px; color: var(--ink-mute);">steve@dcglab</div>
<button class="btn btn-ghost">Sign out</button>
</div>
</div>
</div>
<div class="hairline" style="background: var(--bg);">
<div class="doc flex items-end">
<nav class="flex items-end">
<div class="nav-tab active">Dashboard</div>
<div class="nav-tab">Repos</div>
<div class="nav-tab">Alerts <span class="mono ml-1.5" style="font-size:11px; color: var(--bad);">5</span></div>
<div class="nav-tab">Audit</div>
<div class="nav-tab">Settings</div>
</nav>
</div>
</div>
<div class="doc" style="padding: 28px 32px 0;">
<div class="crumbs"><a>Dashboard</a><span class="sep">/</span><a>build-runner</a><span class="sep">/</span><span style="color: var(--ink-mid);">job 01KQH…9F8C</span></div>
<div class="flex items-start justify-between" style="margin-top: 14px;">
<div>
<div class="flex items-center gap-3">
<span style="width: 9px; height: 9px; border-radius: 50%; background: var(--bad); box-shadow: 0 0 0 4px color-mix(in oklch, var(--bad), transparent 80%);"></span>
<h1 style="font-size: 22px; font-weight: 500; letter-spacing: -0.01em;">backup <span style="color: var(--ink-fade);">·</span> <span class="mono" style="color: var(--ink); font-weight: 500;">build-runner</span></h1>
<span class="mono" style="font-size: 11px; padding: 3px 8px; background: color-mix(in oklch, var(--bad), transparent 88%); color: var(--bad); border: 1px solid color-mix(in oklch, var(--bad), transparent 70%); border-radius: 3px;">failed</span>
</div>
<div class="flex items-center gap-3" style="margin-top: 10px; font-size: 12.5px; color: var(--ink-mute);">
<span>job <span class="mono" style="color: var(--ink-mid);">01KQH3FX9F8C…</span></span>
<span style="color: var(--ink-fade);">·</span>
<span>finished <span class="mono" style="color: var(--ink-mid);">10:53:18 (1.4s)</span></span>
<span style="color: var(--ink-fade);">·</span>
<span>exit code <span class="mono" style="color: var(--bad);">1</span></span>
</div>
</div>
<div class="flex items-center gap-2">
<button class="btn">Retry job</button>
<button class="btn">Open alert</button>
</div>
</div>
<!-- failure summary panel -->
<div class="panel" style="margin-top: 24px; padding: 16px 18px; border-radius: 7px; border-color: color-mix(in oklch, var(--bad), transparent 60%);">
<div style="font-size: 11px; color: var(--bad); text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600; margin-bottom: 6px;">Failure</div>
<p style="font-size: 13.5px; color: var(--ink); line-height: 1.6;" class="text-pretty">
<span class="mono" style="color: var(--bad);">unable to acquire lock</span> — the repo at <span class="mono" style="color: var(--ink-mid);">rest:https://restic.unraid.lab/build-runner/</span> is locked by another operation.
</p>
<div style="font-size: 12px; color: var(--ink-mute); margin-top: 10px; line-height: 1.6;" class="text-pretty">
Most likely a stale lock from a previous run that didn't clean up. Run <span class="mono" style="color: var(--ink);">unlock</span> on this host's repo, then retry the backup.
</div>
</div>
</div>
<div class="doc" style="padding: 24px 32px 56px;">
<div class="log">
<div class="log-line"><span class="ts">10:53:17.001</span><span class="log-stream-tag">OUT</span><span class="log-stream-stdout">opening repository at rest:https://restic.unraid.lab/build-runner/</span></div>
<div class="log-line"><span class="ts">10:53:17.388</span><span class="log-stream-tag">OUT</span><span class="log-stream-stdout">repository f4b81ec7 opened</span></div>
<div class="log-line"><span class="ts">10:53:17.602</span><span class="log-stream-tag">OUT</span><span class="log-stream-stdout">created new cache in /var/lib/restic-manager/cache</span></div>
<div class="log-line"><span class="ts">10:53:18.211</span><span class="log-stream-tag">ERR</span><span class="log-stream-stderr"><span class="log-payload-err">Fatal: 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</span></span></div>
<div class="log-line" style="padding-bottom: 14px;"><span class="ts">10:53:18.298</span><span class="log-stream-tag" style="color: var(--bad);">END</span><span style="color: var(--bad);">restic exited 1 · failed</span></div>
</div>
</div>
</div>
</div>
</div>
<style>
@keyframes pulse {
0%, 100% { box-shadow: 0 0 0 4px color-mix(in oklch, var(--accent), transparent 80%); }
50% { box-shadow: 0 0 0 6px color-mix(in oklch, var(--accent), transparent 92%); }
}
</style>
</body>
</html>