fix(decrypt): bump hashtag+subscription decrypt timeout 6s->15s; document lists-decrypt root cause

Real-env console shows the encrypted lists are FETCHED but DECRYPTION fails on
cold login: hashtags time out at 6s, blocks report signerStatus 'missing', DMs
throw 'helpers unavailable'. Root cause: a relay-connection storm to the user's
~20 dead NIP-65 relays (subsystems use uncapped relayManager read+write) starves
the nip-07 extension. Bumping the decrypt timeout to 15s (matching blocks) is a
partial mitigation for hashtags/subscriptions. The proper fix (bound reads to a
liveness-ranked healthy core incl. defaults + DM decrypt retry) is documented in
KNOWN_BUGS #0 — a first capReadRelays attempt was reverted because capping block
reads broke the security-critical user-blocks integration test.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
thePR0M3TH3ANandClaude Opus 4.8 committed 2026-06-14 22:12:21 -04:00
1 parent c0a8636678
commit 49b7645f80
3 files changed
+35 -6

No files matched your search

+29
View File
@@ -11,6 +11,35 @@ refactor work in that doc — these are defects.
## Open
### 0. Encrypted lists (hashtags, DMs, blocks) fail to DECRYPT on login *(top priority)*
- **Severity:** high — the user's recurring "hashtags/DMs/watch-history don't
load in the profile modal."
- **Diagnosed from real-env console (2026-06-14):** the lists are *fetched* (e.g.
block events `Array(2)`) but **decryption fails**, three different ways:
- DMs: `DM decryption helpers are unavailable` (`client.js` listDirectMessages
→ `buildDmDecryptContext` returns 0 decryptors → hard throw, no retry). Happens
when `ensureExtensionPermissions` returns `ok:false`.
- Blocks: `Decryption timed out; signerStatus: 'missing'/'unknown'`
(`userBlocks.js` applyEvents) — the signer/decrypt capability isn't resolved.
- Hashtags: `Decryption timed out after 6s` (`hashtagPreferencesService.js`).
- **Root cause (strong):** a relay-connection STORM. On cold load the app opens
WebSockets to the user's full ~20-relay NIP-65 list (most **dead**) because
subsystems build relay lists from `relayManager.getReadRelayUrls()` +
`getWriteRelayUrls()` (uncapped) — e.g. `userBlocks.loadBlocksInternal`. The
dozens of failing connections churn the main thread and starve the nip-07
extension's postMessage round-trips → permission/decrypt calls fail or time out.
- **Partial mitigation shipped:** hashtag + subscription decrypt timeout 6s→15s.
- **Proper fix (next):** RelayHealth — bound the SUBSCRIBE/read set to a small,
liveness-ranked healthy core that ALWAYS includes the reliable default
aggregators, applied centrally so every subsystem (blocks, hashtags, subs,
profiles, batch fetcher) reads from it; writes stay uncapped. Plus decrypt
resilience: don't hard-fail DMs on a transient permission miss (retry).
NOTE: a first attempt (`capReadRelays`) was reverted — capping the block-list
read path broke the security-critical `user-blocks` integration test (it
orchestrates per-relay block hydration), so this needs a test-aware rework.
Repro: `scripts/perf/disclaimer-repro.mjs` shape; real-env console is the
source of truth.
### 1. Logged-out: video grid stays empty until a manual refresh
- **Severity:** medium (first-load UX, logged-out).
- **Where:** initial feed render path (`js/index.js` disclaimer flow →
+5 -5
View File
@@ -42,11 +42,11 @@ const LOG_PREFIX = "[HashtagPreferences]";
const HASHTAG_IDENTIFIER = "bitvid:tag-preferences";
const DEFAULT_VERSION = 1;
// PERF: Reduced from 10s to 6s — the signer is guaranteed ready before list
// loading starts (authSessionCoordinator waits for the permission pre-grant),
// so decryption should succeed within 2-3s. 6s is generous enough for slow
// relays while still failing fast for scheme fallback.
const DECRYPT_TIMEOUT_MS = 6000;
// Cold-load decryption competes with other encrypted lists for the serialized
// nip-07 extension, and the main thread can be busy connecting to relays. 6s
// proved too aggressive on real accounts (lists timed out and showed empty);
// 15s matches the block-list timeout and lets decryption complete under load.
const DECRYPT_TIMEOUT_MS = 15000;
// PERF: Reduced from 3s to 1.5s — extensions that already granted permission
// should recover near-instantly. Shorter delay speeds up the login path.
const DECRYPT_RETRY_DELAY_MS = NETWORK_RETRY_DELAY_MS;
+1 -1
View File
@@ -53,7 +53,7 @@ const SUBSCRIPTION_SET_KIND =
// so decryption should succeed within 2-3s. 6s is generous enough for slow
// relays while still failing fast enough for the scheme fallback to try the
// next decryption method without stalling the login path.
const DECRYPT_TIMEOUT_MS = 6000;
const DECRYPT_TIMEOUT_MS = 15000;
// PERF: Reduced from 3s to 1.5s — extensions that already granted permission
// should recover near-instantly. The shorter delay prevents unnecessary wait
// time during the critical login path when the first decrypt attempt fails.