Index: src/services/ActivityPubService.js
===================================================================
--- src/services/ActivityPubService.js	(revision 21ef238b2aef0be15654f3a397db52757fa168b1)
+++ src/services/ActivityPubService.js	(revision 383e8976c0201c4092866eefefb420ac6cedd57a)
@@ -4410,6 +4410,10 @@
  * Eigen berichten tellen nooit mee: je hebt jezelf gelezen.
  */
-export function unreadPerConversation(slug) {
-  try {
+export function unreadPerConversation(slug, { messagesAllowed = true, guardians = new Set() } = {}) {
+  try {
+    // DE POORT TELT MEE. Staat messages dicht, dan toont de app die berichten
+    // niet -- en dan mag een badge ze ook niet aankondigen, want dat getal
+    // vertelt precies wat de poort verbergt. Wat er altijd door mag telt wel:
+    // het guardian-kanaal en de boei. Zelfde regel als bij de serialisatie.
     const rijen = db.prepare(`
       SELECT u.other AS other,
@@ -4421,5 +4425,7 @@
        WHERE u.direction = 'in'
          AND (r.cursor IS NULL OR (u.stamp || '|' || u.ref) > r.cursor)
-       GROUP BY u.other`).all({ slug });
+         AND (@open = 1 OR m.help_request = 1 OR u.other IN (SELECT value FROM json_each(@guardians)))
+       GROUP BY u.other`)
+      .all({ slug, open: messagesAllowed ? 1 : 0, guardians: JSON.stringify([...guardians]) });
     return new Map(rijen.map((r) => [r.other, { n: r.n, wave: !!r.wave }]));
   } catch { return new Map(); }
