Make another unlocker current when the current one is removed #163

Merged
clawbot merged 1 commits from fix/137-current-unlocker-remove into next 2026-10-14 22:02:18 +02:00
Collaborator

Fixes #137.

Removing the vault's current unlocker left current-unlocker naming the removed one, so every command that opened the vault failed although another unlocker remained, and the error sent the user to the mnemonic.

vault.RemoveUnlocker now checks whether the unlocker it removes is the current one. If so, it makes current, before removing it, the first of the vault's other unlockers that secret unlocker select accepts, in the order secret unlocker list shows, and returns its ID, which secret unlocker remove prints as "Switched current unlocker to '...'". secret unlocker select refuses an unlocker whose metadata names a type other than passphrase or pgp, though secret unlocker list shows it. With no unlocker it accepts, current-unlocker is removed after the unlocker.

When current-unlocker names an unlocker that does not exist and the vault has another that secret unlocker select accepts, opening the vault fails with an error that wraps vault.ErrUnlockerNotFound and says to run secret unlocker select, without the mnemonic advice. The secret vault select step that the mnemonic advice puts first for a vault other than the current one moved into a small helper that both messages use.

RemoveUnlocker now returns (string, error). The regression test, TestUnlockerRemoveCurrent, uses a PGP unlocker for a throwaway GPG key, as TestAddPGPUnlocker does.

Judgement call: "pgp" became the constant unlockerTypePGP in internal/vault/unlockers.go, which its two existing type switches now use too.

Unverified: no test covers the select advice for a vault other than the current one.

Model: opus-5-5

Fixes https://git.eeqj.de/sneak/secret/issues/137. Removing the vault's current unlocker left `current-unlocker` naming the removed one, so every command that opened the vault failed although another unlocker remained, and the error sent the user to the mnemonic. `vault.RemoveUnlocker` now checks whether the unlocker it removes is the current one. If so, it makes current, before removing it, the first of the vault's other unlockers that `secret unlocker select` accepts, in the order `secret unlocker list` shows, and returns its ID, which `secret unlocker remove` prints as "Switched current unlocker to '...'". `secret unlocker select` refuses an unlocker whose metadata names a type other than `passphrase` or `pgp`, though `secret unlocker list` shows it. With no unlocker it accepts, `current-unlocker` is removed after the unlocker. When `current-unlocker` names an unlocker that does not exist and the vault has another that `secret unlocker select` accepts, opening the vault fails with an error that wraps `vault.ErrUnlockerNotFound` and says to run `secret unlocker select`, without the mnemonic advice. The `secret vault select` step that the mnemonic advice puts first for a vault other than the current one moved into a small helper that both messages use. `RemoveUnlocker` now returns `(string, error)`. The regression test, `TestUnlockerRemoveCurrent`, uses a PGP unlocker for a throwaway GPG key, as `TestAddPGPUnlocker` does. Judgement call: `"pgp"` became the constant `unlockerTypePGP` in `internal/vault/unlockers.go`, which its two existing type switches now use too. Unverified: no test covers the select advice for a vault other than the current one. Model: opus-5-5
clawbot added the needs-review label 2026-10-14 20:17:18 +02:00
clawbot self-assigned this 2026-10-14 20:17:18 +02:00
Author
Collaborator
  1. internal/vault/unlockers.go line 393, selectOtherUnlocker: it makes current the first ID that ListUnlockers returns, but ListUnlockers also returns an unlocker directory whose metadata names a type this build cannot open (such as keychain), and SelectUnlocker refuses those. When such a directory sorts before the vault's working unlockers, secret unlocker remove of the current unlocker removes nothing and fails with unlocker with ID X not found, where X is that directory: an unlocker the user did not name and that secret unlocker list shows. On next the same command succeeded. The README text at README.md line 244 and the help text at internal/cli/unlockers.go line 225 promise the switch in that case. Acceptable: make current the first other unlocker that SelectUnlocker accepts, treat the vault as having no other when there is none, and cover it in a test with such a directory sorted first.

  2. internal/vault/unlockers.go lines 144 to 151, checkCurrentUnlockerExists: it counts the same directories as other unlockers. When they are the only others, the error says to run secret unlocker select, which then fails with not found, and the mnemonic advice, the one step that works, is left out. Acceptable: count only unlockers that secret unlocker select accepts, so that case keeps the mnemonic advice.

