____ _ _ ____ _
/ ___| _ __ __ _ _ __ | \ | / ___|| |_ ___ _ __ ___
\___ \ | '_ \ / _` || '_ \| \| \___ \| __|/ _ \ | '__|/ _ \
___) || | | || (_| || |_) | |\ |___) | |_| (_) || | | __/
|____/ |_| |_| \__,_|| .__/|_| \_|____/ \__|\___/ |_| \___|
|_|
SnapNStore is a local snapshot backup tool written in C. It stores content-addressed, zstd-compressed chunks in a vault, tracks snapshot metadata in SQLite, and restores complete snapshots or selected paths without requiring a network service.
The snap CLI is intended for local-first backup workflows, scripted
maintenance, offline exports, and interactive inspection through an ncurses TUI.
- Durable local vaults with SQLite metadata and content-addressed object storage.
- Content-defined chunking for deduplication across shifted file contents.
- zstd compression for stored chunk payloads.
- Optional local encryption with PBKDF2-HMAC-SHA256 and AES-256-GCM.
.snapignoresupport for pruning source paths before snapshotting.- Full and selected-path restores with dry-run planning and conflict reports.
- Snapshot diff, status, history, find, tag, note, pin, retention, prune, stats, verify, doctor, export, import, and garbage collection commands.
- JSON output for automation on supported commands.
- Stable process exit codes for scripts and schedulers.
- ncurses TUI for browsing snapshots, manifests, diffs, restore previews, policy previews, verification summaries, and GC previews.
Build dependencies:
- C11 compiler
make- SQLite3 development headers and library
- zstd development headers and library
- OpenSSL libcrypto development headers and library
- ncurses development headers and library
On macOS, the Makefile automatically adds Homebrew zstd include and library
paths when /opt/homebrew exists.
make
./snap --versionThe default target builds the snap binary in the repository root.
make install PREFIX=/usr/local
make uninstall PREFIX=/usr/localmake install copies:
snapto$(PREFIX)/binsnap(1)to$(PREFIX)/share/man/man1- bash, zsh, and fish completions under the selected prefix
Use DESTDIR= for staged package builds:
make install PREFIX=/usr DESTDIR=/tmp/snap-packagesnap init /var/backups/snap-vault
snap snapshot /var/backups/snap-vault /home/alice/project --name "initial"
snap list /var/backups/snap-vault
snap verify /var/backups/snap-vault --deep
snap restore-plan /var/backups/snap-vault snap_0001 --to /tmp/project-restore
snap restore /var/backups/snap-vault snap_0001 --to /tmp/project-restoreFor a disposable local demo:
make demomake demo builds the binary, creates a throwaway source tree and vault, stores
two snapshots, and opens the TUI.
For a guided first production setup, see
docs/MY_FIRST_VAULT.md.
snap [--verbose] [--quiet] [--json] <command> [args] [options]Global flags may appear before the command:
| Flag | Description |
|---|---|
--json |
Emit JSON for commands that support structured output. |
--quiet |
Suppress success and progress messages while preserving errors and command results. |
--verbose |
Add diagnostic status and exit-code details when a command fails. |
--help, -h |
Print the command list. |
--version, -v |
Print the CLI version. |
--verbose and --quiet cannot be used together. Unsupported --json
commands keep their normal text output.
| Command | Purpose | Command flags |
|---|---|---|
version |
Print version information. | --json |
explain <exit-code> |
Explain a public process exit code. | --json |
init <vault> |
Create a vault. | --encrypted |
snapshot <vault> <source> |
Capture a source tree. | --name <name>, --note <text> |
list <vault> |
List snapshots. | --tag <tag> |
show <vault> <snapshot> |
Show snapshot metadata and manifest entries. | --files |
diff <vault> <old> <new> |
Compare two snapshots. | none |
status <vault> <source> |
Compare a source tree to its newest matching snapshot. | none |
doctor <vault> |
Print a local health summary. | none |
restore-plan <vault> <snapshot> |
Preview a restore without writing files. | --path <snapshot_path>, --to <output_path> |
restore <vault> <snapshot> |
Restore a full snapshot or selected path. | --path <snapshot_path>, --to <output_path>, --force, --dry-run |
verify <vault> |
Verify metadata and object integrity. | --deep |
delete-snapshot <vault> <snapshot> |
Delete unpinned snapshot metadata. | --yes |
tag <vault> <snapshot> <tag> |
Attach a case-sensitive tag. | none |
note <vault> <snapshot> <text> |
Replace a snapshot note. | none |
pin <vault> <snapshot> |
Protect a snapshot from deletion and prune. | none |
unpin <vault> <snapshot> |
Remove snapshot protection. | none |
policy show <vault> |
Show the saved retention policy. | --json |
policy set <vault> |
Save a vault-local retention policy. | --keep-last <count>, --keep-daily <count>, --keep-weekly <count>, --keep-monthly <count> |
prune <vault> |
Apply retention rules to snapshot metadata. | --keep-last <count>, --keep-daily <count>, --keep-weekly <count>, --keep-monthly <count>, --policy, --dry-run, --yes |
find <vault> <pattern> |
Search stored manifest paths. | --snapshot <snapshot_id>, --tag <tag> |
history <vault> <path> |
Track one path across snapshots. | none |
stats <vault> |
Show storage analytics. | --top-files, --chunks |
gc <vault> |
Reclaim unreferenced object data. | --dry-run |
export <vault> |
Copy durable vault data to an export directory. | --to <export_path> |
import <vault> <export> |
Create a new vault from an export. | none |
mount <vault> <snapshot> <mount_path> |
Request a read-only snapshot mount. | none |
tui <vault> |
Open the ncurses interface. | none |
Full command behavior and JSON shapes are documented in
docs/COMMANDS.md. The installed manual page is available
with man snap.
Create a vault once:
snap init /srv/backups/snap-vaultCreate an encrypted vault when local at-rest protection is required:
snap init /srv/backups/private-vault --encryptedCapture snapshots with explicit names and optional notes:
snap snapshot /srv/backups/snap-vault /srv/app --name "pre-deploy" --note "before 2026-06 release"
snap snapshot /srv/backups/snap-vault /srv/app --name "daily-$(date +%F)"Preview restores before writing files:
snap restore-plan /srv/backups/snap-vault snap_0008 --to /tmp/app-restore
snap restore-plan /srv/backups/snap-vault snap_0008 --path config/app.toml --to /tmp/app.tomlRestore only after the plan is acceptable:
snap restore /srv/backups/snap-vault snap_0008 --to /tmp/app-restore
snap restore /srv/backups/snap-vault snap_0008 --path config/app.toml --to /tmp/app.tomlVerify and inspect health regularly:
snap doctor /srv/backups/snap-vault
snap verify /srv/backups/snap-vault --deep
snap stats /srv/backups/snap-vault --top-files --chunksUse retention previews before deletion:
snap policy set /srv/backups/snap-vault --keep-last 24 --keep-daily 14 --keep-weekly 8 --keep-monthly 12
snap prune /srv/backups/snap-vault --policy --dry-run
snap prune /srv/backups/snap-vault --policy --yes
snap gc /srv/backups/snap-vault --dry-run
snap gc /srv/backups/snap-vaultExport durable vault state for offline movement or secondary storage:
snap export /srv/backups/snap-vault --to /mnt/archive/snap-vault-export
snap import /srv/backups/restored-vault /mnt/archive/snap-vault-exportUse --json before the command for machine-readable output:
snap --json list /srv/backups/snap-vault
snap --json verify /srv/backups/snap-vault --deep
snap --json prune /srv/backups/snap-vault --policy --dry-runCommands with JSON support include version, explain, list, show,
diff, status, doctor, restore-plan, restore dry-runs, verify,
gc --dry-run, policy show, policy set, prune, find, history, and
stats.
Stable process exit codes:
| Code | Meaning |
|---|---|
0 |
Success |
1 |
User, input, authentication, not-found, or already-exists error |
2 |
I/O error |
3 |
Database error |
4 |
Vault corruption |
5 |
Internal or unknown error |
Explain an exit code from a script:
snap explain 4
snap explain 4 --jsonsnap init creates:
<vault>/
├── config.toml
├── metadata.db
├── objects/
├── tmp/
└── locks/
Durable state lives in config.toml, metadata.db, and objects/. The
tmp/ and locks/ directories are local working areas and are recreated by
snap import.
Snapshots record regular files, directories, and symbolic links. Symlinks are stored as manifest entries and are not followed during scanning. Regular files are split into content-defined chunks, hashed by raw SHA-256, compressed with zstd, optionally encrypted, and stored under their raw content address.
The on-disk format is documented in
docs/STORAGE_FORMAT.md. Implementation notes live
in docs/DESIGN.md.
If <source>/.snapignore exists, snapshot scanning supports:
- Exact names or root-relative paths, such as
.envorbuild/cache.db - Directory prefixes ending in
/, such asbuild/ - Suffix globs beginning with
*, such as*.o - Comment lines beginning with
# - Empty lines
Ignored directories are pruned before traversal. .snapignore itself remains
part of the snapshot unless a rule excludes it.
Encrypted vaults are local-only:
snap init /srv/backups/private-vault --encryptedSnapNStore stores KDF metadata and a key verifier, not the passphrase or derived key. Snapshot, restore, verify, and doctor prompt when an encrypted vault must be unlocked. There is no remote unlock, recovery service, or background key agent. Losing the passphrase makes the encrypted vault unrecoverable.
- Run
restore-planorrestore --dry-runbefore restoring into important paths. restorerefuses an existing destination unless--forceis provided.delete-snapshotand confirmedpruneremove metadata only; rungclater to reclaim unreferenced object files.- Pinned snapshots are protected from
delete-snapshotandprune. pruneis non-destructive unless--yesis provided.gc --dry-runreports reclaimable data before deletion.mountis a capability stub in the portable default build; a platform-specific FUSE backend is required for real mounts.
make check
make fmt
make fmt-check
make analyze
make test
make test-asan
make test-fixture
make clean-test-vaultsmake check is the local quality gate. It runs formatting verification, static
analysis, the normal test suite, and the sanitized test suite. More detail is
available in docs/TESTING.md.
SnapNStore is released under the MIT License. See LICENSE.