With gpg --decrypt x.tar.gpg | vault absorb, vault printed its prompt
while gpg was still writing to the terminal, which buried it, so the
command looked hung. Peek at standard input first: the upstream program
has finished with the terminal by the time data arrives, and an empty
pipe now fails without asking at all.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
shots.py now shoots absorb (a gzipped tarball and a tar pipe) and mirror
(a small site served on localhost). The locking section and the cheat
sheet list migrate, mirror and absorb, and the man pages.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Single-threaded, same host only, Chrome user agent. Links in HTML and
CSS are rewritten to relative paths. Includes a man page and a guide note.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
shots.py gains a pseudo-terminal runner so it can answer migrate's
overwrite question, and the guide shows the resulting screenshot.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
add now downloads http(s) URLs (with a Chrome user agent) through a
private temporary file, and treats ".", ".." and "/" as "this folder's
contents" so the destination folder is no longer dropped for "..".
migrate is add followed by deleting each original and any directories
left empty. It asks on the terminal before overwriting anything already
in the vault, refusing when there is no terminal, unless --yes is given.
Adding or migrating a directory skips the vault and key file if they
live inside it, so migrate can never ingest or delete them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
File bodies are now cut into 4 MiB objects, the last padded to its
power-of-two bucket, so every object is 64 KiB to 4 MiB whatever the file
size. Object IDs are HMAC'd from a secret per-record seed, so nothing on
disk links a file's pieces, and each object still leads with its own ID,
binding it to its record and position. Chunks are encrypted and synced by
a small worker pool so their fsyncs overlap.
records/ and objects/ are sharded into 256 subdirectories by ID prefix,
all created at init, with every shard's mtime pinned to the epoch.
vault init --filler SIZE writes at least SIZE of filler. Filler entries
are now real entries for a throwaway key, of log-uniform random sizes, so
they carry multi-chunk files like a real collection.
The marker becomes VAULT 2; earlier vaults are not supported.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A LaTeX field guide covering install, keys, everyday commands, the
decoy passphrase, scripting, locking and sync, internals, troubleshooting
and a cheat sheet. build-pdf.sh compiles it with a pinned Tectonic,
downloaded on first use. shots.py regenerates every screenshot from the
real vault binary in a sandbox home.