Index: src/services/ActivityPubService.js
===================================================================
--- src/services/ActivityPubService.js	(revision 0fc5db50dd39bd17eca1fe2c4364ddb56ffd03ec)
+++ src/services/ActivityPubService.js	(revision e1c4f3ec41db2dc35d9f7c394a92ee88930398d6)
@@ -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.
