Documentation
Getting started
Everything below applies to 0.9.x. Commands are stable within the 0.9 line; flags marked experimental may change before 1.0.
Install
Release archives contain a single static binary. Verify the checksum, then move it onto your path:
curl -sSLO https://berkas.zanity.net/dl/haven-0.9.4-linux-amd64.tar.gz
curl -sSLO https://berkas.zanity.net/dl/haven-0.9.4-SHA256SUMS
sha256sum -c --ignore-missing haven-0.9.4-SHA256SUMS
tar xzf haven-0.9.4-linux-amd64.tar.gz
install -m 0755 haven /usr/local/bin/haven
Building from source needs Go 1.22 or newer and nothing else:
git clone https://berkas.zanity.net/git/haven.git
cd haven && make build # writes ./out/haven
Concepts
Three things are worth understanding before using the CLI.
Chunks
Files are split at content-defined boundaries using a rolling hash, with a 64 KiB target chunk size. Inserting a byte near the start of a large file shifts one or two chunk boundaries rather than invalidating everything after it. Each chunk is identified by its BLAKE3 digest.
Store
A store is a directory of chunks shared by many archives. Pass
--store to reuse it across builds; that is where deduplication
happens. Without a store, each archive is self-contained and dedupes only against
itself.
Manifest
Each archive begins with a manifest listing paths, modes, sizes and the chunk digests that make up each file. The manifest itself is hashed, so a single digest pins the entire tree.
Commands
create
haven create [--store DIR] [-o FILE] [--exclude GLOB]... PATH
Packs PATH into an archive. Repeat --exclude as needed;
patterns are matched against paths relative to PATH. With
-o - the archive is written to stdout.
extract
haven extract [--into DIR] [--strip N] FILE
Restores an archive. Refuses to overwrite existing files unless
--force is given. Paths escaping the destination directory are
rejected outright.
verify
haven verify [--deep] FILE
Checks the manifest digest and every chunk digest. --deep also
re-chunks file contents to confirm the boundaries match what the manifest claims.
Slower, but catches a corrupt store.
list
haven list [--long] [--json] FILE
Prints archive contents without extracting. --json emits one object
per line for piping into jq.
gc
haven gc --store DIR --keep FILE...
Deletes chunks in the store that are unreachable from the archives listed with
--keep. Always dry-runs first; add --commit to actually
remove. Losing a chunk that some other archive needs makes that archive
unextractable, so be explicit about what you keep.
inspect
haven inspect [--chunks] FILE
Dumps header fields, format version and manifest statistics. Mostly useful when filing a bug report.
Exit codes
0— success1— usage error2— I/O error3— integrity failure (digest mismatch)4— unsupported format version
Using it in CI
Keep the store on a cache volume that survives between runs, and key the archive name on the commit:
haven create --store "$CACHE_DIR/hv-store" \
--exclude '*.map' --exclude 'node_modules/**' \
-o "artifacts/$CI_COMMIT_SHORT_SHA.hv" ./dist
haven gc --store "$CACHE_DIR/hv-store" \
--keep artifacts/*.hv --commit
Run gc after pruning old artifacts, not before — the
--keep list is the source of truth for what stays reachable.
Limitations
- No encryption. Pipe through
ageor similar if you need it. - No compression of individual chunks yet; planned for 0.10.
- Hard links are stored as separate files. Symlinks are preserved as symlinks.
- Extended attributes and ACLs are not captured.
- Windows is not supported and is not on the roadmap.