Model: opus-5-5

1. `internal/vault/unlockers.go` line 393, `selectOtherUnlocker`: it makes current the first ID that `ListUnlockers` returns, but `ListUnlockers` also returns an unlocker directory whose metadata names a type this build cannot open (such as `keychain`), and `SelectUnlocker` refuses those. When such a directory sorts before the vault's working unlockers, `secret unlocker remove` of the current unlocker removes nothing and fails with `unlocker with ID X not found`, where X is that directory: an unlocker the user did not name and that `secret unlocker list` shows. On `next` the same command succeeded. The README text at `README.md` line 244 and the help text at `internal/cli/unlockers.go` line 225 promise the switch in that case. Acceptable: make current the first other unlocker that `SelectUnlocker` accepts, treat the vault as having no other when there is none, and cover it in a test with such a directory sorted first. 2. `internal/vault/unlockers.go` lines 144 to 151, `checkCurrentUnlockerExists`: it counts the same directories as other unlockers. When they are the only others, the error says to run `secret unlocker select`, which then fails with `not found`, and the mnemonic advice, the one step that works, is left out. Acceptable: count only unlockers that `secret unlocker select` accepts, so that case keeps the mnemonic advice. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-14 20:45:19 +02:00
clawbot added 1 commit 2026-10-14 21:04:37 +02:00
Removing the vault's current unlocker left current-unlocker naming it,
so every command that opened the vault failed although another
unlocker remained, and the error pointed at the mnemonic.
RemoveUnlocker now first makes current the first other unlocker that
`secret unlocker select` accepts, in the order `secret unlocker list`
shows, and returns its ID, which `secret unlocker remove` prints; with
none, current-unlocker is removed too. When current-unlocker names an
unlocker that does not exist and the vault has another that select
accepts, the error wraps ErrUnlockerNotFound and says to run
`secret unlocker select`, without the mnemonic advice.

Unverified: no test covers that advice for a vault other than the
current one, which gets `secret vault select` first.

Model: opus-5-5
clawbot force-pushed fix/137-current-unlocker-remove from b9c552c3cc to 4d030f1109 2026-10-14 21:04:37 +02:00 Compare
Author
Collaborator

Rework, rebased onto next:

  1. selectOtherUnlocker now makes current the first other unlocker of type passphrase or pgp, the types secret unlocker select accepts, found by the new helper firstOtherUnlockerID; with none, current-unlocker is removed. TestUnlockerRemoveCurrent gained a case with such a directory, odd, sorted first. The README and the help text now say "that secret unlocker select accepts".
  2. checkCurrentUnlockerExists uses the same helper, so when only such directories remain the error keeps the mnemonic advice; TestMissingCurrentUnlockerNamesMnemonic covers it and follows that advice.

Model: opus-5-5

Rework, rebased onto `next`: 1. `selectOtherUnlocker` now makes current the first other unlocker of type `passphrase` or `pgp`, the types `secret unlocker select` accepts, found by the new helper `firstOtherUnlockerID`; with none, `current-unlocker` is removed. `TestUnlockerRemoveCurrent` gained a case with such a directory, `odd`, sorted first. The README and the help text now say "that `secret unlocker select` accepts". 2. `checkCurrentUnlockerExists` uses the same helper, so when only such directories remain the error keeps the mnemonic advice; `TestMissingCurrentUnlockerNamesMnemonic` covers it and follows that advice. Model: opus-5-5
clawbot added needs-review and removed needs-rework labels 2026-10-14 21:05:16 +02:00
Author
Collaborator

Review passed.

Model: opus-5-5

Review passed. Model: opus-5-5
clawbot merged commit 0630a32167 into next 2026-10-14 22:02:18 +02:00
clawbot deleted branch fix/137-current-unlocker-remove 2026-10-14 22:02:19 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/secret#163