Restore

O Operator
Restore

Restore file to captured state

Every captured change carries its SHA-256-addressed content in BLOB_STORE. Restoring writes that content back to the original path — current file gets backed up to <path>.drift_restore_backup_<epoch> before overwrite.

What will change
Preview -- nothing has been written yet.
Target file/var/www/vhosts/3dshawn.com/site1/TEMPLATES/named_releases.html
SiteDriftSense self-monitor on local
Kindlocal
Captured at2026-07-12 00:19:41
Captured SHAdef89dd3ea6d0a2402e51111d7378db395ee89a513ad8b01bab06f5fca00b0d1
Current state on disk
Live check just now. If SHAs match, the restore is a no-op.
File presentyes
Size12553 bytes
Current SHA61256b30cf27
Same as captured?no -- restore will change it
What restore will change — live diff
Left column shows current on-disk content; right shows what restore will write. +16 additions, -110 deletions, 95 unchanged context lines.
11<div class="page-head">
22 <span class="page-eyebrow"><span class="dot"></span> Bookmark</span>
33 <h1 class="page-title">Named releases</h1>
44 <p class="page-subtitle">Retroactively bookmark a moment. "Before the schema refactor." "Customer verified this worked." "Before I fired the intern." Every blob referenced by a named release is protected from auto-purge, so restore-to-moment always works &mdash; scoped portfolio-wide or to a single site.</p>
5</div>
6
7<div class="ds-guide">
8 <div class="dsg-icon">i</div>
9 <div>
10 <span class="dsg-title">Three ways to pin content:</span>
11 (1) <strong>Portfolio-wide</strong>: leave scope as "All monitored sites" &mdash; protects every blob captured up to <em>now</em>. (2) <strong>Per-site</strong>: pick a monitor &mdash; only that site's blobs are pinned. (3) <strong>Explicit pins</strong><span class="ds-help" data-help-title="Explicit pins" data-help-body="Instead of pinning &quot;everything captured before this moment&quot;, explicit pins tie a release to specific <code>NAMED_RELEASE_PINS</code> rows &mdash; each pointing at a chosen <code>file_changes_id</code>. Auto-purge always keeps those specific blobs. Use <a href='/pin_hotspots.cgi'>Pin hotspots</a> to create one." data-help-link="/pin_hotspots.cgi" data-help-link-label="Pin hotspots"></span>: use <a href="/pin_hotspots.cgi">Pin hotspots</a> to freeze the current-latest blobs of the churniest files. Set a <strong>Drift alert URL</strong> below and DriftSense will page you when any pinned file's current SHA no longer matches its pinned SHA.
12 </div>
135</div>
146
157<div class="module glow-emerald" style="margin-bottom:16px">
168 <div class="module-head"><div class="left">
179 <div class="module-icon"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 13V7a2 2 0 0 0-2-2h-4l-2-3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h5"/><path d="M14 19l2 2 4-4"/></svg></div>
1810 <div class="module-title">Create a bookmark</div>
1911 <span class="module-subtitle">Names the current moment. Everything captured up to now becomes restorable from this label forever.</span>
2012 </div></div>
2113 <div class="module-body">
2214 <form method="post" action="/named_releases.cgi" style="display:flex;flex-direction:column;gap:12px">
2315 <input type="hidden" name="save" value="1">
2416
2517 <div style="display:flex;gap:12px">
2618 <label style="flex:1;display:flex;flex-direction:column;gap:4px">
27 <span class="rb-label">Internal name</span>
19 <span class="rb-label">Name</span>
2820 <input type="text" name="name" required placeholder="e.g. before-schema-refactor"
29 style="background:var(--bg-1);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 12px;font-family:var(--mono);font-size:12.5px">
30 </label>
31 <label style="flex:1;display:flex;flex-direction:column;gap:4px">
32 <span class="rb-label">Version label
33 <span class="ds-help" data-help-title="What's a version label?" data-help-body="Display name shown across the UI (e.g. <code>v1.0</code>, <code>working v2.4</code>, <code>stable Nov-2026</code>). If left blank we fall back to the internal name."></span>
34 </span>
35 <input type="text" name="version_label" placeholder="e.g. v1.0 or Working 2.4"
3621 style="background:var(--bg-1);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 12px;font-family:var(--mono);font-size:12.5px">
3722 </label>
38 <label style="width:150px;display:flex;flex-direction:column;gap:4px">
39 <span class="rb-label">Tier
40 <span class="ds-help" data-help-title="Tier decides how visible this release is" data-help-body="<strong>Draft</strong> = a plain bookmark. <strong>Working</strong> = a milestone you've verified but might revise. <strong>Stable</strong> = production-ready, elevated in the sidebar and dashboard, one-click rollback target."></span>
41 </span>
42 <select name="tier" style="background:var(--bg-1);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 12px;font-size:12.5px">
43 <option value="draft">Draft</option>
44 <option value="working">Working</option>
45 <option value="stable">Stable</option>
46 </select>
23 <label style="flex:2;display:flex;flex-direction:column;gap:4px">
24 <span class="rb-label">Description</span>
25 <input type="text" name="description" placeholder="Optional context"
26 style="background:var(--bg-1);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 12px;font-size:12.5px">
4727 </label>
48 <label style="width:140px;display:flex;flex-direction:column;gap:4px">
28 <label style="width:160px;display:flex;flex-direction:column;gap:4px">
4929 <span class="rb-label">By</span>
5030 <input type="text" name="released_by" value="Operator"
5131 style="background:var(--bg-1);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 12px;font-size:12.5px">
5232 </label>
5333 </div>
54
55 <label style="display:flex;flex-direction:column;gap:4px">
56 <span class="rb-label">Description</span>
57 <input type="text" name="description" placeholder="Optional context -- what this checkpoint represents"
58 style="background:var(--bg-1);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 12px;font-size:12.5px">
59 </label>
6034
6135 <label style="display:flex;flex-direction:column;gap:4px">
62 <span class="rb-label">Scope <span class="dim" style="text-transform:none;letter-spacing:0;font-size:10.5px">&mdash; primary site (leave "All" for portfolio-wide)</span></span>
36 <span class="rb-label">Scope <span class="dim" style="text-transform:none;letter-spacing:0;font-size:10.5px">&mdash; leave "All monitored sites" for a portfolio-wide bookmark</span></span>
6337 <select name="scope_monitor_id" style="background:var(--bg-1);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 12px;font-size:12.5px">
6438 <option value="0">All monitored sites (portfolio-wide)</option>
65 [loop:@monitors]
66 <option value="$mid">$label</option>
67 [/loop]
68 </select>
69 </label>
70
71 <label style="display:flex;flex-direction:column;gap:4px">
72 <span class="rb-label">Additional scopes
73 <span class="ds-help" data-help-title="Cross-scope releases" data-help-body="A release can cover multiple sites at once. Ctrl+click / Cmd+click to add extra monitors. Great for &quot;stable v1.0 across PTMatrix + AffSoft + ContactForge&quot; without creating three separate releases."></span>
74 <span class="dim" style="text-transform:none;letter-spacing:0;font-size:10.5px">&mdash; Ctrl/Cmd-click to add more (optional)</span>
75 </span>
76 <select name="extra_monitor_ids" multiple size="4"
77 style="background:var(--bg-1);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 12px;font-size:12.5px">
7839 [loop:@monitors]
7940 <option value="$mid">$label</option>
8041 [/loop]
8142 </select>
8243 </label>
8344
8445 <div style="display:flex;gap:12px">
8546 <label style="flex:1;display:flex;flex-direction:column;gap:4px">
8647 <span class="rb-label">Drift alerts <span class="dim" style="text-transform:none;letter-spacing:0;font-size:10.5px">(optional)</span></span>
8748 <select name="alert_delivery_kind" style="background:var(--bg-1);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 12px;font-size:12.5px">
8849 <option value="none">Off</option>
8950 <option value="generic_webhook">Generic HTTP webhook</option>
9051 <option value="slack">Slack webhook</option>
9152 <option value="discord">Discord webhook</option>
9253 </select>
9354 </label>
9455 <label style="flex:2;display:flex;flex-direction:column;gap:4px">
9556 <span class="rb-label">Delivery URL <span class="dim" style="text-transform:none;letter-spacing:0;font-size:10.5px">&mdash; fires when any pinned file drifts from its pinned SHA</span></span>
9657 <input type="text" name="alert_delivery_url" placeholder="https://hooks.slack.com/... (leave blank to disable)"
9758 style="background:var(--bg-1);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 12px;font-family:var(--mono);font-size:11.5px">
9859 </label>
9960 </div>
10061
10162 <div style="display:flex;justify-content:flex-end">
10263 <button type="submit" class="tr-chip is-active" style="border:none;padding:8px 18px">Save bookmark</button>
10364 </div>
10465 </form>
10566 </div>
10667</div>
10768
10869<div class="module glow-teal">
10970 <div class="module-head"><div class="left">
11071 <div class="module-icon"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><line x1="4" y1="9" x2="20" y2="9"/><line x1="4" y1="15" x2="20" y2="15"/><line x1="10" y1="3" x2="8" y2="21"/><line x1="16" y1="3" x2="14" y2="21"/></svg></div>
11172 <div class="module-title">All bookmarks</div>
11273 <span class="module-subtitle">$total bookmarks. Protected ones (default) survive auto-purge; unlocked ones are eligible for cleanup after the retention window.</span>
11374 </div></div>
11475 <div class="module-body tight">
11576 [if:$has_rows]
11677 <table class="tbl">
11778 <thead><tr>
118 <th>Version</th>
119 <th style="width:90px">Tier</th>
120 <th style="width:140px">Scope</th>
79 <th>Name</th>
80 <th style="width:150px">Scope</th>
81 <th style="width:110px">By</th>
82 <th style="width:110px">Protection</th>
12183 <th style="width:130px">Moment</th>
122 <th style="width:260px">Actions</th>
84 <th style="width:150px">Actions</th>
12385 </tr></thead>
12486 <tbody>
12587 [loop:@rows]
12688 <tr>
127 <td>
128 <strong>$display_label</strong>
129 <div class="dim" style="font-size:11.5px;margin-top:2px">$description</div>
130 <div class="mono dim" style="font-size:10.5px;margin-top:2px">by $released_by &middot; internal: $name</div>
131 </td>
132 <td><span class="pill $tier_pill">$tier_lbl</span></td>
89 <td><strong>$name</strong><div class="dim" style="font-size:11.5px;margin-top:2px">$description</div></td>
13390 <td>
13491 <span class="pill $scope_pill" style="font-size:10px">$scope_lbl</span>
13592 <div class="mono dim" style="font-size:10.5px;margin-top:3px">$scope_h</div>
13693 </td>
94 <td class="dim">$released_by</td>
95 <td><span class="pill $lock_pill">$lock_lbl</span></td>
13796 <td class="mono dim"><span class="ts" data-ts="$released_epoch" data-fmt="datetime">$released_h</span></td>
13897 <td>
139 <a href="$restore_url" class="tr-chip" style="padding:3px 10px;font-size:11px">Restore all</a>
140 [if:!$is_stable]
141 <form method="post" action="/named_releases.cgi" style="display:inline">
142 <input type="hidden" name="promote" value="1">
143 <input type="hidden" name="release_id" value="$id">
144 <input type="hidden" name="to_tier" value="stable">
145 <button type="submit" class="tr-chip" style="padding:3px 10px;font-size:11px;background:rgba(52,211,153,.14);color:#86efac;border-color:rgba(52,211,153,.35)">&uarr; Mark stable</button>
146 </form>
147 [/if]
148 <button type="button" onclick="dsTestAlert($id, this)" class="tr-chip" style="padding:3px 10px;font-size:11px" title="Send synthetic drift payload">Test alert</button>
149 [if:$is_stable]
150 <form method="post" action="/named_releases.cgi" style="display:inline">
151 <input type="hidden" name="promote" value="1">
152 <input type="hidden" name="release_id" value="$id">
153 <input type="hidden" name="to_tier" value="working">
154 <button type="submit" class="tr-chip" style="padding:3px 10px;font-size:11px">&darr; Working</button>
155 </form>
156 [/if]
98 <a href="/restore_release.cgi?release_id=$id" class="tr-chip" style="padding:3px 10px;font-size:11px">Restore all</a>
15799 </td>
158100 </tr>
159101 [/loop]
160102 </tbody>
161103 </table>
162104 [/if]
163105 [if:!$has_rows]
164106 <div style="padding:36px 14px;text-align:center;color:var(--text-dim);font-size:13.5px">
165107 No bookmarks yet. Save your first one above.
166108 </div>
167109 [/if]
168110 </div>
169111</div>
170
171<script>
172window.dsTestAlert = async function (id, btn) {
173 var orig = btn.textContent;
174 btn.textContent = 'Sending...';
175 btn.disabled = true;
176 try {
177 var r = await fetch('/named_releases.cgi?op=test_alert&release_id=' + id, { cache: 'no-store' });
178 var j = await r.json();
179 if (j.ok) {
180 btn.textContent = 'Sent ✓';
181 btn.style.background = 'rgba(52,211,153,.14)';
182 btn.style.color = '#86efac';
183 btn.style.borderColor = 'rgba(52,211,153,.35)';
184 alert('Test alert delivered\nHTTP ' + j.http_code);
185 } else {
186 btn.textContent = 'FAIL';
187 btn.style.background = 'rgba(244,63,94,.14)';
188 btn.style.color = '#fda4af';
189 btn.style.borderColor = 'rgba(244,63,94,.35)';
190 alert('Test alert failed:\n' + (j.message || 'unknown'));
191 }
192 } catch (e) {
193 alert('Error: ' + e.message);
194 btn.textContent = orig;
195 } finally {
196 setTimeout(function () {
197 btn.disabled = false;
198 btn.textContent = orig;
199 btn.style.background = '';
200 btn.style.color = '';
201 btn.style.borderColor = '';
202 }, 3000);
203 }
204};
205</script>
Confirm restore
Backs current content to <target>.drift_restore_backup_<epoch>, writes the captured content, verifies SHA post-write.
Cancel Logged to RESTORE_LOG regardless of outcome.