README
¶
locsnap
locsnap is a CLI that continuously snapshots a codebase while agents or tools modify it. It observes the filesystem from the outside, writes snapshots to an isolated .locsnap/ store, and never touches the repository's normal Git index, refs, or history.
Status: Stable for local timeline capture and recovery. Debouncing captures states that persist through the configured quiet period, not every transient write. Keep an independent off-device backup for protection from disk loss, repository deletion, or host compromise.
Features
- Recursive filesystem watching with
fsnotify - Debounced snapshots for bursty writes and atomic-save workflows
- Periodic reconciliation scans that recover persistent changes after missed watcher events
- One serialized writer per stream, with streams running in parallel
- Cross-process writer and maintenance locking to prevent competing writers and GC/read races
- Durable object and ref writes using file and directory synchronization
- Crash-safe commit ordering with rebuildable indexes
- Persistent health reporting for watcher and snapshot failures
- Isolated
.locsnap/Git-style object store - Git-compatible blobs, nested trees, commits, and stream refs
- Content-addressed deduplication
- Optional alternates pointing at
.git/objects - bbolt
index.dbfor fast timeline and metadata queries - Gitignore-compatible ignore matching
- Built-in ignores for
.locsnap,.git,node_modules,.venv,dist,build, and.next - Large-file skip threshold
- Snapshot restore to a target directory
- Basic retention and unreachable-object GC
Install
Install the latest release with curl:
curl -fsSL https://raw.githubusercontent.com/patriceckhart/locsnap/main/install.sh | sh
By default this installs locsnap to $HOME/.local/bin. To choose another directory:
curl -fsSL https://raw.githubusercontent.com/patriceckhart/locsnap/main/install.sh | LOCSNAP_INSTALL_DIR=/usr/local/bin sh
Install a specific release:
curl -fsSL https://raw.githubusercontent.com/patriceckhart/locsnap/main/install.sh | LOCSNAP_VERSION=0.1.0 sh
You can also install with Go:
go install github.com/patriceckhart/locsnap@latest
For local development:
go build -o locsnap .
Quick start
locsnap init
locsnap watch
In another terminal, edit files in the repository. locsnap watch will create snapshots whenever the tree reaches a stable state.
Open the built-in snapshot viewer while watching:
locsnap watch --ui
The UI opens in the default browser. It lists snapshots across all streams, previews captured files and changes from the parent snapshot, and restores a selected snapshot to a target directory.
View status:
locsnap status
List snapshots:
locsnap log main
Inspect a snapshot:
locsnap show <snapshot>
Restore a snapshot:
locsnap restore <snapshot> --to /tmp/restored-tree
If the target directory already exists and is not empty, pass --force. This merges the snapshot into the directory and leaves unrelated existing files in place:
locsnap restore <snapshot> --to /tmp/restored-tree --force
Built-in snapshot viewer
Start the watcher and viewer together:
locsnap watch --ui
locsnap starts an HTTP server on a random loopback port and opens the viewer in the default browser. The server stops when the watch process exits. UI assets are embedded in the executable, so no separate frontend installation or internet connection is required.
The viewer provides:
- A reverse-chronological timeline across all configured streams
- Snapshot search and stream filtering
- Capture time, trigger, changed-file count, and changed-byte metadata
- A searchable file tree for each snapshot
- Text-file previews with changes from the parent snapshot highlighted
- Safe handling of binary files without rendering them as text
- Automatic timeline refresh while new snapshots are captured
- Restore controls for the selected snapshot
To restore from the viewer:
- Select a snapshot.
- Click Restore snapshot.
- Enter a target directory.
- Enable Merge into a non-empty target only when existing content may be overwritten.
- Confirm the restore.
Restore behavior matches the CLI. Existing files with matching paths are replaced, while unrelated files remain in place. Restoring into .locsnap/ is always rejected.
UI security
- The server binds only to
127.0.0.1on a random port. - Restore requests require a cryptographically random token generated for that watch process.
- The token is delivered only to the locally served viewer.
- Browser security headers restrict scripts, network connections, framing, and referrer data.
- Request bodies are size-limited and strictly decoded.
- Restore paths use the same traversal and symlink protections as CLI restores.
The UI is available only while locsnap watch --ui is running. Use locsnap log, locsnap show, and locsnap restore when a browser is unavailable.
Commands
locsnap init
Creates .locsnap/, initializes the object store, writes .locsnap/config, sets up alternates when .git/objects exists, creates the bbolt index, and adds .locsnap/ to .gitignore.
locsnap add <dir> [--stream <id>]
Registers a watch root as a stream. Each watch root maps to one stream.
locsnap add . --stream main
locsnap add worktrees/agent-1 --stream agent-1
locsnap watch [--ui]
Starts watchers for all configured streams. Each stream has its own serialized writer queue, while streams run in parallel. Pass --ui to start the local snapshot viewer on a random loopback port and open it in the default browser. Restore requests require a per-process security token and cannot target .locsnap/.
locsnap status
Shows configured streams, their latest snapshots, latest snapshot time, store size, and the latest persisted health report. It can run while locsnap watch is active. Failures are also appended to .locsnap/errors.log so a later success cannot erase the failure history.
locsnap log [stream]
Lists snapshots in reverse chronological order. Defaults to main.
locsnap show <snapshot>
Shows snapshot metadata, a diff summary versus the parent snapshot when available, and the files in the snapshot.
locsnap restore <snapshot> --to <dir> [--force]
Materializes a snapshot into a target directory. The target must be empty unless --force is provided.
locsnap gc
Applies retention policy and removes unreachable objects from .locsnap/objects. GC performs a full integrity preflight and aborts without pruning if any reachable object is missing or corrupt.
locsnap version
Prints the release version, commit, and build date.
Configuration
Configuration lives in .locsnap/config.
Example:
[core]
repositoryformatversion = 0
filemode = true
bare = true
[locsnap]
debounce-ms = 200
scan-interval-ms = 5000
max-file-size = 10485760
use-alternates = true
self-copy-blobs = true
[stream "main"]
root = "."
[stream "agent-1"]
root = "worktrees/agent-1"
[retention]
keep-full-hours = 24
thin-to = "hourly"
max-store-bytes = 2147483648
Options
debounce-ms: quiet period after filesystem events before snapshotting.scan-interval-ms: periodic full reconciliation interval.max-file-size: files larger than this are skipped.0disables the limit.use-alternates: read existing objects from.git/objectswhen available.self-copy-blobs: always write blob copies into.locsnap/objects, even if found through alternates. New stores default totrueso snapshots remain durable if the repository's Git objects are pruned.keep-full-hours: keep all snapshots for this many hours before thinning.thin-to: currently supportshourlythinning.max-store-bytes: store-size cap used by retention.
Storage layout
.locsnap/
objects/ Git loose object store
objects/info/alternates Optional pointer to ../../.git/objects
refs/locsnap/streams/<id> Stream heads
index.db bbolt query index
lock.db cross-process writer lock
maintenance.db GC and read-operation exclusion lock
health.json latest operation or watcher health report
errors.log durable append-only failure history
config locsnap config plus Git-compatible core config
Git compatibility
Snapshots are Git commit objects stored in .locsnap/objects. You can inspect them with Git plumbing:
git --git-dir .locsnap cat-file -t <snapshot>
git --git-dir .locsnap ls-tree -r <snapshot>
locsnap does not use or modify the repository's normal Git index, branches, refs, or history.
Current limitations
- Symlinks and other non-regular files are skipped.
- Files larger than
max-file-sizeare omitted from snapshots. - Debouncing can coalesce transient intermediate states.
restore --forcemerges into its target rather than deleting unrelated files.- Retention can rewrite commit ancestry, so snapshot IDs removed or rewritten by
locsnap gcshould not be treated as permanent identifiers. - The shell installer supports Linux and macOS. Windows users can use
go installor a release archive.
Development
Run tests:
go test ./...
Format code:
gofmt -w .
Build:
go build -o locsnap .
License
MIT
Documentation
¶
There is no documentation for this package.