Vault: one unopenable sealed row permanently bricks unlock #142
Labels
No labels
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lz/agent-nexus#142
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found in the
/pr-reviewpass overfeat/mcp(envelope encryption).The mechanism
upgradeLegacyVaultthrows when any sealed row fails to open, rolls its transaction back, and rethrows.routes/api/auth/unlock/+server.ts:15has no try/catch, so the throw becomes SvelteKit's default{"message":"Internal Error"}with status 500.On a legacy install the upgrade runs on every unlock attempt until it succeeds. So a single undecryptable
connections.encrypted_tokenorrepo_env_vars.encrypted_valuerow means:The throw itself is correct and deliberate — reporting damage as "wrong passphrase" sends the operator to the wrong problem, and
vault-envelope.tsdocuments that choice. The bug is that "throw" currently means "uncaught 500", not "a legible error with a recovery path".Note the throw protects data that is already unrecoverable: a row that opens under no key is lost whether or not the upgrade proceeds.
How a dead row could arise
Verified there was a real window:
c8aa70b(2026-07-16 19:37Z) addedrepo_env_vars;c472266(2026-07-17 10:13Z) taughtrotateMasterPassphraseto re-seal it. A passphrase rotation in that ~15h window leftencrypted_valuerows sealed under the pre-rotation key. Both commits are onmain.This instance is not affected — checked before merging
feat/mcp: onerepo_env_varsrow, created 2026-07-17T17:24Z, 7.2 hours after the fix. Filed because the mechanism outlives that particular window (partial restores, manual edits, future bugs).Options
(1)+(3) is probably right, but it changes the "damage must not read as a wrong passphrase" contract, so it wants a deliberate decision rather than a drive-by fix.
Related
/api/auth/unlockis also the only route where vault damage is invisible — the rotation route already returns{ok:false, reason:'error', message}and surfaces it in the modal.