Index: src/routes/activitypub.js
===================================================================
--- src/routes/activitypub.js	(revision ef519a306e73601f098daf2a3cef9cdaf10a4d20)
+++ src/routes/activitypub.js	(revision cae8ded17a1ccf1df4860076a16aa2d9394881f7)
@@ -174,4 +174,24 @@
   <p class="small">No Shaer? Any fediverse app can follow ${esc(handle)}.</p>
 </main></body></html>`);
+});
+
+// ── Long-poll (owner only, Robins verzoek 31-7) ───────────────────
+// Hold the request until something push-worthy lands for this account, then
+// answer 200 (news: re-read your feed) or 204 after ~25s (nothing: re-arm).
+// The thread in the app stays live without interval polling.
+router.get('/ap/users/:slug/inbox/wait', (req, res) => {
+  const auth = OAuth.verifyBearer(req.headers.authorization);
+  if (!auth || auth.site.slug !== req.params.slug) return res.status(403).end();
+  let settled = false;
+  const done = (code) => {
+    if (settled) return;
+    settled = true;
+    clearTimeout(timer);
+    off();
+    if (!res.headersSent) res.status(code).end();
+  };
+  const off = AP.onNews(auth.site.slug, () => done(200));
+  const timer = setTimeout(() => done(204), 25_000);
+  req.on('close', () => done(204));
 });
 
