Restore
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/dashboard.html |
| Site | DriftSense self-monitor on local |
| Kind | local |
| Captured at | 2026-07-12 00:02:13 |
| Captured SHA | a0ec3199a4280e9022792890ae324d225ab070d9ced8692aa179578b6fd2e60c |
Current state on disk
Live check just now. If SHAs match, the restore is a no-op.
| File present | yes |
| Size | 22319 bytes |
| Current SHA | 3c6f4d0cd753 |
| 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. +11 additions, -92 deletions, 260 unchanged context lines.
| 1 | 1 | <div class="page-head"> |
| 2 | 2 | <span class="page-eyebrow"><span class="dot"></span> Overview</span> |
| 3 | 3 | <h1 class="page-title">Dashboard</h1> |
| 4 | 4 | <p class="page-subtitle">Portfolio-wide health at a glance. Every file change, every schema change, every drifted container -- captured silently, restorable to any moment, no workflow discipline required.</p> |
| 5 | </div> | |
| 6 | ||
| 7 | <div class="ds-guide"> | |
| 8 | <div class="dsg-icon">i</div> | |
| 9 | <div> | |
| 10 | <span class="dsg-title">New here?</span> | |
| 11 | This is your control tower. Every module below is a live view of what DriftSense has captured. Hover any <span class="ds-help" data-help-title="You just hovered a help chip!" data-help-body="These chips are scattered across DriftSense to explain what you're looking at. Hover, focus with keyboard, or click through to related docs. If you get lost anywhere, look for the <strong>?</strong>."></span> chip for a plain-English explanation. Start with <a href="/file_changes.cgi">File changes</a> to see recent activity, or hit <a href="/pin_hotspots.cgi">Pin hotspots</a> to freeze the most-edited files. | |
| 12 | </div> | |
| 13 | 5 | </div> |
| 14 | 6 | |
| 15 | 7 | <!-- KPI row --> |
| 16 | 8 | <div class="dash-grid"> |
| 17 | 9 | <div class="module glow-teal"> |
| 18 | 10 | <div class="module-head"><div class="left"> |
| 19 | 11 | <div class="module-icon"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg></div> |
| 20 | <div class="module-title">File changes<span class="ds-help" data-help-title="File changes captured" data-help-body="Total <strong>file additions, modifications, and deletions</strong> DriftSense saw in the last 24 hours across every configured File Monitor. Each capture is content-addressed by SHA-256 and gzipped in <code>BLOB_STORE</code>, so 100 versions of the same file cost one copy plus a refcount." data-help-link="/file_changes.cgi" data-help-link-label="Open file changes"></span></div> | |
| 12 | <div class="module-title">File changes</div> | |
| 21 | 13 | <span class="module-subtitle">Captured in the last 24 hours across every monitored path on every server.</span> |
| 22 | 14 | </div></div> |
| 23 | 15 | <div class="module-body kpi kpi-teal"> |
| 24 | <div class="num">$kpi_file_today<span class="ds-trend ds-trend-$kpi_file_class">$kpi_file_txt</span></div> | |
| 16 | <div class="num">$kpi_file_today</div> | |
| 25 | 17 | <div class="lbl">Last 24h</div> |
| 26 | <div class="sub">vs prior 24h: $kpi_file_prev · all watched paths</div> | |
| 18 | <div class="sub">All watched paths + containers</div> | |
| 27 | 19 | </div> |
| 28 | 20 | </div> |
| 29 | 21 | |
| 30 | 22 | <div class="module glow-amber"> |
| 31 | 23 | <div class="module-head"><div class="left"> |
| 32 | 24 | <div class="module-icon"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5v6c0 1.7 4 3 9 3s9-1.3 9-3V5"/><path d="M3 11v6c0 1.7 4 3 9 3s9-1.3 9-3v-6"/></svg></div> |
| 33 | <div class="module-title">Schema changes<span class="ds-help" data-help-title="Schema DDL captured" data-help-body="Every <code>CREATE TABLE</code> / <code>ALTER TABLE</code> / <code>DROP</code> across your monitored MariaDB / MySQL databases in the last 7 days. The scanner uses <code>information_schema.TABLES.UPDATE_TIME</code> for a cheap early-exit -- unchanged tables aren't re-snapshotted." data-help-link="/schema_changes.cgi" data-help-link-label="Open schema changes"></span></div> | |
| 25 | <div class="module-title">Schema changes</div> | |
| 34 | 26 | <span class="module-subtitle">DDL diffs across every monitored DB in the last 7 days -- caught even if no one committed them.</span> |
| 35 | 27 | </div></div> |
| 36 | 28 | <div class="module-body kpi kpi-amber"> |
| 37 | <div class="num">$kpi_schema_7d<span class="ds-trend ds-trend-$kpi_schema_class">$kpi_schema_txt</span></div> | |
| 29 | <div class="num">$kpi_schema_7d</div> | |
| 38 | 30 | <div class="lbl">Last 7 days</div> |
| 39 | <div class="sub">vs prior 7d: $kpi_schema_prev · all monitored DBs</div> | |
| 31 | <div class="sub">Across all monitored databases</div> | |
| 40 | 32 | </div> |
| 41 | 33 | </div> |
| 42 | 34 | |
| 43 | 35 | <div class="module glow-emerald"> |
| 44 | 36 | <div class="module-head"><div class="left"> |
| 45 | 37 | <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> |
| 46 | <div class="module-title">Named releases<span class="ds-help" data-help-title="Named releases" data-help-body="Retroactive bookmarks that pin captured content forever. A release can be <strong>portfolio-wide</strong> (protect everything captured up to that moment) or <strong>scoped</strong> to a single site. Explicit pins (via <a href='/pin_hotspots.cgi'>Pin hotspots</a>) protect specific blobs regardless of scope. Auto-purge always respects pins." data-help-link="/named_releases.cgi" data-help-link-label="Open releases"></span></div> | |
| 38 | <div class="module-title">Named releases</div> | |
| 47 | 39 | <span class="module-subtitle">Bookmark moments you've saved. Auto-purge respects these -- their blobs stay forever.</span> |
| 48 | 40 | </div></div> |
| 49 | 41 | <div class="module-body kpi kpi-emerald"> |
| 50 | 42 | <div class="num">$kpi_releases</div> |
| 51 | 43 | <div class="lbl">Bookmarked</div> |
| 52 | 44 | <div class="sub">Point-in-time restore anchors</div> |
| 53 | 45 | </div> |
| 54 | 46 | </div> |
| 55 | 47 | |
| 56 | 48 | <div class="module glow-rose"> |
| 57 | 49 | <div class="module-head"><div class="left"> |
| 58 | 50 | <div class="module-icon"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg></div> |
| 59 | <div class="module-title">Drift alerts<span class="ds-help" data-help-title="Container drift alerts" data-help-body="Containers you told DriftSense should be <strong>immutable</strong> (never change at runtime) but which had files change inside them. Any non-zero count here is a compliance signal worth investigating. Coming in the container-awareness release (host-side OverlayFS reader)."></span></div> | |
| 51 | <div class="module-title">Drift alerts</div> | |
| 60 | 52 | <span class="module-subtitle">Containers that were supposed to stay immutable but got modified at runtime. Compliance signal.</span> |
| 61 | 53 | </div></div> |
| 62 | 54 | <div class="module-body kpi kpi-rose"> |
| 63 | 55 | <div class="num">$kpi_drifted</div> |
| 64 | 56 | <div class="lbl">Active drift</div> |
| 65 | 57 | <div class="sub">On supposedly-immutable containers</div> |
| 66 | 58 | </div> |
| 67 | 59 | </div> |
| 68 | 60 | </div> |
| 69 | 61 | |
| 70 | 62 | <!-- 90-day activity timeline --> |
| 71 | 63 | <div class="module glow-teal" style="margin-bottom:20px"> |
| 72 | 64 | <div class="module-head"><div class="left"> |
| 73 | 65 | <div class="module-icon"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 3v18h18"/><rect x="6" y="12" width="3" height="6"/><rect x="11" y="7" width="3" height="11"/><rect x="16" y="10" width="3" height="8"/></svg></div> |
| 74 | 66 | <div class="module-title">Activity, last 90 days</div> |
| 75 | 67 | <span class="module-subtitle">Every captured change, stacked by kind. Teal is files, amber is schema DDL. Hover for exact daily counts.</span> |
| 76 | 68 | </div></div> |
| 77 | 69 | <div class="module-body" style="padding-top:10px"> |
| 78 | 70 | $timeline_svg |
| 79 | 71 | </div> |
| 80 | 72 | </div> |
| 81 | 73 | |
| 82 | 74 | <!-- ============================ Per-site health ============================ --> |
| 83 | 75 | [if:$has_per_site] |
| 84 | 76 | <div class="module glow-emerald" style="margin-bottom:20px"> |
| 85 | 77 | <div class="module-head"><div class="left"> |
| 86 | 78 | <div class="module-icon"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="9"/><rect x="14" y="3" width="7" height="5"/><rect x="14" y="12" width="7" height="9"/><rect x="3" y="16" width="7" height="5"/></svg></div> |
| 87 | <div class="module-title">Per-site health<span class="ds-help" data-help-title="Per-site health" data-help-body="One row per active <code>FILE_MONITOR_SETTINGS</code> row. <strong>Active files</strong> = distinct filenames that haven't been marked deleted. <strong>Captures</strong> = total change rows ever recorded. <strong>Last 24h</strong> = the noisy signal you probably care about. Click a row to filter <code>/file_changes.cgi</code> to just that site."></span></div> | |
| 79 | <div class="module-title">Per-site health</div> | |
| 88 | 80 | <span class="module-subtitle">One row per active monitor. Sparkline is the last 30 days of change activity. Click a row to drill into that site's file changes.</span> |
| 89 | 81 | </div></div> |
| 90 | 82 | <div class="module-body tight"> |
| 91 | 83 | <table class="tbl"> |
| 92 | 84 | <thead><tr> |
| 93 | 85 | <th style="width:36%">Site / scan</th> |
| 94 | 86 | <th style="width:120px">30-day rhythm</th> |
| 95 | 87 | <th style="width:100px" class="mono">Active files</th> |
| 96 | 88 | <th style="width:100px" class="mono">Captures</th> |
| 97 | 89 | <th style="width:100px" class="mono">Last 24h</th> |
| 98 | 90 | <th style="width:170px">Last change</th> |
| 99 | 91 | </tr></thead> |
| 100 | 92 | <tbody> |
| 101 | 93 | [loop:@per_site] |
| 102 | 94 | <tr> |
| 103 | 95 | <td> |
| 104 | 96 | <a href="$drilldown_url" style="color:var(--accent-hi);text-decoration:none"> |
| 105 | 97 | <strong>$scan_name</strong> |
| 106 | 98 | </a> |
| 107 | 99 | <div class="mono dim" style="font-size:11px">$path_short <span class="pill $kind_pill" style="margin-left:6px;font-size:9.5px">$server_kind</span></div> |
| 108 | 100 | </td> |
| 109 | 101 | <td>$spark_svg</td> |
| 110 | 102 | <td class="mono">$active_files</td> |
| 111 | 103 | <td class="mono">$captured_ct</td> |
| 112 | 104 | <td class="mono" style="color:var(--accent-hi)">$changes_24h</td> |
| 113 | 105 | <td class="mono dim"><span class="ts" data-ts="$last_change_epoch" data-fmt="relative">$last_change_h</span></td> |
| 114 | 106 | </tr> |
| 115 | 107 | [/loop] |
| 116 | 108 | </tbody> |
| 117 | 109 | </table> |
| 118 | 110 | </div> |
| 119 | 111 | </div> |
| 120 | 112 | [/if] |
| 121 | 113 | |
| 122 | 114 | <!-- ============================ Change-frequency ranking ============================ --> |
| 123 | 115 | [if:$has_top_files] |
| 124 | 116 | <div class="dash-grid" style="grid-template-columns:2fr 1fr;margin-bottom:20px"> |
| 125 | 117 | <div class="module glow-amber"> |
| 126 | 118 | <div class="module-head"><div class="left"> |
| 127 | 119 | <div class="module-icon"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 17 9 11 13 15 21 7"/><polyline points="14 7 21 7 21 14"/></svg></div> |
| 128 | <div class="module-title">Change-frequency ranking<span class="ds-help" data-help-title="What is the "live-config heatmap"?" data-help-body="Files listed here change most often over the last 30 days. In practice they're usually <strong>self-updating config files</strong> (nginx access logs mid-rotation, cache files, security-tool state files) or <strong>files being actively edited by humans</strong>. Either way, they're the files most worth pinning to a Named Release — and the ones most likely to trigger a false-alarm alert if you don't." data-help-link="/pin_hotspots.cgi" data-help-link-label="Bulk-pin them"></span></div> | |
| 120 | <div class="module-title">Change-frequency ranking</div> | |
| 129 | 121 | <span class="module-subtitle">Top 20 files by change count in the last 30 days — the "live-config heatmap". Files that show up here are likely candidates for pinning to a Named Release.</span> |
| 130 | 122 | </div> |
| 131 | 123 | <a href="/pin_hotspots.cgi?n=20&days=30" class="tr-chip is-active" style="border:none;margin-left:auto">Pin these →</a> |
| 132 | 124 | </div> |
| 133 | 125 | <div class="module-body tight" style="max-height:520px;overflow:auto"> |
| 134 | 126 | <table class="tbl"> |
| 135 | 127 | <thead><tr> |
| 136 | 128 | <th style="width:60px">Rank</th> |
| 137 | 129 | <th>File</th> |
| 138 | 130 | <th style="width:120px">Site</th> |
| 139 | 131 | <th style="width:70px">Changes</th> |
| 140 | 132 | <th style="width:150px">Latest</th> |
| 141 | 133 | </tr></thead> |
| 142 | 134 | <tbody> |
| 143 | 135 | [loop:@top_files] |
| 144 | 136 | <tr> |
| 145 | 137 | <td class="mono" style="color:var(--accent-warn)"><strong>#</strong></td> |
| 146 | 138 | <td class="mono" style="font-size:11.5px"><a href="$diff_url" style="color:var(--accent-hi);text-decoration:none">$file_short</a></td> |
| 147 | 139 | <td class="dim" style="font-size:11.5px">$scan_name</td> |
| 148 | 140 | <td class="mono" style="color:var(--accent-warn);font-weight:700">$change_ct</td> |
| 149 | 141 | <td class="mono dim"><span class="ts" data-ts="$latest_epoch" data-fmt="relative">$latest_h</span></td> |
| 150 | 142 | </tr> |
| 151 | 143 | [/loop] |
| 152 | 144 | </tbody> |
| 153 | 145 | </table> |
| 154 | 146 | </div> |
| 155 | 147 | </div> |
| 156 | 148 | |
| 157 | 149 | <!-- ============================ Cross-site dedup ============================ --> |
| 158 | 150 | <div class="module glow-teal"> |
| 159 | 151 | <div class="module-head"><div class="left"> |
| 160 | 152 | <div class="module-icon"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/></svg></div> |
| 161 | <div class="module-title">Cross-site dedup<span class="ds-help" data-help-title="Why does this matter?" data-help-body="These blobs are <strong>byte-identical across multiple sites</strong> in your portfolio. BLOB_STORE was already storing one physical copy plus a refcount, so the disk savings are automatic — but making the list visible surfaces <strong>shared code</strong> (a template.pl your team copy-pasted across sister apps) so you can consider extracting it into a real library, or spot a shared file that <em>shouldn't</em> be identical anymore." data-help-link="/sync.cgi" data-help-link-label="Cross-site sync"></span></div> | |
| 153 | <div class="module-title">Cross-site dedup</div> | |
| 162 | 154 | <span class="module-subtitle">Byte-identical files that appear on multiple sites. BLOB_STORE already deduplicates — but seeing this list makes the shared files visible.</span> |
| 163 | 155 | </div></div> |
| 164 | 156 | <div class="module-body tight" style="max-height:520px;overflow:auto"> |
| 165 | 157 | [if:$has_shared_blobs] |
| 166 | 158 | <table class="tbl"> |
| 167 | 159 | <thead><tr><th style="width:60px">Sites</th><th>Blob</th><th style="width:70px">Size</th></tr></thead> |
| 168 | 160 | <tbody> |
| 169 | 161 | [loop:@shared_blobs] |
| 170 | 162 | <tr> |
| 171 | 163 | <td class="mono"><strong style="color:var(--accent-hi)">$site_ct</strong></td> |
| 172 | 164 | <td class="mono" style="font-size:11px"> |
| 173 | 165 | <a href="$diff_url" style="color:var(--accent-hi);text-decoration:none">$sha_short</a> |
| 174 | 166 | <div class="dim" style="font-size:10.5px;line-height:1.4">$scans_h</div> |
| 175 | 167 | </td> |
| 176 | 168 | <td class="mono dim">$size_h</td> |
| 177 | 169 | </tr> |
| 178 | 170 | [/loop] |
| 179 | 171 | </tbody> |
| 180 | 172 | </table> |
| 181 | 173 | [/if] |
| 182 | 174 | [if:!$has_shared_blobs] |
| 183 | 175 | <div style="padding:26px 14px;text-align:center;color:var(--text-dim);font-size:12.5px"> |
| 184 | 176 | Nothing shared across sites yet. |
| 185 | 177 | </div> |
| 186 | 178 | [/if] |
| 187 | 179 | </div> |
| 188 | </div> | |
| 189 | </div> | |
| 190 | [/if] | |
| 191 | ||
| 192 | <!-- Stable versions panel (prominent, per Shawn's ask) --> | |
| 193 | [if:$has_stables] | |
| 194 | <div class="module glow-emerald" style="margin-bottom:20px"> | |
| 195 | <div class="module-head"><div class="left"> | |
| 196 | <div class="module-icon"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 11 12 14 22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg></div> | |
| 197 | <div class="module-title">Stable versions<span class="ds-help" data-help-title="What is a Stable version?" data-help-body="Named releases tagged with <strong>stable</strong> tier are your production-ready checkpoints. Great for "working v1.0", "stable Nov-2026", or "shipped-to-customer" snapshots. One-click restore rolls every pinned file back to that state. Working-tier releases show here too but ranked below stable." data-help-link="/named_releases.cgi" data-help-link-label="Manage releases"></span></div> | |
| 198 | <span class="module-subtitle">Bookmarked working / production-ready states. One-click roll-back.</span> | |
| 199 | </div> | |
| 200 | <a href="/named_releases.cgi" class="tr-chip" style="margin-left:auto">Save a version →</a> | |
| 201 | </div> | |
| 202 | <div class="module-body tight"> | |
| 203 | <table class="tbl"> | |
| 204 | <thead><tr> | |
| 205 | <th>Version</th> | |
| 206 | <th style="width:80px">Tier</th> | |
| 207 | <th style="width:90px">Pins</th> | |
| 208 | <th style="width:150px">Saved</th> | |
| 209 | <th style="width:130px"></th> | |
| 210 | </tr></thead> | |
| 211 | <tbody> | |
| 212 | [loop:@stable_versions] | |
| 213 | <tr> | |
| 214 | <td> | |
| 215 | <strong>$label</strong> | |
| 216 | <div class="dim" style="font-size:11.5px;margin-top:2px">$description</div> | |
| 217 | </td> | |
| 218 | <td><span class="pill $tier_pill">$tier_lbl</span></td> | |
| 219 | <td class="mono">$pin_ct</td> | |
| 220 | <td class="mono dim"><span class="ts" data-ts="$released_epoch" data-fmt="date">$released_h</span></td> | |
| 221 | <td><a href="$restore_url" class="tr-chip is-active" style="border:none;padding:4px 12px;font-size:11px">Roll back to this</a></td> | |
| 222 | </tr> | |
| 223 | [/loop] | |
| 224 | </tbody> | |
| 225 | </table> | |
| 226 | </div> | |
| 227 | </div> | |
| 228 | [/if] | |
| 229 | ||
| 230 | <!-- Pin drift tile (Wave 5 F1) --> | |
| 231 | [if:$has_drifts] | |
| 232 | <div class="module glow-rose" style="margin-bottom:20px"> | |
| 233 | <div class="module-head"><div class="left"> | |
| 234 | <div class="module-icon"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg></div> | |
| 235 | <div class="module-title">Pinned files that drifted<span class="ds-help" data-help-title="Pinned drift" data-help-body="Named releases with explicit <code>NAMED_RELEASE_PINS</code> rows are supposed to freeze content forever. When the current-latest SHA of any pinned file no longer matches the pinned SHA, it shows up here. Restore the release to bring every drifted file back to the pinned state at once."></span></div> | |
| 236 | <span class="module-subtitle"><strong>$drifted_ct</strong> pinned file(s) currently differ from their pinned SHA across <strong>$releases_affected</strong> release(s). Restore or acknowledge each below.</span> | |
| 237 | </div></div> | |
| 238 | <div class="module-body tight"> | |
| 239 | <table class="tbl"> | |
| 240 | <thead><tr> | |
| 241 | <th>File</th> | |
| 242 | <th style="width:170px">Release</th> | |
| 243 | <th style="width:110px">Pinned SHA</th> | |
| 244 | <th style="width:110px">Current SHA</th> | |
| 245 | <th style="width:150px">Detected</th> | |
| 246 | <th style="width:100px"></th> | |
| 247 | </tr></thead> | |
| 248 | <tbody> | |
| 249 | [loop:@drift_rows] | |
| 250 | <tr> | |
| 251 | <td class="mono" style="font-size:11.5px">$file_short</td> | |
| 252 | <td class="dim">$release_name</td> | |
| 253 | <td class="mono dim">$pinned_short</td> | |
| 254 | <td class="mono" style="color:var(--accent-danger-hi)">$current_short</td> | |
| 255 | <td class="mono dim"><span class="ts" data-ts="$detected_epoch" data-fmt="relative">$detected_h</span></td> | |
| 256 | <td><a href="$restore_url" class="tr-chip" style="padding:3px 10px;font-size:11px">Fix</a></td> | |
| 257 | </tr> | |
| 258 | [/loop] | |
| 259 | </tbody> | |
| 260 | </table> | |
| 261 | 180 | </div> |
| 262 | 181 | </div> |
| 263 | 182 | [/if] |
| 264 | 183 | |
| 265 | 184 | <!-- Recent activity --> |
| 266 | 185 | <div class="module glow-teal" style="margin-bottom:20px"> |
| 267 | 186 | <div class="module-head"><div class="left"> |
| 268 | 187 | <div class="module-icon"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg></div> |
| 269 | 188 | <div class="module-title">Recent activity</div> |
| 270 | 189 | <span class="module-subtitle">Newest first. Click any row to open its full diff. File + schema changes merged into one timeline.</span> |
| 271 | 190 | </div></div> |
| 272 | 191 | <div class="module-body tight"> |
| 273 | 192 | [if:$has_recent] |
| 274 | 193 | <table class="tbl"> |
| 275 | 194 | <thead><tr> |
| 276 | 195 | <th style="width:80px">Type</th> |
| 277 | 196 | <th>What</th> |
| 278 | 197 | <th>Detail</th> |
| 279 | 198 | <th style="width:170px">When</th> |
| 280 | 199 | </tr></thead> |
| 281 | 200 | <tbody> |
| 282 | 201 | [loop:@recent] |
| 283 | 202 | <tr> |
| 284 | 203 | <td><span class="pill $kind_pill">$kind</span></td> |
| 285 | 204 | <td class="mono" style="font-size:12.5px"><a href="$diff_url" style="color:var(--accent-hi);text-decoration:none">$what</a></td> |
| 286 | 205 | <td class="dim" style="font-size:12.5px">$detail</td> |
| 287 | 206 | <td class="mono dim"><span class="ts" data-ts="$ts_epoch" data-fmt="datetime">$ts</span></td> |
| 288 | 207 | </tr> |
| 289 | 208 | [/loop] |
| 290 | 209 | </tbody> |
| 291 | 210 | </table> |
| 292 | 211 | [/if] |
| 293 | 212 | [if:!$has_recent] |
| 294 | 213 | <div style="padding:36px 14px;text-align:center;color:var(--text-dim);font-size:13.5px"> |
| 295 | 214 | No activity captured yet. Start a file monitor or point at a database in the <a href="[url:file_monitors]" style="color:var(--accent-hi)">config</a> to begin capture. |
| 296 | 215 | </div> |
| 297 | 216 | [/if] |
| 298 | 217 | </div> |
| 299 | 218 | </div> |
| 300 | 219 | |
| 301 | 220 | <div class="dash-grid" style="grid-template-columns:2fr 1fr"> |
| 302 | 221 | <div class="module glow-emerald"> |
| 303 | 222 | <div class="module-head"><div class="left"> |
| 304 | 223 | <div class="module-icon"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg></div> |
| 305 | 224 | <div class="module-title">Named releases</div> |
| 306 | 225 | <span class="module-subtitle">Recent bookmarks. Any file blob referenced from a named release is protected from auto-purge.</span> |
| 307 | 226 | </div></div> |
| 308 | 227 | <div class="module-body tight"> |
| 309 | 228 | [if:$has_releases] |
| 310 | 229 | <table class="tbl"> |
| 311 | 230 | <thead><tr><th>Name</th><th>By</th><th style="width:130px">When</th></tr></thead> |
| 312 | 231 | <tbody> |
| 313 | 232 | [loop:@releases] |
| 314 | 233 | <tr> |
| 315 | 234 | <td><strong>$name</strong><div class="dim" style="font-size:11.5px">$description</div></td> |
| 316 | 235 | <td class="dim">$released_by</td> |
| 317 | 236 | <td class="mono dim"><span class="ts" data-ts="$released_epoch" data-fmt="date">$released_h</span></td> |
| 318 | 237 | </tr> |
| 319 | 238 | [/loop] |
| 320 | 239 | </tbody> |
| 321 | 240 | </table> |
| 322 | 241 | [/if] |
| 323 | 242 | [if:!$has_releases] |
| 324 | 243 | <div style="padding:28px 14px;text-align:center;color:var(--text-dim);font-size:13.5px"> |
| 325 | 244 | No named releases yet. Bookmark a moment to protect it from auto-purge. |
| 326 | 245 | </div> |
| 327 | 246 | [/if] |
| 328 | 247 | </div> |
| 329 | 248 | </div> |
| 330 | 249 | |
| 331 | 250 | <div class="module glow-sky"> |
| 332 | 251 | <div class="module-head"><div class="left"> |
| 333 | 252 | <div class="module-icon"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="2" width="20" height="8" rx="2"/><rect x="2" y="14" width="20" height="8" rx="2"/></svg></div> |
| 334 | 253 | <div class="module-title">Servers</div> |
| 335 | 254 | <span class="module-subtitle">Central + agent nodes.</span> |
| 336 | 255 | </div></div> |
| 337 | 256 | <div class="module-body tight"> |
| 338 | 257 | <table class="tbl"> |
| 339 | 258 | <thead><tr><th>Name</th><th>Status</th><th class="dim" style="width:100px">Last seen</th></tr></thead> |
| 340 | 259 | <tbody> |
| 341 | 260 | [loop:@servers] |
| 342 | 261 | <tr> |
| 343 | 262 | <td><strong>$server_name</strong> <span class="dim mono" style="font-size:11px">$role</span></td> |
| 344 | 263 | <td><span class="pill $status_pill">$status</span></td> |
| 345 | 264 | <td class="mono dim"><span class="ts" data-ts="$heartbeat_epoch" data-fmt="relative">$last_heartbeat_h</span></td> |
| 346 | 265 | </tr> |
| 347 | 266 | [/loop] |
| 348 | 267 | </tbody> |
| 349 | 268 | </table> |
| 350 | 269 | </div> |
| 351 | 270 | </div> |
| 352 | 271 | </div> |
Confirm restore
Backs current content to <target>.drift_restore_backup_<epoch>, writes the captured content, verifies SHA post-write.