Add cp, folder mv, get -r, and unlock/lock to remember the key

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.
This commit is contained in:
Andy Dixon
2026-10-02 22:13:06 +01:00
parent 0d45ecea59
commit ec204f0ea2
31 changed files with 807 additions and 51 deletions
+8 -2
View File
@@ -10,7 +10,13 @@ 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`, `mv`, `rm` (`-r` for a folder, asks unless `-f`).
`get` (`-r` for a folder), `mv`/`cp` (files or folders; `transfers`
resolves names like mv(1), copies share objects), `rm` (`-r` for a
folder, asks unless `-f`), `unlock`/`lock`.
- `agent*.go` - `vault unlock` re-executes itself as a hidden `__agent`
process 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. `keyIdentity` asks it first unless `$VAULT_PASSPHRASE` is 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.
@@ -49,7 +55,7 @@ export VAULT_PASSPHRASE=test VAULT_KEY=$PWD/k VAULT_DIR=$PWD/s
## Invariants
- Never write a seed or private key unencrypted.
- 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.