Files
steve 8b7b1479a1 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>
2026-05-01 19:05:39 +01:00

414 lines
27 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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>