Skip to content

fix(messaging): revert sharedSecretCache reuse in getMessageHistory (regression hotfix)#76

Merged
TortoiseWolfe merged 1 commit intomainfrom
hotfix/messaging-shared-secret-cache
May 5, 2026
Merged

fix(messaging): revert sharedSecretCache reuse in getMessageHistory (regression hotfix)#76
TortoiseWolfe merged 1 commit intomainfrom
hotfix/messaging-shared-secret-cache

Conversation

@TortoiseWolfe
Copy link
Copy Markdown
Owner

Summary

Surgical revert of one change from PR #74 (batch 8) that introduced a Firefox/WebKit messaging E2E regression. All other batch 8 changes (non-extractable in-memory ECDH key, cursorRef pagination fix, console→logger, payment type narrow) remain intact.

Evidence — this is a real regression, not a flake

Run Branch / commit Date Result
25306048649 main dd83ac7 (pre-batch-8) today 07:14 UTC ✅ all green
25332113434 main 0e20fb3 (post-batch-8) today 17:04 UTC ❌ firefox-msg failed
PR #70 rebased 33e7b80 (carries batch 8) today ❌ webkit-msg failed
PR #75 first run ca61ee7 (carries batch 8) today ❌ firefox-msg failed

The very first post-batch-8 run on main reproduced the failure pattern. Pre-batch-8 main was green this morning. Causality chain is clear.

Failure symptoms

  • Firefox/WebKit messaging tests time out waiting for cross-window message visibility
  • message-delete-placeholder: getByText('[Message deleted]') resolved to 2 elements — duplicate render
  • All failures involve the polling fallback (waitForMessageOnPage2)

Root cause

Batch 8 added a module-level shared-secret cache read-path in getMessageHistory (lines 772-802 of message-service.ts) to avoid re-deriving ECDH on every poll tick. The cache reuse is symmetric in design (same pattern as sendMessage's cache, which has been there for months) — but interacts badly with Firefox/WebKit reload + tab semantics.

Hypothesis: the cached CryptoKey reference behaves differently across these browsers when getCurrentKeys() returns a freshly-deserialized key from IndexedDB after a page reload. The invalidation guard cachedSenderPrivateKey !== currentKeys.privateKey was likely thrashing or holding a stale reference, causing decryption to use mismatched secrets.

What this PR does

getMessageHistory now derives a fresh shared secret per call, matching pre-batch-8 behavior. The cache infrastructure remains in place — sendMessage still uses it as before. This revert is read-path only.

Cost

~6 ECDH derivations/min under polling — measurable but not user-perceivable. A browser-safe instance-aware cache key can return as a separate change once the Firefox CryptoKey-reference behavior is properly understood.

What's preserved from batch 8

  • ✅ Non-extractable in-memory ECDH private key (security fix, primary motivation)
  • cursorRef pagination stale-closure fix in ConversationView
  • ✅ console → logger.{debug,error} migration
  • ✅ payment-service type narrow

Verification

  • pnpm run type-check: clean
  • pnpm run lint: clean
  • pnpm test: 3237/3237 pass
  • Husky pre-push (lint, type-check, unit, build): all green

Test plan

  • CI runs the full E2E suite — Firefox/WebKit messaging shards expected to recover

Refs

🤖 Generated with Claude Code

…regression hotfix)

Batch 8 (commit 52becd7, PR #74) added a module-level shared-secret cache
read-path in getMessageHistory to avoid re-deriving ECDH on every poll
tick. Post-merge CI showed a real Firefox/WebKit messaging E2E regression:

- Run 25306048649 (main, dd83ac7, pre-batch-8) at 07:14 UTC: all green
- Run 25332113434 (main, 0e20fb3, post-batch-8) at 17:04 UTC: firefox-msg
  failed with messages-not-visible timeouts on real-time-delivery,
  encrypted-messaging, and message-delete-placeholder specs
- Subsequent PR runs reproduced the same pattern, with webkit-msg also
  failing on the rebased PR #70 branch

The cache reuse is symmetric in design (same pattern as sendMessage's
cache, which has been there for months) but interacts badly with
Firefox/WebKit reload + tab semantics. The cached CryptoKey reference
appears to behave differently across these browsers when getCurrentKeys
returns a freshly-deserialized key from IndexedDB after a page reload —
the invalidation guard `cachedSenderPrivateKey !== currentKeys.privateKey`
was likely thrashing or holding a stale reference.

Hotfix: getMessageHistory now derives a fresh shared secret per call,
matching pre-batch-8 behavior. The cache infrastructure remains in place
(sendMessage still uses it as before) — this revert is read-path only.

Cost: ~6 ECDH derivations/min under polling — measurable but not
user-perceivable. A browser-safe instance-aware cache key can return as
a separate change once the Firefox CryptoKey-reference behavior is
properly understood.

Verification:
- pnpm run type-check: clean
- pnpm run lint: clean
- pnpm test: 3237/3237 pass

Refs: #74 (introduced regression), runs 25306048649 (last green),
25332113434 (first red post-batch-8)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@TortoiseWolfe TortoiseWolfe merged commit d24691b into main May 5, 2026
28 checks passed
@TortoiseWolfe TortoiseWolfe deleted the hotfix/messaging-shared-secret-cache branch May 5, 2026 02:11
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