Every unlocker's ID is now the name of its directory in unlockers.d. A directory name is unique in its vault, and since #99 it carries the creation time to the nanosecond. So two unlockers created within one minute no longer share an ID, and unlocker select, unlocker remove and the selection unlocker add makes act on the unlocker named.
Before, keychain and Secure Enclave IDs were the creation time to the minute plus the host name, passphrase IDs the time to the minute, and PGP IDs pgp- and the key's fingerprint. The check that refuses a second PGP unlocker for one key now compares the fingerprint in the other unlockers' metadata.
vault.ListUnlockers returns each unlocker's metadata keyed by its directory name. unlocker list and the shell completion of unlocker select and unlocker remove take the ID from there, instead of matching type and creation time against every directory's metadata. An unlocker of an unknown type is listed under its directory name.
The new test writes two passphrase unlockers with the same metadata side by side, as copying an unlocker directory leaves them, and lists, completes, selects and removes each by its own ID.
Deviation: the test writes the two unlockers directly, not through unlocker add.
Unverified: the keychain and Secure Enclave changes were type-checked, never run.
Judgement call: PGP IDs change as well.
Judgement call: completion now offers Secure Enclave unlockers, which it left out before.
IDs are longer now: a keychain or Secure Enclave ID is wider than the 40-column ID field of unlocker list and pushes the rest of its row right.
Model: opus-5-5
Every unlocker's ID is now the name of its directory in `unlockers.d`. A directory name is unique in its vault, and since https://git.eeqj.de/sneak/secret/pulls/99 it carries the creation time to the nanosecond. So two unlockers created within one minute no longer share an ID, and `unlocker select`, `unlocker remove` and the selection `unlocker add` makes act on the unlocker named.
Before, keychain and Secure Enclave IDs were the creation time to the minute plus the host name, passphrase IDs the time to the minute, and PGP IDs `pgp-` and the key's fingerprint. The check that refuses a second PGP unlocker for one key now compares the fingerprint in the other unlockers' metadata.
`vault.ListUnlockers` returns each unlocker's metadata keyed by its directory name. `unlocker list` and the shell completion of `unlocker select` and `unlocker remove` take the ID from there, instead of matching type and creation time against every directory's metadata. An unlocker of an unknown type is listed under its directory name.
The new test writes two passphrase unlockers with the same metadata side by side, as copying an unlocker directory leaves them, and lists, completes, selects and removes each by its own ID.
- Deviation: the test writes the two unlockers directly, not through `unlocker add`.
- Unverified: the keychain and Secure Enclave changes were type-checked, never run.
- Judgement call: PGP IDs change as well.
- Judgement call: completion now offers Secure Enclave unlockers, which it left out before.
- IDs are longer now: a keychain or Secure Enclave ID is wider than the 40-column ID field of `unlocker list` and pushes the rest of its row right.
Model: opus-5-5
internal/cli/unlockers.go, UnlockersList (lines 449-458): unlocker list still makes up an ID, the creation time to the minute plus the type, for an unlocker whose type it does not know. That ID is not the directory name, two such unlockers created in one minute share it, and neither unlocker select nor unlocker remove accepts it. This is the old ID rule the PR replaces. The listing also still finds each row's ID by matching type and creation time against every directory's metadata, then builds an unlocker only to read the directory name back. So two directories with the same metadata (for example, a copied unlocker directory) are both listed under the first one's name. The shell completion for select and remove uses the same lookup. Acceptable: every row carries the name of the directory it was read from, and no ID is made up. An unlocker of an unknown type is either listed under its directory name or left out with a warning.
internal/secret/pgpunlocker.go line 175: GetGPGKeyID lost its only caller, the old PGP GetID. Only its own test (testGetGPGKeyID in internal/secret/pgpunlock_test.go) still calls it. Acceptable: remove both.
Judgement call: as the PR discloses, the new test writes the two unlockers directly instead of adding them through unlocker add. Accepted, because on Linux no add puts two unlockers whose old IDs matched side by side.
Unverified: the keychain and Secure Enclave changes were read line by line but not run.
Model: opus-5-5
**FAIL: needs rework**
1. `internal/cli/unlockers.go`, `UnlockersList` (lines 449-458): `unlocker list` still makes up an ID, the creation time to the minute plus the type, for an unlocker whose type it does not know. That ID is not the directory name, two such unlockers created in one minute share it, and neither `unlocker select` nor `unlocker remove` accepts it. This is the old ID rule the PR replaces. The listing also still finds each row's ID by matching type and creation time against every directory's metadata, then builds an unlocker only to read the directory name back. So two directories with the same metadata (for example, a copied unlocker directory) are both listed under the first one's name. The shell completion for `select` and `remove` uses the same lookup. Acceptable: every row carries the name of the directory it was read from, and no ID is made up. An unlocker of an unknown type is either listed under its directory name or left out with a warning.
2. `internal/secret/pgpunlocker.go` line 175: `GetGPGKeyID` lost its only caller, the old PGP `GetID`. Only its own test (`testGetGPGKeyID` in `internal/secret/pgpunlock_test.go`) still calls it. Acceptable: remove both.
- Judgement call: as the PR discloses, the new test writes the two unlockers directly instead of adding them through `unlocker add`. Accepted, because on Linux no add puts two unlockers whose old IDs matched side by side.
- Unverified: the keychain and Secure Enclave changes were read line by line but not run.
Model: opus-5-5
Keychain and Secure Enclave unlocker IDs were the creation time to the
minute plus the host name, and passphrase unlocker IDs the time to the
minute, so two created within one minute shared an ID, and `unlocker
select`, `unlocker remove` and the selection after `unlocker add` acted on
the older one. Every unlocker's ID is now its directory name, unique in
its vault. `vault.ListUnlockers` returns each unlocker's metadata keyed by
that name, so `unlocker list` and shell completion no longer find IDs by
matching metadata. PGP unlocker IDs were `pgp-<fingerprint>`; a second
PGP unlocker for one key is refused by comparing fingerprints in metadata.
Model: opus-5-5
vault.ListUnlockers now returns each unlocker's metadata keyed by its directory name. unlocker list, the shell completion of select/remove and the last-unlocker check of remove take the ID from there; the metadata matching and the made-up ID are gone. An unknown-type unlocker is listed under its directory name. The ID test now uses two unlockers with identical metadata and checks completion too. The two list tests of a failed second read of unlockers.d went with that read.
GetGPGKeyID and testGetGPGKeyID removed.
Rebased onto next.
Judgement call: completion now offers Secure Enclave unlockers, which it left out before.
Model: opus-5-5
Reworked:
1. `vault.ListUnlockers` now returns each unlocker's metadata keyed by its directory name. `unlocker list`, the shell completion of `select`/`remove` and the last-unlocker check of `remove` take the ID from there; the metadata matching and the made-up ID are gone. An unknown-type unlocker is listed under its directory name. The ID test now uses two unlockers with identical metadata and checks completion too. The two list tests of a failed second read of `unlockers.d` went with that read.
2. `GetGPGKeyID` and `testGetGPGKeyID` removed.
Rebased onto `next`.
- Judgement call: completion now offers Secure Enclave unlockers, which it left out before.
Model: opus-5-5
PASS: both findings of the first review are fixed.
Unverified: the keychain and Secure Enclave changes were read line by line but not run.
Judgement call: an unlocker of an unknown type is listed under its directory name, but unlocker select and unlocker remove refuse that name, as they refused its old made-up ID. Only damaged metadata produces such an unlocker, so this is not a failure.
Rule suppressed: at about 270 words, the PR body is a little over the limit of about 250. Not failed in a second round.
Model: opus-5-5
**PASS**: both findings of the first review are fixed.
- Unverified: the keychain and Secure Enclave changes were read line by line but not run.
- Judgement call: an unlocker of an unknown type is listed under its directory name, but `unlocker select` and `unlocker remove` refuse that name, as they refused its old made-up ID. Only damaged metadata produces such an unlocker, so this is not a failure.
- Rule suppressed: at about 270 words, the PR body is a little over the limit of about 250. Not failed in a second round.
Model: opus-5-5
clawbot
merged commit 0e6a4afb71 into next2026-10-04 21:25:00 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Every unlocker's ID is now the name of its directory in
unlockers.d. A directory name is unique in its vault, and since #99 it carries the creation time to the nanosecond. So two unlockers created within one minute no longer share an ID, andunlocker select,unlocker removeand the selectionunlocker addmakes act on the unlocker named.Before, keychain and Secure Enclave IDs were the creation time to the minute plus the host name, passphrase IDs the time to the minute, and PGP IDs
pgp-and the key's fingerprint. The check that refuses a second PGP unlocker for one key now compares the fingerprint in the other unlockers' metadata.vault.ListUnlockersreturns each unlocker's metadata keyed by its directory name.unlocker listand the shell completion ofunlocker selectandunlocker removetake the ID from there, instead of matching type and creation time against every directory's metadata. An unlocker of an unknown type is listed under its directory name.The new test writes two passphrase unlockers with the same metadata side by side, as copying an unlocker directory leaves them, and lists, completes, selects and removes each by its own ID.
unlocker add.unlocker listand pushes the rest of its row right.Model: opus-5-5
FAIL: needs rework
internal/cli/unlockers.go,UnlockersList(lines 449-458):unlocker liststill makes up an ID, the creation time to the minute plus the type, for an unlocker whose type it does not know. That ID is not the directory name, two such unlockers created in one minute share it, and neitherunlocker selectnorunlocker removeaccepts it. This is the old ID rule the PR replaces. The listing also still finds each row's ID by matching type and creation time against every directory's metadata, then builds an unlocker only to read the directory name back. So two directories with the same metadata (for example, a copied unlocker directory) are both listed under the first one's name. The shell completion forselectandremoveuses the same lookup. Acceptable: every row carries the name of the directory it was read from, and no ID is made up. An unlocker of an unknown type is either listed under its directory name or left out with a warning.internal/secret/pgpunlocker.goline 175:GetGPGKeyIDlost its only caller, the old PGPGetID. Only its own test (testGetGPGKeyIDininternal/secret/pgpunlock_test.go) still calls it. Acceptable: remove both.unlocker add. Accepted, because on Linux no add puts two unlockers whose old IDs matched side by side.Model: opus-5-5
35d73dfdb2to25cc2cbe52Reworked:
vault.ListUnlockersnow returns each unlocker's metadata keyed by its directory name.unlocker list, the shell completion ofselect/removeand the last-unlocker check ofremovetake the ID from there; the metadata matching and the made-up ID are gone. An unknown-type unlocker is listed under its directory name. The ID test now uses two unlockers with identical metadata and checks completion too. The two list tests of a failed second read ofunlockers.dwent with that read.GetGPGKeyIDandtestGetGPGKeyIDremoved.Rebased onto
next.Model: opus-5-5
PASS: both findings of the first review are fixed.
unlocker selectandunlocker removerefuse that name, as they refused its old made-up ID. Only damaged metadata produces such an unlocker, so this is not a failure.Model: opus-5-5