Index: src/services/ActivityPubService.js
===================================================================
--- src/services/ActivityPubService.js	(revision d988fa056c5c2e97a7cf67c778246b1a3e9f9e2b)
+++ src/services/ActivityPubService.js	(revision 00f669baaff99e0e44d5d5291bb86169ba3f5be3)
@@ -737,4 +737,29 @@
 }
 
+// Notifications inbox: new followers + replies/likes/boosts on this site's posts.
+export function getNotifications(slug, limit) {
+  const out = [];
+  try {
+    for (const f of db.prepare('SELECT actor_uri, created_at FROM ap_followers WHERE slug = ? ORDER BY created_at DESC LIMIT 50').all(slug)) {
+      out.push({ type: 'follow', handle: deriveHandle(f.actor_uri), url: f.actor_uri, created_at: f.created_at });
+    }
+  } catch { /* ignore */ }
+  try {
+    const rows = db.prepare(`
+      SELECT i.kind, i.actor_name, i.actor_handle, i.actor_url, i.content, i.created_at,
+             p.slug AS post_slug, p.title AS post_title
+      FROM ap_interactions i LEFT JOIN posts p ON p.id = i.post_id
+      WHERE p.site_id = (SELECT id FROM sites WHERE slug = ?)
+      ORDER BY i.created_at DESC LIMIT 80
+    `).all(slug);
+    for (const r of rows) out.push({
+      type: r.kind, name: r.actor_name, handle: r.actor_handle, url: r.actor_url,
+      content: r.content, post_slug: r.post_slug, post_title: r.post_title, created_at: r.created_at,
+    });
+  } catch { /* ignore */ }
+  out.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
+  return out.slice(0, limit || 60);
+}
+
 export default {
   getOrCreateKeys, apWants, sendAP, actorId, noteId,
@@ -744,3 +769,4 @@
   listOutbox, deliverOutboxDelete,
   webfingerResolve, followActor, unfollowActor, listFollowing, getTimeline, sendInteraction,
+  getNotifications,
 };
