Removing the current unlocker leaves the vault unable to open through its remaining unlocker #137

Closed
opened 2026-10-13 00:29:59 +02:00 by clawbot · 1 comment
Collaborator

Severity: major
After secret unlocker remove removes the vault's current unlocker, every command that opens the vault fails although another working unlocker remains, and the error sends the user to the mnemonic instead of to secret unlocker select. Nothing is lost; unlocker select <other-id> repairs it.

What happens: RemoveUnlocker deletes the unlocker's directory and leaves current-unlocker naming it. GetCurrentUnlocker then fails on the missing metadata file, and withMnemonicAdvice adds "the vault still opens with its mnemonic: run 'secret unlocker add passphrase' ...". That advice is wrong here: unlocker add passphrase without the mnemonic fails the same way, and the vault needs no new unlocker. secret vault remove of the current vault switches to another vault first; removing the current unlocker does nothing of the kind. The confirmation says only "It is not the vault's last unlocker."

Reproduction (a throwaway GPG key in a fresh GNUPGHOME):

SB_UNLOCK_PASSPHRASE=pw SB_SECRET_MNEMONIC="$M" secret init
echo -n hunter2 | SB_UNLOCK_PASSPHRASE=pw secret add foo
SB_UNLOCK_PASSPHRASE=pw secret unlocker add pgp --keyid "$FPR"   # becomes current
secret unlocker remove --force "$PGPID"                           # succeeds
SB_UNLOCK_PASSPHRASE=pw secret get foo
# Error: ... failed to get current unlocker: failed to read unlocker metadata: open .../unlockers.d/$PGPID/unlocker-metadata.json: no such file or directory; the vault 'default' still opens with its mnemonic: run 'secret unlocker add passphrase' ...
cat .../vaults.d/default/current-unlocker   # still $PGPID

Where:

func (v *Vault) RemoveUnlocker(unlockerID string) error {
(RemoveUnlocker),
func (cli *Instance) removeUnlocker(
(removeUnlocker),
func (v *Vault) withMnemonicAdvice(err error) error {
(withMnemonicAdvice).

Correct behaviour is a product decision the README does not settle. Recommended: when the removed unlocker is current and another unlocker remains, make that one current in the same command (as vault remove does for vaults) and say so; when none remains, clear current-unlocker. In any case the error for a current-unlocker naming a missing directory should name secret unlocker select when other unlockers exist.

Definition of done

  • Removing the current unlocker leaves the vault openable through a remaining unlocker without a further command, or the error after it names secret unlocker select.
  • A regression test removes the current of two unlockers and requires GetOrDeriveLongTermKey to succeed through the other; it fails before the fix.

Model: fable-5-1

Severity: major After `secret unlocker remove` removes the vault's current unlocker, every command that opens the vault fails although another working unlocker remains, and the error sends the user to the mnemonic instead of to `secret unlocker select`. Nothing is lost; `unlocker select <other-id>` repairs it. What happens: `RemoveUnlocker` deletes the unlocker's directory and leaves `current-unlocker` naming it. `GetCurrentUnlocker` then fails on the missing metadata file, and `withMnemonicAdvice` adds "the vault still opens with its mnemonic: run 'secret unlocker add passphrase' ...". That advice is wrong here: `unlocker add passphrase` without the mnemonic fails the same way, and the vault needs no new unlocker. `secret vault remove` of the current vault switches to another vault first; removing the current unlocker does nothing of the kind. The confirmation says only "It is not the vault's last unlocker." Reproduction (a throwaway GPG key in a fresh `GNUPGHOME`): ``` SB_UNLOCK_PASSPHRASE=pw SB_SECRET_MNEMONIC="$M" secret init echo -n hunter2 | SB_UNLOCK_PASSPHRASE=pw secret add foo SB_UNLOCK_PASSPHRASE=pw secret unlocker add pgp --keyid "$FPR" # becomes current secret unlocker remove --force "$PGPID" # succeeds SB_UNLOCK_PASSPHRASE=pw secret get foo # Error: ... failed to get current unlocker: failed to read unlocker metadata: open .../unlockers.d/$PGPID/unlocker-metadata.json: no such file or directory; the vault 'default' still opens with its mnemonic: run 'secret unlocker add passphrase' ... cat .../vaults.d/default/current-unlocker # still $PGPID ``` Where: https://git.eeqj.de/sneak/secret/src/commit/ef0ae90768c8b18a829033b3ebd4cf98fcaeb3d1/internal/vault/unlockers.go#L299 (`RemoveUnlocker`), https://git.eeqj.de/sneak/secret/src/commit/ef0ae90768c8b18a829033b3ebd4cf98fcaeb3d1/internal/cli/unlockers.go#L705 (`removeUnlocker`), https://git.eeqj.de/sneak/secret/src/commit/ef0ae90768c8b18a829033b3ebd4cf98fcaeb3d1/internal/vault/vault.go#L309 (`withMnemonicAdvice`). Correct behaviour is a product decision the README does not settle. Recommended: when the removed unlocker is current and another unlocker remains, make that one current in the same command (as `vault remove` does for vaults) and say so; when none remains, clear `current-unlocker`. In any case the error for a `current-unlocker` naming a missing directory should name `secret unlocker select` when other unlockers exist. **Definition of done** - Removing the current unlocker leaves the vault openable through a remaining unlocker without a further command, or the error after it names `secret unlocker select`. - A regression test removes the current of two unlockers and requires `GetOrDeriveLongTermKey` to succeed through the other; it fails before the fix. Model: fable-5-1
sneak was assigned by clawbot 2026-10-13 00:29:59 +02:00
Author
Collaborator

Reassigned to clawbot: sneak ruled that nothing is a release candidate while a known bug is open (sneak/project-management#32).

Plan: the issue's recommendation. When the removed unlocker is the current one and others remain, the same command makes the first remaining unlocker in secret unlocker list order current and says so; when none remains, current-unlocker is removed. A current-unlocker naming a missing unlocker gives an error that names secret unlocker select when other unlockers exist, without the mnemonic advice. README updated to match.

Model: opus-5-5

Reassigned to clawbot: sneak ruled that nothing is a release candidate while a known bug is open (https://git.eeqj.de/sneak/project-management/issues/32). Plan: the issue's recommendation. When the removed unlocker is the current one and others remain, the same command makes the first remaining unlocker in `secret unlocker list` order current and says so; when none remains, `current-unlocker` is removed. A `current-unlocker` naming a missing unlocker gives an error that names `secret unlocker select` when other unlockers exist, without the mnemonic advice. README updated to match. Model: opus-5-5
sneak was unassigned by clawbot 2026-10-14 19:42:51 +02:00
clawbot self-assigned this 2026-10-14 19:42:51 +02:00
clawbot added this to the 1.0.0 milestone 2026-10-14 19:42:51 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/secret#137