From 2823d93cc3bfb518c37a1b00bc74532976c1d96b Mon Sep 17 00:00:00 2001 From: sneak Date: Sun, 4 Oct 2026 13:08:11 +0000 Subject: [PATCH] Give every new unlocker a directory of its own (closes #71) A passphrase unlocker added to a vault that had one, and a PGP, keychain or Secure Enclave unlocker added on the same day as another of its type, were written into the existing unlocker's directory file by file, so a crash part-way left a current unlocker whose files did not belong together. Unlocker directories, keychain items and Secure Enclave keys are now named with the time to the nanosecond, and secret.WriteDir refuses a directory that exists. Adding a passphrase unlocker writes the new one, points current-unlocker at it, and only then removes the vault's other passphrase unlockers. Model: opus-5-5 --- README.md | 9 +- TODO.md | 26 +++-- internal/cli/integration_test.go | 19 ++-- internal/cli/path_traversal_test.go | 2 +- internal/secret/atomic.go | 16 +-- internal/secret/atomic_test.go | 154 ++++++++++++++++++++++++--- internal/secret/constants.go | 6 ++ internal/secret/keychainunlocker.go | 6 +- internal/secret/pgpunlocker.go | 9 +- internal/secret/pgpunlocker_test.go | 36 +++++++ internal/secret/seunlocker_darwin.go | 4 +- internal/vault/unlockers.go | 73 +++++++++++-- 12 files changed, 293 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index a50be3c..da57fcd 100644 --- a/README.md +++ b/README.md @@ -197,6 +197,9 @@ Creates a new unlocker of the specified type: **Options:** - `--keyid `: GPG key ID (optional for PGP type, uses default key if not specified) +A vault has one passphrase unlocker: adding one replaces the one the vault +has, which is removed only once the new one is the current unlocker. + #### `secret unlocker remove [--force]` / `secret unlocker rm` ⚠️ 🛑 **DANGER**: Permanently removes an unlocker. Like Unix `rm`, this command @@ -243,8 +246,8 @@ Decrypts data using an Age key stored as a secret. ├── vaults.d/ │ ├── default/ │ │ ├── unlockers.d/ -│ │ │ ├── passphrase/ # Passphrase unlocker -│ │ │ └── pgp/ # PGP unlocker +│ │ │ ├── passphrase-