Skip to content

fix(server): gracefully degrade when vault is locked during browser search#93

Merged
ErikChevalier merged 2 commits into
mainfrom
claude/searchmob-vault-dek-error-0rasem
Jun 28, 2026
Merged

fix(server): gracefully degrade when vault is locked during browser search#93
ErikChevalier merged 2 commits into
mainfrom
claude/searchmob-vault-dek-error-0rasem

Conversation

@ErikChevalier

Copy link
Copy Markdown
Contributor

What & why

When the app is in passphrase (zero-knowledge) mode and the user switches to the browser to search, the Android process lifecycle fires onStop(), which wipes the DEK from memory via StorageLockController.lockNow(). The embedded web server is still running, so the browser's search request hits /search and calls rankingPreferences.load() — which tries to decrypt preferences via store.get()EncryptedPreferencesCodec.decode()dek(). With the DEK gone, DekHolder.get() throws "vault is locked: DEK not present in memory", and the unhandled error propagates to the browser as a crash page.

Both RankingPreferences.load() and PersonalizationPreferences.load() document themselves as "fail-soft" (missing or corrupt → empty defaults), but the store.get() call was unguarded and threw on a locked vault. This wraps those calls in runCatching so a locked vault is treated the same as a missing value — search works with default/empty ranking rules, and personalization is simply absent, exactly as the docs describe.

Relates to OpenSpec change: N/A (bug fix)

Checklist

  • Built on its own feat//fix/ branch off main
  • ./gradlew ktlintCheck lint test assembleDebug is green locally
  • Added/updated tests (unit and/or instrumentation)
  • No telemetry, trackers, or device identifiers added
  • No Google scraping introduced
  • Battery: no wake-lock held while idle
  • OpenSpec change validates (openspec validate <name> --strict) and tasks are checked off

Generated by Claude Code

claude added 2 commits June 28, 2026 22:56
…earch

RankingPreferences.load() and PersonalizationPreferences.load() both
document themselves as fail-soft, but store.get() was unguarded and threw
when the DEK was absent. On a passphrase-mode device the process
lifecycle wipes the DEK on background, so a browser search while the app
is backgrounded crashed with "vault is locked: DEK not present in memory".
Wrap store.get() in runCatching so locked-vault reads fall back to empty
defaults instead of propagating the error to the served page.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AAUvwoGuyk1ToQf9WSU7A1
Extract a shared locked() helper to stay under the 120-char line limit
and match the project's multi-line parameter style for put().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AAUvwoGuyk1ToQf9WSU7A1
@ErikChevalier ErikChevalier marked this pull request as ready for review June 28, 2026 23:37
@ErikChevalier ErikChevalier merged commit 9934410 into main Jun 28, 2026
2 checks passed
@ErikChevalier ErikChevalier deleted the claude/searchmob-vault-dek-error-0rasem branch June 28, 2026 23:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants