fix(notifications): notification replies re-check encryption at send time and use the right account (#299) #397

Merged
jared merged 1 commits from fix/sw-reply-checks into lotus 2026-10-18 03:55:02 -04:00
Owner

Fixes #299.

Inline notification replies (#203) are posted by the service worker as plain m.room.messages, because a worker can't encrypt. Two gaps:

  1. The Reply action was left out only for rooms that were encrypted when the notification was shown. A reply typed hours later, into a room that has since enabled encryption, went out unencrypted.
  2. The worker posted with Array.from(sessions.values()).pop(), the newest session, not the notification's account.

Change

  • sendNotificationReply first does GET /rooms/{id}/state/m.room.encryption/. Only a definite 404 lets the plain reply go out. If the state exists the result is encrypted; any other answer counts as failed, so it fails safe.
  • The page now passes its userId with the session (setSession), and notifications carry data.userId. New pickReplySession uses that account's session only. An old notification without a user id may reply only when exactly one session exists.
  • "Reply not sent" says why, e.g. "This room is now encrypted, so "…" wasn't sent. Open the app to reply."

Verified

  • Unit tests: the encryption check order; encrypted, error and send-failure results, where no PUT happens on refusal; session picking; failure texts.
  • The real helper against the local Synapse: an encrypted room gives encrypted, an unencrypted room gives sent.
  • I didn't drive a real OS notification reply; headless Chromium has no inline-reply UI.

tsc clean · unit tests pass · ESLint clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5

Fixes #299. Inline notification replies (#203) are posted by the service worker as **plain** `m.room.message`s, because a worker can't encrypt. Two gaps: 1. The Reply action was left out only for rooms that were encrypted **when the notification was shown**. A reply typed hours later, into a room that has since enabled encryption, went out **unencrypted**. 2. The worker posted with `Array.from(sessions.values()).pop()`, the newest session, not the notification's account. **Change** - `sendNotificationReply` first does `GET /rooms/{id}/state/m.room.encryption/`. Only a definite **404** lets the plain reply go out. If the state exists the result is `encrypted`; any other answer counts as `failed`, so it fails safe. - The page now passes its `userId` with the session (`setSession`), and notifications carry `data.userId`. New `pickReplySession` uses that account's session only. An old notification without a user id may reply only when exactly one session exists. - "Reply not sent" says why, e.g. "This room is now encrypted, so "…" wasn't sent. Open the app to reply." **Verified** - Unit tests: the encryption check order; encrypted, error and send-failure results, where no PUT happens on refusal; session picking; failure texts. - The real helper against the local Synapse: an encrypted room gives `encrypted`, an unencrypted room gives `sent`. - I didn't drive a real OS notification reply; headless Chromium has no inline-reply UI. `tsc` clean · unit tests pass · ESLint clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
jared added 1 commit 2026-10-17 20:52:17 -04:00
fix(notifications): notification replies re-check encryption at send time and use the right account (#299)
CI / Build & Quality Checks (pull_request) Successful in 2m43s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 12s
CI / Playwright smoke (e2e) (pull_request) Successful in 16m49s
005fc9e768
The service worker posts inline notification replies in the clear (it
cannot encrypt). The Reply action was only withheld for rooms that were
encrypted when the notification was shown, so a reply typed hours later
into a room that has since turned on encryption went out unencrypted.
It also posted with the newest session the worker knew, not the
notification's account.

The worker now GETs the room's m.room.encryption state first and only
sends on a definite 404; notifications carry the user id and the worker
uses that account's session (or refuses). "Reply not sent" says why.

Fixes #299

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
jared merged commit a20e68596a into lotus 2026-10-18 03:55:02 -04:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: LotusGuild/cinny#397