Index: src/routes/activitypub.js
===================================================================
--- src/routes/activitypub.js	(revision e2150909dd29bd970c606ecdc6b09f233415d3a8)
+++ src/routes/activitypub.js	(revision 383e8976c0201c4092866eefefb420ac6cedd57a)
@@ -562,5 +562,8 @@
   // dezelfde categorie als shaer:liked. Niet in totalItems persen: dat betekent
   // 'hoeveel er zijn' en niet 'hoeveel jij nog niet zag'.
-  const ongelezen = AP.unreadPerConversation(auth.site.slug);
+  const ongelezen = AP.unreadPerConversation(auth.site.slug, {
+    messagesAllowed: P.messagesAllowed,
+    guardians: (() => { try { return new Set(Guardianship.listGuardians(auth.site.slug).map((g) => g.other_uri)); } catch { return new Set(); } })(),
+  });
   const gezien = new Set();
   for (const { head, item } of pairs) {
Index: src/services/ActivityPubService.js
===================================================================
--- src/services/ActivityPubService.js	(revision e2150909dd29bd970c606ecdc6b09f233415d3a8)
+++ 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(); }
