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/README.md |
| Site | DriftSense self-monitor on local |
| Kind | local |
| Captured at | 2026-07-10 18:57:30 |
| Captured SHA | e183b0b25947a998eeb3ad4ed66b5a630b04dd06d335f1f03493ff36f6caccf0 |
Current state on disk
Live check just now. If SHAs match, the restore is a no-op.
| File present | yes |
| Size | 7987 bytes |
| Current SHA | b7628512be54 |
| Same as captured? | no -- restore will change it |
Recent restores of this file
This file has been restored before — check whether you want to duplicate that action or pick a different captured moment.
| Status | Source SHA | By | Error | When |
|---|---|---|---|---|
| success | b7628512be54 | operator | Jul 11 23:36 | |
| success | 3fdb8f6a2875 | operator | Jul 11 23:36 |
What restore will change — live diff
Left column shows current on-disk content; right shows what restore will write. +0 additions, -2 deletions, 168 unchanged context lines.
| 1 | 1 | # DriftSense |
| 2 | 2 | |
| 3 | 3 | **Silent safety net for code + schema drift.** Automatic change capture across files, database schemas, and containers -- no workflow discipline required, no commits ahead of time, restorable to any moment. |
| 4 | 4 | |
| 5 | 5 | --- |
| 6 | 6 | |
| 7 | 7 | ## What it does |
| 8 | 8 | |
| 9 | 9 | - Watches configured filesystem paths, captures every change as a diff + full-file snapshot |
| 10 | 10 | - Watches configured MySQL/MariaDB databases, captures every DDL change (schema drift) |
| 11 | 11 | - Watches Docker containers via host-side OverlayFS inspection -- no in-container agent |
| 12 | 12 | - Deduplicates and gzip-compresses all snapshots (content-addressable storage) |
| 13 | 13 | - Lets the operator retroactively **name a moment** ("before the schema refactor") and pin its snapshots forever |
| 14 | 14 | - Restore any file or schema back to any captured moment |
| 15 | 15 | - Emails when things change in a way the operator flagged as noteworthy |
| 16 | 16 | |
| 17 | 17 | ## Positioning |
| 18 | 18 | |
| 19 | 19 | Not competing with git. Aimed at teams and solos that **don't have git discipline** or where changes happen outside version control: |
| 20 | 20 | |
| 21 | 21 | - WordPress / PHP / Perl shops editing files directly on prod |
| 22 | 22 | - Agencies inheriting client codebases with no VCS at all |
| 23 | 23 | - Sysadmins protecting `/etc/` configs across a fleet |
| 24 | 24 | - Small teams where junior devs sometimes forget to commit |
| 25 | 25 | - Compliance-driven orgs needing audit trails of *all* changes, not just intentional commits |
| 26 | 26 | |
| 27 | 27 | The pitch: *"You don't have to change how your team works. It watches. It captures. It's there when you need it."* |
| 28 | 28 | |
| 29 | 29 | ## Business model |
| 30 | 30 | |
| 31 | 31 | **Install-and-run, self-hosted.** One-time license + optional update sub. Never SaaS -- customer code + DB schemas + file contents stay entirely on customer infrastructure. Zero data on our servers, zero liability, deployable to air-gapped environments. |
| 32 | 32 | |
| 33 | 33 | - v1: $99-499 license (single server) + $10-25/mo optional update sub |
| 34 | 34 | - v2: Team tier (multi-server + K8s DaemonSet) at higher price |
| 35 | 35 | - v3: Enterprise tier (compliance reporting bundle) six-figure territory |
| 36 | 36 | |
| 37 | 37 | ## Architecture |
| 38 | 38 | |
| 39 | 39 | ``` |
| 40 | 40 | Single-server: [ agent | storage | web UI ] <- one tarball, customer's box |
| 41 | 41 | | |
| 42 | 42 | v |
| 43 | 43 | customer's MariaDB (or SQLite fallback) |
| 44 | 44 | |
| 45 | 45 | Multi-server: [ agent ] --HMAC HTTP--> [ central: storage | web UI ] |
| 46 | 46 | [ agent ] --HMAC HTTP--> | |
| 47 | 47 | [ agent ] --HMAC HTTP--> customer's MariaDB |
| 48 | 48 | (customer-owned central node) |
| 49 | 49 | ``` |
| 50 | 50 | |
| 51 | 51 | HMAC-signed HTTP ingest between agents and central node (same pattern as the portfolio's `support_meta_send.cgi`). All traffic stays inside customer's private network. |
| 52 | 52 | |
| 53 | 53 | **Container awareness (Tier 2, v1):** |
| 54 | 54 | - Host-side OverlayFS reader (`/var/lib/docker/overlay2/<id>/diff/`) |
| 55 | 55 | - Docker API for container enumeration + name mapping |
| 56 | 56 | - Zero agent-in-container, zero container privilege escalation |
| 57 | 57 | |
| 58 | 58 | ## Project layout |
| 59 | 59 | |
| 60 | 60 | ``` |
| 61 | 61 | _Claude_DriftSense/ |
| 62 | 62 | _config/ |
| 63 | 63 | drift_sense.conf.example # installer template, key=value pairs |
| 64 | 64 | _schema/ |
| 65 | 65 | drift_sense_v2_schema.sql # fresh install DDL |
| 66 | 66 | MODS/ |
| 67 | 67 | Config.pm # reads /etc/drift_sense/drift_sense.conf + defaults |
| 68 | 68 | DBConnect.pm # unified portfolio version, MySQL or SQLite |
| 69 | 69 | Template.pm # portfolio's current template engine |
| 70 | 70 | PageWrapper.pm # page shell (sidebar + topbar + body injection) |
| 71 | 71 | Urls.pm # URL registry |
| 72 | 72 | Mail.pm # SMTP alerts (v1 stub, upgraded in migration) |
| 73 | 73 | RandCode.pm # random alphanumeric helper |
| 74 | 74 | TEMPLATES/ |
| 75 | 75 | page_wrapper.html # app shell (sidebar, topbar) |
| 76 | 76 | dashboard.html # dashboard content |
| 77 | 77 | ... (per-page templates) |
| 78 | 78 | assets/ |
| 79 | 79 | styles/ |
| 80 | 80 | drift_sense.css # design system (midnight navy + teal + amber) |
| 81 | 81 | dashboard.cgi # modernized proof-of-concept |
| 82 | 82 | _schema_monitor.cgi # DB schema polling daemon (v1 name: httpd) |
| 83 | 83 | _file_monitor.cgi # filesystem walker daemon (v1 name: bash) |
| 84 | 84 | ... (per-page CGIs) |
| 85 | 85 | ``` |
| 86 | 86 | |
| 87 | 87 | ## Palette (design language) |
| 88 | 88 | |
| 89 | 89 | - **Backgrounds**: deep midnight navy (`#030710` -> `#131e30`) |
| 90 | 90 | - **Primary accent**: teal (`#14b8a6`) -- "sense" color, precision instrument feel |
| 91 | 91 | - **Amber**: drift alerts (`#f59e0b`) |
| 92 | 92 | - **Rose**: critical / danger (`#f43f5e`) |
| 93 | 93 | - **Emerald**: clean baseline / OK (`#34d399`) |
| 94 | 94 | - **Sky-blue**: info notices (`#38bdf8`) |
| 95 | 95 | |
| 96 | 96 | Distinct from every portfolio site (Meta-Admin wine, CF cyan, PT burgundy, etc.) so nobody confuses this product with an internal tool. |
| 97 | 97 | |
| 98 | 98 | ## Roadmap |
| 99 | 99 | |
| 100 | 100 | ### v1 -- Foundation (in progress) |
| 101 | 101 | - [x] Standalone workspace copied out of `system_monitor` |
| 102 | 102 | - [x] `MODS::Config` reads external conf file with code defaults |
| 103 | 103 | - [x] `MODS::DBConnect` unified version (`$sth->finish`, timezone, utf8mb4, MySQL + SQLite) |
| 104 | 104 | - [x] `MODS::Template` copied from portfolio (current 571-line version) |
| 105 | 105 | - [x] `MODS::PageWrapper` modernized |
| 106 | 106 | - [x] `MODS::Urls` refactored for product install paths |
| 107 | 107 | - [x] v2 schema draft: BLOB_STORE, NAMED_RELEASES, SERVERS, AGENT_UPLOADS, CONTAINER_TARGETS, CONFIG_DRIFT_BASELINES + FILE_CHANGES/SCHEMA_CHANGE column adds |
| 108 | 108 | - [x] Palette (midnight navy + teal + amber) as reusable CSS |
| 109 | 109 | - [x] `TEMPLATES/page_wrapper.html` -- new sidebar + topbar |
| 110 | 110 | - [x] `dashboard.cgi` + `dashboard.html` -- modernized proof-of-concept |
| 111 | 111 | - [ ] Rewrite remaining CGIs (file_changes, schema_changes, table_locks, databases, file_monitors, servers, containers, settings) |
| 112 | 112 | - [ ] Rewrite daemons (`_schema_monitor.cgi`, `_file_monitor.cgi`) to use unified DBConnect + write to BLOB_STORE with content-addressable dedup |
| 113 | 113 | - [ ] Implement **named releases** feature (bookmark + auto-purge exemption) |
| 114 | 114 | - [ ] Implement **point-in-time restore** UI (pick date, download file / restore schema DDL) |
| 115 | 115 | - [ ] Implement **auto-purge past N days** cron |
| 116 | 116 | - [ ] Implement **timestamp-only change suppression** |
| 117 | 117 | - [ ] Implement **missing-INT PK gap detection** (Shawn's own note -- detect table deletes) |
| 118 | 118 | - [ ] Diff viewer with syntax highlighting |
| 119 | 119 | - [ ] SMTP alerts on rule-matching changes |
| 120 | 120 | |
| 121 | 121 | ### v1 packaging |
| 122 | 122 | - [ ] `install.sh` -- interactive installer (asks for MySQL creds or falls back to SQLite) |
| 123 | 123 | - [ ] systemd unit files for the two daemons |
| 124 | 124 | - [ ] `/etc/cron.d/` entry for auto-purge |
| 125 | 125 | - [ ] Landing page |
| 126 | 126 | - [ ] Documentation site |
| 127 | 127 | |
| 128 | 128 | ### v2 -- Container awareness |
| 129 | 129 | - [ ] Docker API integration (enumerate containers, map to overlay2 dirs) |
| 130 | 130 | - [ ] OverlayFS `upper` reader -- captures inside-container file changes from host |
| 131 | 131 | - [ ] Container drift baselines + alerts |
| 132 | 132 | - [ ] Docker Compose config monitoring |
| 133 | 133 | - [ ] `docker-compose.yml` and `Dockerfile` change capture |
| 134 | 134 | |
| 135 | 135 | ### v2 -- Multi-server |
| 136 | 136 | - [ ] Agent binary (lightweight, calls the same OverlayFS + FS walker) |
| 137 | 137 | - [ ] Agent registration flow (installer generates HMAC secret + agent config) |
| 138 | 138 | - [ ] `/api/ingest_file.cgi`, `/api/ingest_sql.cgi`, `/api/heartbeat.cgi` -- HMAC-signed endpoints |
| 139 | 139 | - [ ] Central dashboard filters by server / container / cluster |
| 140 | 140 | |
| 141 | 141 | ### v3 -- Kubernetes + Enterprise |
| 142 | 142 | - [ ] K8s DaemonSet install (one pod per node, reads that node's overlay2) |
| 143 | 143 | - [ ] K8s API integration (watch ConfigMaps, Secrets, Deployments as first-class objects) |
| 144 | 144 | - [ ] Cross-environment aggregation ("this config changed in staging AND prod within 24h") |
| 145 | 145 | - [ ] Compliance reporting bundle (SOX/HIPAA/PCI-DSS/SOC 2 audit trail exports) |
| 146 | 146 | |
| 147 | 147 | ## Install (targeted UX) |
| 148 | 148 | |
| 149 | 149 | ```sh |
| 150 | 150 | $ curl -sSL https://driftsense.com/install.sh | sudo bash |
| 151 | 151 | [?] MySQL detected. Use existing MariaDB at localhost? [Y/n] |
| 152 | 152 | [?] MySQL user for DriftSense (will be created if missing): drift_sense |
| 153 | 153 | [?] Web UI port: [8080] |
| 154 | 154 | [?] Auto-configure common watch paths (/var/www, /etc, /etc/cron.d)? [Y/n] |
| 155 | 155 | |
| 156 | 156 | Installing systemd units... done |
| 157 | 157 | Applying schema... done |
| 158 | 158 | Starting drift_sense_schema.service... done |
| 159 | 159 | Starting drift_sense_files.service... done |
| 160 | 160 | |
| 161 | 161 | DriftSense is running. |
| 162 | 162 | UI: http://localhost:8080/ |
| 163 | 163 | Log: journalctl -u drift_sense_* -f |
| 164 | 164 | |
| 165 | 165 | First captured change should appear in the UI within 60 seconds. |
| 166 | 166 | ``` |
| 167 | 167 | |
| 168 | 168 | Target: `install.sh -> first captured change visible in the UI < 5 minutes.` |
| 169 | # canary | |
| 170 | # canary again |
Confirm restore
Backs current content to <target>.drift_restore_backup_<epoch>, writes the captured content, verifies SHA post-write.