From 5106e572a5e41346f79021ae83de092a25eb3531 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 23 Apr 2026 00:34:51 +0000 Subject: [PATCH 1/2] fix: consume Marmot inner reactions/deletions as verified MIP-03 inner events inside a kind:445 GroupEvent are unsigned (sig is empty or placeholder), so LocalCache's `justVerify()` fails and `consume(ReactionEvent)` skips the branch that attaches the reaction to its target note via `addReaction()`. The same applies to kind:5 deletions. Side-channel inner events from other Marmot clients (WhiteNoise in particular) were therefore silently dropped by LocalCache, so an inbound emoji reaction never appeared on the target chat bubble. NIP-17 has the same "unsigned inner rumor" shape and already hands the inner event to `justConsume(..., wasVerified = true)`. Align Marmot with that contract at the two inbound call sites: - `GroupEventHandler.add` (fresh kind:445 from relay) - `Account` restore-from-disk loop (persisted plaintext at app start) Authenticity is already guaranteed by the MLS layer: `MarmotInboundProcessor.processPrivateMessage` rejects any inner event whose `pubkey` doesn't match the MLS sender credential identity before ever emitting an `ApplicationMessage`. https://claude.ai/code/session_01KhVtLJAEkNCwpRWUUTqDFM --- .../java/com/vitorpamplona/amethyst/model/Account.kt | 7 ++++++- .../ui/screen/loggedIn/DecryptAndIndexProcessor.kt | 10 +++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index 831057941..10ec32d29 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -3018,7 +3018,12 @@ class Account( val innerEvent = com.vitorpamplona.quartz.nip01Core.core.Event .fromJson(json) - val isNew = cache.justConsume(innerEvent, null, false) + // wasVerified=true: these events were already verified + // via MLS credential identity when first decrypted (see + // GroupEventHandler). Persisted inner events are + // unsigned per MIP-03, so justVerify would fail and + // reactions/deletions would silently drop here. + val isNew = cache.justConsume(innerEvent, null, true) val innerNote = cache.getOrCreateNote(innerEvent.id) if (isNew) { innerNote.event = innerEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DecryptAndIndexProcessor.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DecryptAndIndexProcessor.kt index 100760c5e..6226c6fd8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DecryptAndIndexProcessor.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DecryptAndIndexProcessor.kt @@ -592,7 +592,15 @@ class GroupEventHandler( "GroupEventHandler.add: ApplicationMessage decrypted innerKind=${innerEvent.kind} " + "innerId=${innerEvent.id.take(8)}… author=${innerEvent.pubKey.take(8)}…" } - if (cache.justConsume(innerEvent, null, false)) { + // wasVerified=true: MIP-03 inner events are unsigned (sig is empty + // or placeholder), so justVerify() would fail. Authenticity is + // already guaranteed by the MLS layer — MarmotInboundProcessor + // enforces innerEvent.pubKey == MLS sender credential identity + // before returning ApplicationMessage. Without this, side-channel + // inner events from other Marmot clients (kind:7 reactions, + // kind:5 deletions) are silently dropped by LocalCache, so a + // WhiteNoise reaction never attaches to its target chat bubble. + if (cache.justConsume(innerEvent, null, true)) { val innerNote = cache.getOrCreateNote(innerEvent.id) innerNote.event = innerEvent From 19a92e9bf5a96e7651e3a9fa614182876c6fe13b Mon Sep 17 00:00:00 2001 From: Crowdin Bot Date: Thu, 23 Apr 2026 00:50:42 +0000 Subject: [PATCH 2/2] New Crowdin translations by GitHub Action --- amethyst/src/main/res/values-pl-rPL/strings.xml | 2 +- amethyst/src/main/res/values-zh-rCN/strings.xml | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/amethyst/src/main/res/values-pl-rPL/strings.xml b/amethyst/src/main/res/values-pl-rPL/strings.xml index 6032853ab..2715c23f5 100644 --- a/amethyst/src/main/res/values-pl-rPL/strings.xml +++ b/amethyst/src/main/res/values-pl-rPL/strings.xml @@ -1690,7 +1690,7 @@ Niektóre akcje nie powiodły się Wszystkie akcje udane Wszystkie akcje nieudane - Reakcja + Reakcję Wiadomość głosowa Odpowiedź głosowa Czas zakończenia głosowania diff --git a/amethyst/src/main/res/values-zh-rCN/strings.xml b/amethyst/src/main/res/values-zh-rCN/strings.xml index 6af34ef7a..4faad6e55 100644 --- a/amethyst/src/main/res/values-zh-rCN/strings.xml +++ b/amethyst/src/main/res/values-zh-rCN/strings.xml @@ -458,6 +458,11 @@ 公共聊天 关注包 直播 + 音频室 + 阶段 + 观众 + 举手 + 放下手 视频 文章 私人书签 @@ -818,6 +823,16 @@ 服务器 URL 用户名 凭证 + 保持与收件箱中继的连接以便接收实时通知 + Amethyst 通知活跃 + 已连接到 %1$d 个收件箱中继 + 正在连接到收件箱中继\u2026 + 暂停 + 始终开启通知服务 + 保持与收件箱中继的持续连接以便即时发送通知。 显示正在进行的通知。使用更多电量,但确保您永远不会错过消息。 + 电池优化已启用 + Android 可能会限制后台中继连接。禁用针对Amethyst电池优化以确保可靠的通知。 + 立即修复 通知: 加入对话 用户或群组 ID