Index: src/services/CircleService.js
===================================================================
--- src/services/CircleService.js	(revision 57929aaab80d03a3b7ca89b30a1687c618e5b5e8)
+++ src/services/CircleService.js	(revision 4fee2288954d3c22dc060833bc6ef760dc22a2bf)
@@ -6,5 +6,5 @@
 
 import db from '../config/database.js';
-import { verifyBody } from './CircleFederation.js';
+import { verifyBody, KLONKT_PROTO, MIN_PROTO } from './CircleFederation.js';
 import { getTenancy } from './SettingsService.js';
 
@@ -25,4 +25,13 @@
 function baseOf(remoteUrl) {
   return String(remoteUrl).replace(/\/+$/, '');
+}
+
+// Bron buiten de cirkel zetten met een leesbare reden (geen stille mislukking).
+// Aparte status 'outdated' zodat de Beheer-UI er een nette "update vereist"-
+// melding van kan maken i.p.v. een generieke fout.
+function markOutdated(link, msg) {
+  db.prepare("UPDATE circle_links SET status='outdated', last_error=?, last_synced=CURRENT_TIMESTAMP WHERE id=?")
+    .run(String(msg).slice(0, 300), link.id);
+  return { ok: false, outdated: true, link: link.remote_url, error: msg };
 }
 
@@ -36,5 +45,9 @@
       signal: ac.signal,
       redirect: 'follow',
-      headers: { Accept: 'application/activity+json, application/json' },
+      headers: {
+        Accept: 'application/activity+json, application/json',
+        // Vertel de publisher onze proto → die kan ons met 426 weren als we te oud zijn.
+        'Klonkt-Proto': String(KLONKT_PROTO),
+      },
     });
     if (!res.ok) throw new Error(`HTTP ${res.status}`);
@@ -84,4 +97,18 @@
   if (originOf(actorId) !== originOf(actorUrl)) throw new Error('actor.id heeft andere origin dan de actor-URL');
 
+  // Protocol-versie-gate. De proto zit óók in de outbox-handtekening-grondslag,
+  // dus liegen in de (ongetekende) actor helpt niet: bij een echte mismatch faalt
+  // de verificatie verderop alsnog. Hier vooral voor een DUIDELIJKE melding +
+  // buitensluiten zonder stille mislukking.
+  const remoteProto = Number(actor.klonkt && actor.klonkt.proto) || 1;
+  if (remoteProto > KLONKT_PROTO) {
+    return markOutdated(link,
+      `Jouw Klonkt (proto ${KLONKT_PROTO}) is ouder dan ${base} (proto ${remoteProto}). Werk je eigen instance bij om te blijven federeren.`);
+  }
+  if (remoteProto < MIN_PROTO) {
+    return markOutdated(link,
+      `${base} draait een oudere Klonkt (proto ${remoteProto}; minimaal ${MIN_PROTO} vereist). Vraag ze te updaten.`);
+  }
+
   // TOFU: een sleutelwissel vereist expliciete herbevestiging (anti-hijack)
   const existing = db.prepare('SELECT public_key FROM remote_actors WHERE id = ?').get(actorId);
@@ -104,5 +131,5 @@
   const sigHeader = o.headers.get('klonkt-signature') || '';
   const sig = (sigHeader.match(/ed25519=(.+)\s*$/) || [])[1];
-  if (!sig || !verifyBody(o.text, sig, pubKey)) {
+  if (!sig || !verifyBody(o.text, sig, pubKey, remoteProto)) {
     throw new Error('outbox-handtekening ongeldig of ontbreekt');
   }
