Index: src/routes/activitypub.js
===================================================================
--- src/routes/activitypub.js	(revision e4119c22651ba589d6fceee5bc1802a11716f279)
+++ src/routes/activitypub.js	(revision 3155d8cc64d4ccbb40d068c206728faea3faee6a)
@@ -977,6 +977,14 @@
   const ouderLink = (pagina) => (pagina.more && pagina.oldest
     ? `${inboxId}?before=${encodeURIComponent(pagina.oldest)}${size}` : undefined);
-  if (req.query.before) {
-    const pagina = AP.inboxPage(auth.site.slug, { before: String(req.query.before), limit: req.query.limit });
+  // ?random=1 is dezelfde pagina, maar vanaf een willekeurige plek in je
+  // geschiedenis: met inbox-zero aan is 'ouder' alles wat je al doorgenomen
+  // hebt, en dan is een greep eruit zinniger dan een rij ervan. De pagina
+  // noemt zichzelf naar waar hij werkelijk begint, en `next` loopt vanaf daar
+  // gewoon door naar beneden.
+  if (req.query.before || req.query.random) {
+    const pagina = req.query.random
+      ? AP.inboxRandomPage(auth.site.slug, { limit: req.query.limit })
+      : AP.inboxPage(auth.site.slug, { before: String(req.query.before), limit: req.query.limit });
+    const vanaf = req.query.random ? pagina.start : String(req.query.before);
     const ids = (leg) => pagina.rows.filter((r) => r.leg === leg).map((r) => r.ref);
     const tl = new Map(AP.timelineRowsByIds(auth.site.slug, ids('timeline')).map((r) => [r.id, r]));
@@ -1006,5 +1014,5 @@
     return AP.sendAP(res, {
       '@context': AP.AP_CONTEXT,
-      id: `${inboxId}?before=${encodeURIComponent(String(req.query.before))}${size}`,
+      id: vanaf ? `${inboxId}?before=${encodeURIComponent(vanaf)}${size}` : `${inboxId}?random=1${size}`,
       type: 'OrderedCollectionPage',
       partOf: inboxId,
Index: src/services/ActivityPubService.js
===================================================================
--- src/services/ActivityPubService.js	(revision e4119c22651ba589d6fceee5bc1802a11716f279)
+++ src/services/ActivityPubService.js	(revision 3155d8cc64d4ccbb40d068c206728faea3faee6a)
@@ -55,5 +55,5 @@
   getTimeline, replyRowsByUri, timelineRowsByIds, getReplyMessages,
   feedCursor, feedChangesSince, waitForFeedChange,
-  conversationHeads, conversationHistory, inboxPage, messageRowsByUri,
+  conversationHeads, conversationHistory, inboxPage, inboxRandomPage, messageRowsByUri,
   readMarkers, markRead, unreadPerConversation, getDirectMessages,
   isoStamp, timelineAttachments, extractEmojiTags, gateAttachments,
@@ -64,5 +64,5 @@
   getTimeline, replyRowsByUri, timelineRowsByIds, getReplyMessages,
   feedCursor, feedChangesSince, waitForFeedChange,
-  conversationHeads, conversationHistory, inboxPage, messageRowsByUri,
+  conversationHeads, conversationHistory, inboxPage, inboxRandomPage, messageRowsByUri,
   readMarkers, markRead, unreadPerConversation, getDirectMessages,
   isoStamp, timelineAttachments, extractEmojiTags, gateAttachments,
@@ -4395,5 +4395,5 @@
   getInteractions, getInteractionById, setInteractionBoosted, setInteractionLiked, buildReplyNote, getOutboxNote, getSentNotes, deliverReply, resolveRemoteNote, noteAudience, mayReadNote,
   listOutbox, deliverOutboxDelete, deliverOutboxUpdate, deliverDirectNote,
-  webfingerResolve, followActor, resolveRemoteActor, unfollowActor, handleMoveInbox, moveAccount, listFollowing, setAutoBoost, backfillFromOutbox, getTimeline, timelineRowsByIds, contentWarning, getDirectMessages, readMarkers, markRead, unreadPerConversation, messageRowsByUri, replyRowsByUri, conversationHeads, conversationHistory, inboxPage, isoStamp, timelineAttachments, timelineEmojis, timelineObjectLinks, timelineQuote, timelineEmbed, applyQuoteProps, deliverToActor, sendInteraction, voteOnPoll, voteOnRemotePoll,
+  webfingerResolve, followActor, resolveRemoteActor, unfollowActor, handleMoveInbox, moveAccount, listFollowing, setAutoBoost, backfillFromOutbox, getTimeline, timelineRowsByIds, contentWarning, getDirectMessages, readMarkers, markRead, unreadPerConversation, messageRowsByUri, replyRowsByUri, conversationHeads, conversationHistory, inboxPage, inboxRandomPage, isoStamp, timelineAttachments, timelineEmojis, timelineObjectLinks, timelineQuote, timelineEmbed, applyQuoteProps, deliverToActor, sendInteraction, voteOnPoll, voteOnRemotePoll,
   acceptGatedFollow, rejectGatedFollow, isWardGuardian, outboxAudience, sendFollowDecision,
   gateOutgoingFollow, performApprovedFollow, recordGuardianEvent, listGuardianEvents, GUARDIAN_EVENT_KEEP,
Index: src/services/ap-timeline.js
===================================================================
--- src/services/ap-timeline.js	(revision e4119c22651ba589d6fceee5bc1802a11716f279)
+++ src/services/ap-timeline.js	(revision 3155d8cc64d4ccbb40d068c206728faea3faee6a)
@@ -402,4 +402,30 @@
 }
 
+/**
+ * Een pagina vanaf een willekeurige post in je geschiedenis (shaer-ewrt).
+ *
+ * Het anker is een TIJDLIJNRIJ, geen willekeurige rij uit de unie: de app
+ * toont posts in het raster, en een greep die op een stapel berichten landt
+ * laat niets zien. Vanaf daar is het een gewone pagina over alle poten, dus
+ * `start` is een echte cursor en `next` loopt gewoon door.
+ *
+ * `rnd` is er voor de toetsen.
+ */
+export function inboxRandomPage(slug, { limit = 60, rnd = Math.random } = {}) {
+  try {
+    const { n } = db.prepare('SELECT COUNT(*) AS n FROM ap_timeline WHERE slug = ?').get(slug);
+    const k = n ? Math.min(Math.floor(rnd() * n), n - 1) : 0;
+    if (k === 0) return { ...inboxPage(slug, { limit }), start: null };
+    // De rij er net boven: de pagina begint direct daaronder, met het anker
+    // als eerste post. Zelfde stempel en volgorde als de unie.
+    const boven = db.prepare(`
+      SELECT id AS ref, ${STEMPEL('COALESCE(published, created_at)')} AS stamp
+        FROM ap_timeline WHERE slug = ?
+       ORDER BY stamp DESC, ref DESC LIMIT 1 OFFSET ?`).get(slug, k - 1);
+    const start = cursorOf(boven);
+    return { ...inboxPage(slug, { before: start, limit }), start };
+  } catch { return { rows: [], more: false, oldest: null, start: null }; }
+}
+
 // De kolommen die een bericht tot kaart maken. Een constante, want de
 // gesprekslezing haalt dezelfde rows op: twee lijsten die uiteenlopen leveren
Index: test/inbox-ouder.test.js
===================================================================
--- test/inbox-ouder.test.js	(revision e4119c22651ba589d6fceee5bc1802a11716f279)
+++ test/inbox-ouder.test.js	(revision 3155d8cc64d4ccbb40d068c206728faea3faee6a)
@@ -155,2 +155,33 @@
   assert.ok(p.next.endsWith('&limit=10'), 'anders wordt de tweede pagina stilletjes de standaard');
 });
+
+// ── Een greep uit je geschiedenis (inbox-zero) ─────────────────────────────
+
+test('een willekeurige pagina begint bij een post, en loopt van daar gewoon door', () => {
+  // Alle tijdlijnrijen op volgorde, zoals de unie ze ziet.
+  const posts = AP.inboxPage('lezer', { limit: 500 }).rows.filter((r) => r.leg === 'timeline');
+  for (const k of [0, 1, 40, posts.length - 1]) {
+    const p = AP.inboxRandomPage('lezer', { limit: 10, rnd: () => k / posts.length });
+    const eerstePost = p.rows.find((r) => r.leg === 'timeline');
+    assert.equal(eerstePost.ref, posts[k].ref, `anker ${k} is de eerste post van de pagina`);
+    // En het is een echte pagina: met zijn start opnieuw gevraagd geeft hij hetzelfde.
+    if (p.start) assert.deepEqual(AP.inboxPage('lezer', { before: p.start, limit: 10 }).rows, p.rows);
+  }
+  // Een lege geschiedenis is geen fout.
+  assert.deepEqual(AP.inboxRandomPage('niemand', { limit: 10 }).rows, []);
+});
+
+test('?random=1 geeft een pagina die zichzelf naar zijn echte begin noemt', async () => {
+  for (let i = 0; i < 5; i++) {
+    const p = await haal(`http://127.0.0.1:${poort}/ap/users/lezer/inbox?random=1&limit=15`);
+    assert.equal(p.type, 'OrderedCollectionPage');
+    assert.ok(p.partOf.endsWith('/ap/users/lezer/inbox'));
+    assert.ok(p.orderedItems.length > 0);
+    assert.ok(p.orderedItems.some((x) => x.object && String(x.object.id).startsWith('https://elders/n/')), 'er zit een post in');
+    if (p.id.includes('?before=')) {
+      const zelfde = await haal(p.id);
+      assert.deepEqual(zelfde.orderedItems.map(idVan), p.orderedItems.map(idVan), 'het id is een adres, niet een toevalstreffer');
+      assert.ok(p.id.endsWith('&limit=15'));
+    }
+  }
+});
