A cross-platform file time machine. Snapback runs quietly in the system tray, takes a deduplicated snapshot every time you save a file, lets you scrub a visual daily timeline, and restores any earlier version with one click, including files you have already deleted from disk.
Everything stays on your machine. Optional per-scope AES-256-GCM encryption of stored blobs is available.
- True cross-platform desktop app: Windows, macOS and Linux from one Electron codebase.
- Snapshot-on-save backed by a content-addressed blob store with BLAKE3 hashing, so identical content across files and versions costs zero extra bytes.
- Cascading rules engine. A global default, per-folder overrides and per-file-type or glob overrides on top.
- Time-based retention. A configurable keep-everything window, then automatic thinning into hourly, daily and weekly buckets, then hard delete after a max age. Per-scope size cap is also supported.
- Whole-machine mode that intelligently skips system directories and dotfiles
on each OS (for example
~/Libraryon macOS,AppDataon Windows,.cache,node_modules,.giteverywhere). - Visual daily timeline plus a recovery view for files that no longer exist on disk.
- Autostart on login so the daemon comes back automatically after reboot. Uses XDG autostart on Linux, the Electron login items API on macOS and Windows.
End users do not need Node.js, Visual Studio, Xcode or any compilers. Just grab the installer for your OS from the Releases page and double-click it like any other app.
- Download
Snapback-<version>-setup.exefrom the latest release. - Double-click it. Windows SmartScreen will show "Windows protected your PC" because the installer is not yet code-signed. Click More info, then Run anyway.
- Follow the installer. By default Snapback installs per-user, creates a desktop shortcut, and adds itself to the Start menu.
- Launch Snapback from the Start menu. The first run shows the setup wizard; after that it lives in the system tray near the clock.
To uninstall: Settings > Apps > Snapback > Uninstall.
- Download the matching dmg from the latest release:
Snapback-<version>-arm64.dmgfor Apple Silicon Macs (M1/M2/M3/M4).Snapback-<version>-x64.dmgfor Intel Macs.
- Open the dmg and drag Snapback into the Applications folder.
- The first time you launch it, macOS Gatekeeper will say "Snapback can't
be opened because Apple cannot check it for malicious software". This
is expected for unsigned apps. To bypass:
- Easy way: Right-click (or Control-click) Snapback in Applications, choose Open, then click Open in the dialog. macOS remembers this choice; future launches just work.
- One-shot fix from Terminal:
xattr -dr com.apple.quarantine /Applications/Snapback.app
- Snapback lives in the menu bar after launch.
To uninstall: drag Snapback from /Applications to the Trash.
Two formats are published. Pick whichever fits your distro.
AppImage (any distro, no install needed):
chmod +x Snapback-<version>.AppImage
./Snapback-<version>.AppImageDebian / Ubuntu / Mint (.deb):
sudo dpkg -i Snapback-<version>.deb
sudo apt-get install -f # only if dpkg reports missing depsThe .deb installs into /opt/Snapback/ and adds a desktop entry, so
Snapback appears in your application launcher.
If the watcher logs ENOSPC: System limit for number of file watchers reached, bump the inotify limits once:
echo 'fs.inotify.max_user_watches=524288' | sudo tee -a /etc/sysctl.conf
echo 'fs.inotify.max_queued_events=131072' | sudo tee -a /etc/sysctl.conf
sudo sysctl -pThe installers are not yet signed with an Apple Developer ID or Windows code-signing certificate, so both OSes will warn you on first launch. The app itself is the same code you can read in this repo. Code signing is on the roadmap; until then the workarounds above are the way in.
You only need this if you want to compile your own installer, contribute code, or run with hot reload. Regular users should use the prebuilt installers above.
You need Node.js 18 or newer and a C/C++ toolchain so better-sqlite3 can
build its native binding:
- Linux:
sudo apt install build-essential python3(or your distro's equivalent). - macOS:
xcode-select --install. - Windows: install the Desktop development with C++ workload from
Visual Studio Build Tools,
plus Python 3.
npmwill pick them up automatically.
git clone https://github.com/walid-baharwal/snapback.git
cd snapback
npm install
npm run dev # Electron with HMR on the renderer
npm run typecheck # both tsconfig projects
npm run package # build an installer for the current OS onlyThe packaged installer lands in release/. On Linux you can also run the
unpacked binary directly:
./release/linux-unpacked/snapbackIf it aborts complaining about the SUID sandbox helper, either run it
with --no-sandbox or fix the helper permissions:
sudo chown root:root release/linux-unpacked/chrome-sandbox
sudo chmod 4755 release/linux-unpacked/chrome-sandboxPer-OS packaging targets are also available, but each one only runs on its own OS (native modules can't be cross-compiled):
npm run package:linux # AppImage + deb
npm run package:win # NSIS .exe
npm run package:mac # dmg (x64 + arm64)A push of a v* tag triggers
.github/workflows/release.yml, which
builds Snapback on ubuntu-latest, windows-latest and macos-latest
in parallel and uploads every artifact (.exe, two .dmg files,
AppImage, .deb, plus the latest*.yml update metadata) to a GitHub
Release matching the tag.
To cut a release:
# Bump the version in package.json first, commit it, then:
git tag v0.1.0
git push origin v0.1.0Watch the run under the
Actions tab. When
all three jobs are green, the release with the installers is live at
https://github.com/walid-baharwal/snapback/releases/tag/v0.1.0.
src/
main/ Electron main process (background daemon).
index.ts App entry point, window + tray lifecycle.
tray.ts System tray menu (pause/resume, quit).
db/
database.ts SQLite (better-sqlite3) connection.
schema.ts Table definitions and migrations.
storage/
blobStore.ts Content-addressed blob store (BLAKE3 hashing).
storageEngine.ts Snapshot capture, dedupe, content reads.
textStats.ts Line/word/char counts, binary detection.
services/
watcher.ts chokidar based file watcher.
rulesEngine.ts Cascading rule resolution (global -> folder -> type).
retention.ts Hourly thinning, hard delete, size cap, blob GC.
scopesRepo.ts Scope CRUD against SQLite.
preferences.ts JSON-backed user preferences.
encryption.ts Optional AES-256-GCM blob encryption.
dryRun.ts Estimate matched files + storage cost for a scope.
linuxAutostart.ts Writes ~/.config/autostart/snapback.desktop.
ipc/
handlers.ts Every IPC channel implementation.
preload/
index.ts contextBridge exposing window.api to the renderer.
renderer/
src/
App.tsx Top-level page router.
components/
Shell.tsx Sidebar + topbar layout.
Diff.tsx Simple line-level diff viewer.
Heatmap.tsx GitHub-style daily activity heatmap.
Pagination.tsx Reusable client-side pagination.
pages/
Setup.tsx First-run wizard (folders vs whole machine).
Timeline.tsx Daily timeline of file changes.
FileHistory.tsx Versions of a single file.
Recovery.tsx Search and restore deleted files.
RulesManager.tsx Scopes + rule editor (drag-resizable panel).
Storage.tsx Disk usage per scope, "Run retention now".
Settings.tsx App preferences.
lib/
format.ts, useThrottle.ts
shared/
types.ts Cross-process types (IPC payloads, scope shape).
ipcChannels.ts Channel name constants used by both sides.
defaults.ts Default global rules, common excludes, OS excludes.
electron.vite.config.ts Vite config for main, preload and renderer.
electron-builder.yml Installer config (NSIS, dmg, AppImage, deb).
build/after-install.sh Linux post-install hook.
tsconfig.{node,web}.json Separate TS projects for backend vs renderer.
The main process talks to the renderer only through the channels declared in
src/shared/ipcChannels.ts. The shape of every payload lives in
src/shared/types.ts, so the renderer never imports anything from main/
directly. This separation is what keeps the security boundary clean
(sandbox: false, contextIsolation: true, nodeIntegration: false).
- Fork the repo and create a feature branch.
- Run
npm install, thennpm run devto develop with hot reload on the renderer. - Before committing run
npm run typecheck. Both projects must pass. - Keep the IPC surface small. New features should add a channel to
shared/ipcChannels.ts, types toshared/types.ts, a handler inmain/ipc/handlers.tsand an entry inpreload/index.ts. - New main-process work that can block (file IO, hashing, walks) belongs in
a service under
src/main/services/. Keepmain/index.tsskinny. - UI changes should respect the Tailwind tokens already defined in
tailwind.config.cjs(background, border, text, accent, success, warn, danger). Avoid hard-coded colors. - Open a pull request describing what changed and how to reproduce. Include screenshots for renderer changes.
- TypeScript strict everywhere. No
anywithout a comment explaining why. - Prefer pure functions in services. Side effects belong at the edges (handlers, watcher, scheduler).
- Comments should explain non-obvious intent or constraints, not narrate the code.
<userData>/snapback/snapback.dbis the SQLite index.<userData>/snapback/blobs/<aa>/<hash>stores content-addressed blobs.<userData>/snapback/enc.keyis the generated AES-256 key (mode 0600).<userData>/snapback/preferences.jsonis the user preferences file.
<userData> resolves to ~/.config/snapback/ on Linux,
~/Library/Application Support/Snapback/ on macOS, and
%APPDATA%\Snapback\ on Windows.
To reset Snapback completely, quit it (tray menu) and delete the
<userData>/snapback directory.
.env,*.pem, SSH keys and other secret patterns are excluded by default.- Binary files are skipped (size only on the
everythingpreset). - Per-folder
.gitignoreis respected on the Code preset. - A max file size cap (5 MB global, 10 MB per scope by default) prevents huge files from overwhelming the index.
- The actual files on your disk are never written to. Retention only prunes Snapback's snapshot copies, never your live files.
MIT. See LICENSE (or package.json) for details.