Index: src/services/ActivityPubService.js
===================================================================
--- src/services/ActivityPubService.js	(revision 2f50c533f5a3f95640b17e833ac1bb7bc0207ac1)
+++ src/services/ActivityPubService.js	(revision 1b1cc89338acb01d612f09ac46cef0274eae8981)
@@ -1662,6 +1662,8 @@
   fwStmts().ins.run(site.slug, actor.id, ai.handle, ai.name, ai.icon, ai.url, actor.inbox, followId, 'pending', autoBoost ? 1 : 0);
   const follow = { '@context': AP_CONTEXT, id: followId, type: 'Follow', actor: me, object: actor.id };
-  try { await deliver(actor.inbox, follow, `${me}#main-key`, keys.private_pem); }
-  catch (e) { console.warn('[AP] follow deliver failed:', e.message); }
+  // Deliver via the retry queue: a Follow that fails the first attempt (peer down,
+  // timeout, transient 5xx) is retried with backoff instead of staying stuck on
+  // 'pending' forever — the Accept can only come back once the Follow lands.
+  await deliverWithRetry(site.slug, actor.inbox, follow, `${me}#main-key`, keys.private_pem);
   console.log('[AP] follow', site.slug, '→', actor.id);
   // Follow + feature in one step → backfill their recent posts into the Cirkel right away.
