Skip to content

finn-abel/snapnstore

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

53 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

  ____                       _   _ ____  _
 / ___|  _ __    __ _  _ __ | \ | / ___|| |_  ___   _ __  ___
 \___ \ | '_ \  / _` || '_ \|  \| \___ \| __|/ _ \ | '__|/ _ \
  ___) || | | || (_| || |_) | |\  |___) | |_| (_) || |  |  __/
 |____/ |_| |_| \__,_|| .__/|_| \_|____/ \__|\___/ |_|   \___|
                      |_|

CI

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.

SnapNStore TUI showing the snapshot browser

Capabilities

  • 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.
  • .snapignore support 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.

Requirements

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.

Build

make
./snap --version

The default target builds the snap binary in the repository root.

Install

make install PREFIX=/usr/local
make uninstall PREFIX=/usr/local

make install copies:

  • snap to $(PREFIX)/bin
  • snap(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-package

Quick Start

snap 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-restore

For a disposable local demo:

make demo

make 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.

CLI

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.

Commands And Flags

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.

Production Workflow

Create a vault once:

snap init /srv/backups/snap-vault

Create an encrypted vault when local at-rest protection is required:

snap init /srv/backups/private-vault --encrypted

Capture 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.toml

Restore 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.toml

Verify 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 --chunks

Use 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-vault

Export 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-export

Automation

Use --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-run

Commands 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 --json

Vault Layout

snap 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.

Ignore Rules

If <source>/.snapignore exists, snapshot scanning supports:

  • Exact names or root-relative paths, such as .env or build/cache.db
  • Directory prefixes ending in /, such as build/
  • 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.

Encryption

Encrypted vaults are local-only:

snap init /srv/backups/private-vault --encrypted

SnapNStore 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.

Safety Notes

  • Run restore-plan or restore --dry-run before restoring into important paths.
  • restore refuses an existing destination unless --force is provided.
  • delete-snapshot and confirmed prune remove metadata only; run gc later to reclaim unreferenced object files.
  • Pinned snapshots are protected from delete-snapshot and prune.
  • prune is non-destructive unless --yes is provided.
  • gc --dry-run reports reclaimable data before deletion.
  • mount is a capability stub in the portable default build; a platform-specific FUSE backend is required for real mounts.

Development And Test

make check
make fmt
make fmt-check
make analyze
make test
make test-asan
make test-fixture
make clean-test-vaults

make 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.

License

SnapNStore is released under the MIT License. See LICENSE.

About

SnapNStore: A lightweight tool for capturing, organizing, and storing snapshots with a focus on simplicity, speed, and local-first control.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages