Index: src/routes/activitypub.js
===================================================================
--- src/routes/activitypub.js	(revision fb30b5d0f27e2c88d8c14739a2d20267e7a655ed)
+++ src/routes/activitypub.js	(revision 8b07c12ebf1ce49b35e4b1ca78fddc8702616a5b)
@@ -87,4 +87,24 @@
   ).all(site.id);
   AP.sendAP(res, AP.buildOutbox(baseUrl(req), site, posts));
+});
+
+// ── Blocked collection (owner only, AP §5.6) ──────────────────────
+// The server blocklist is the source of truth for Shaer's "in Orbit":
+// clients read it here instead of keeping their own state. Actor-kind
+// blocks only (domain blocks are instance policy, not an Orbit member).
+router.get('/ap/users/:slug/blocked', (req, res) => {
+  const auth = OAuth.verifyBearer(req.headers.authorization);
+  if (!auth || auth.site.slug !== req.params.slug) return res.status(403).end();
+  const base = baseUrl(req);
+  const items = AP.listBlocks(auth.site.slug)
+    .filter((b) => b.kind === 'actor')
+    .map((b) => b.target);
+  AP.sendAP(res, {
+    '@context': AP.AP_CONTEXT,
+    id: `${base}/ap/users/${auth.site.slug}/blocked`,
+    type: 'OrderedCollection',
+    totalItems: items.length,
+    orderedItems: items,
+  });
 });
 
