Filesystem basics: mv now moves folders as well as files, and like mv(1) a NEW ending in / or naming an existing folder means into it. cp copies a file, or a folder with -r, by writing new records that share the original's objects, so it is instant and takes no space; rm already keeps objects another record still uses. New records go first, so an interrupted move leaves both names, never neither. get -r extracts a folder, keeping its structure. vault unlock [--for 5m] checks the passphrase and starts the binary again as a hidden __agent process, handing it the suite ID and seed over a pipe. The agent keeps them in mlocked memory, with core dumps off and (on Linux) PR_SET_DUMPABLE cleared, serves them on a Unix socket in a 0700 per-user directory whose owner is checked on every use, and wipes them and exits at a hard deadline or on vault lock. Commands ask it first unless VAULT_PASSPHRASE is set, and genkey stops any agent left for that path. Guide, man pages (new vault-cp, vault-unlock, vault-lock), README and screenshots updated; the guide's error table is split so it fits.
3.4 KiB
3.4 KiB
CLAUDE.md
This file provides guidance to Claude Code when working with this repository.
Overview
vault is a post-quantum encrypted file vault: one package main at the repo
root that builds a single vault binary. Module github.com/andydixon/vault.
main.go- CLI:genkey,init,add(file, directory or URL),migrate(add, then delete originals),mirror,absorb(tar on stdin,absorb.go),ls(one folder level),list(recursive),get(-rfor a folder),mv/cp(files or folders;transfersresolves names like mv(1), copies share objects),rm(-rfor a folder, asks unless-f),unlock/lock.agent*.go-vault unlockre-executes itself as a hidden__agentprocess holding the suite ID and seed (pipe in, mlocked, undumpable) and serving them on a Unix socket in a 0700 per-user dir until a hard deadline.keyIdentityasks it first unless$VAULT_PASSPHRASEis set.keyfile.go- key file with two passphrase slots (Argon2id + XChaCha20-Poly1305 sealing a suite ID and 32-byte master seed). The second slot is either a decoy identity or random bytes; slot order is random.store.go- on-disk vault:records/holds fixed-size encrypted catalogue records,objects/holds file bodies cut into 4 MiB chunks (last one bucket-padded) whose IDs are HMAC'd from a per-record seed. Both are sharded into 256xx/subdirectories by ID prefix. Records a key cannot open are skipped, not treated as errors.lock*.go- vault lock onDIR/.lock: fcntl on unix, LockFileEx on Windows, an exclusive lock file elsewhere. Writers lock exclusively, readers shared, taken after the passphrase prompt.docs/guide- LaTeX user guide (build-pdf.shvia Tectonic;shots.pyre-shoots screenshots from the real binary). Keep it in step with the CLI.build.sh- cross-compiles everygo tool dist listtarget except iOS and android/386, amd64, arm (cgo-only).internal/pqcrypt- file encryption (header + STREAM ChaCha20-Poly1305), suite registry, hybrid FrodoKEM-640 + ML-KEM-768 default suite.internal/format,internal/stream,internal/term- header encoding, chunked payload encryption, terminal passphrase prompts.
Commands
go test ./...
go build .
./build.sh # all platforms, into ./dist
TARGETS="linux/amd64" ./build.sh # a subset
End-to-end check:
export VAULT_PASSPHRASE=test VAULT_KEY=$PWD/k VAULT_DIR=$PWD/s
./vault genkey && ./vault init && ./vault add README.md docs && ./vault list
Invariants
- Never write a seed or private key unencrypted (the agent gets it by pipe).
- Keep suite IDs, stanza tags, HKDF info strings and
fileKeySize(32) stable. - Every ciphertext's and shard directory's mtime is pinned to the epoch; records stay one fixed size; objects stay at most 4 MiB and bucket-padded. These hide metadata from the storage host.
- An object's plaintext starts with its own ID, binding it to its record and its position in the file.
getpublishes output only after every object has authenticated.- Decoy deniability: both key slots stay the same size and are both always
tried;
initalways writes filler; foreign records are skipped silently. - Every command that changes the vault holds the exclusive lock.
- Keep dependencies pure Go and CGO_ENABLED=0 builds working.
- Diagnostics go to standard error, lower-case, no trailing full stops.