Give every new unlocker a directory of its own (closes #71)
check / check (push) Failing after 2s

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
This commit is contained in:
2026-10-04 13:11:35 +00:00
parent 62967f28d0
commit 2823d93cc3
12 changed files with 293 additions and 67 deletions
+17 -9
View File
@@ -25,6 +25,22 @@ Bring the repo into policy compliance in one commit:
# Completed Steps
- 2026-10-04: A crash while an unlocker is being replaced no longer
leaves a current unlocker that cannot open the vault
(https://git.eeqj.de/sneak/secret/issues/71). Every new unlocker gets
a directory of its own, named with the time to the nanosecond:
`passphrase-<time>`, `<host>-pgp-<time>`, and for a keychain or Secure
Enclave unlocker the keychain item or Secure Enclave key, which names
the directory, carries the time instead of the day. `secret.WriteDir`
fails on a directory that exists instead of writing into it.
`unlocker add passphrase` writes the new unlocker, makes it current,
and only then removes the vault's other passphrase unlockers; a crash
between the last two steps leaves the old one beside the new, and the
old passphrase still opens the vault through it until the next
`unlocker add passphrase` or an `unlocker remove` removes it. A PGP,
keychain or
Secure Enclave unlocker added on the same host and day as another of
its type is added beside it instead of replacing it.
- 2026-10-04: `secret unlocker add pgp` works on Linux
(https://git.eeqj.de/sneak/secret/issues/88). `CreatePGPUnlocker` gets
the vault's long-term key as adding a passphrase unlocker does, with the
@@ -59,9 +75,7 @@ Bring the repo into policy compliance in one commit:
and encrypt everything before writing anything. All four unlocker
types write their files through `secret.WriteDir`: a new unlocker is
built in a temporary directory, renamed into place when complete and
removed on a failure. One added under the directory name of an
existing unlocker is still written into that directory in place
(https://git.eeqj.de/sneak/secret/issues/71).
removed on a failure.
- 2026-10-04: `secret unlocker select` and `secret unlocker remove`
skip, with the warning `unlocker list` gives, an unlocker directory
whose metadata file cannot be checked for, read or parsed, instead of
@@ -157,12 +171,6 @@ Bring the repo into policy compliance in one commit:
into place, and removals rename out of the way first, so a version
or secret is never half-added and never half-removed. An
interrupted command can still leave:
- a broken unlocker, when it was replacing one: an unlocker added
under the directory name of an existing one is rewritten file by
file. That happens to a passphrase unlocker added to a vault that
has one, and to a PGP, keychain or Secure Enclave unlocker added
on the same host and day as another of its type
(https://git.eeqj.de/sneak/secret/issues/71);
- from `init` or `vault create` killed after the passphrase prompt
but before the unlocker is written, a vault with no unlocker,
which `vault create` has already made the current vault;