Index: src/services/ActivityPubService.js
===================================================================
--- src/services/ActivityPubService.js	(revision 64016815dd12ec86d05866d6d71bca783024094d)
+++ src/services/ActivityPubService.js	(revision f59a46cef840d0f6005427b2265304d01d519292)
@@ -29,5 +29,5 @@
 import Blocklist from './BlocklistService.js';
 import * as Guardianship from './guardianship/index.js';
-import { PUBLIC, AP_CONTEXT, safeUrl, actorId, noteId, guessMediaType, normalizeTags, tagParts, hashtagTags, buildHashtagList, pagedCollection, PAGINA_GROOTTE } from './ap-core.js';
+import { PUBLIC, AP_CONTEXT, safeUrl, actorId, noteId, guessMediaType, normalizeTags, tagParts, hashtagTags, buildHashtagList, pagedCollection, PAGINA_GROOTTE, artiestUrl } from './ap-core.js';
 // Doorgeven wat hier altijd vandaan kwam, zodat elke bestaande aanroep blijft werken.
 export { AP_CONTEXT, actorId, noteId, guessMediaType };
@@ -394,4 +394,13 @@
   // and publishers should stop delivering here.
   if (site.moved_to && /^https?:\/\//i.test(String(site.moved_to))) actor.movedTo = String(site.moved_to);
+  // De MusicBrainz-koppeling van de artiest (shaer-mbz). Alleen als hij ZELF
+  // gekozen heeft -- er staat niets als er niets gekoppeld is, want een lege
+  // of geraden verwijzing is erger dan geen.
+  //
+  // schema:sameAs en niet alsoKnownAs: dat laatste is in AS2 voor vroegere
+  // identiteiten van dezelfde actor, en FEP-7628 leunt erop bij een verhuizing.
+  // Een MBID hier neerzetten zou een verhuizing kunnen laten mislukken.
+  const mbUrl = artiestUrl(site.mb_artist_id);
+  if (mbUrl) actor.sameAs = mbUrl;
   // Profile links → PropertyValue rows: Mastodon/PeerTube/WordPress-ActivityPub render these as
   // profile metadata (rel=me enables link-back verification). Additive; ignored by simpler receivers.
Index: src/services/MusicBrainzService.js
===================================================================
--- src/services/MusicBrainzService.js	(revision 64016815dd12ec86d05866d6d71bca783024094d)
+++ src/services/MusicBrainzService.js	(revision f59a46cef840d0f6005427b2265304d01d519292)
@@ -20,4 +20,8 @@
  */
 import { safeFetch } from './ActivityPubService.js';
+// De twee pure vormcontroles wonen in ap-core: ActivityPubService heeft ze ook
+// nodig voor de actor, en zonder die verhuizing zou dat een KRINGLOOP zijn --
+// deze module leent immers safeFetch dáár.
+import { isMbid, artiestUrl } from './ap-core.js';
 
 const BASIS = 'https://musicbrainz.org/ws/2';
@@ -97,13 +101,6 @@
 }
 
-/** Is dit een MBID? Een UUID, en niets anders. */
-export function isMbid(s) {
-  return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(String(s || '').trim());
-}
-
-/** De publieke pagina van een artiest, of null als het geen MBID is. */
-export function artiestUrl(mbid) {
-  return isMbid(mbid) ? `https://musicbrainz.org/artist/${String(mbid).trim().toLowerCase()}` : null;
-}
-
+// Her-geexporteerd zodat een aanroeper er niet over hoeft na te denken waar
+// ze precies wonen.
+export { isMbid, artiestUrl };
 export default { zoekArtiesten, isMbid, artiestUrl };
Index: src/services/ap-core.js
===================================================================
--- src/services/ap-core.js	(revision 64016815dd12ec86d05866d6d71bca783024094d)
+++ src/services/ap-core.js	(revision f59a46cef840d0f6005427b2265304d01d519292)
@@ -50,4 +50,11 @@
     // schema.org, dus we hoeven geen vreemd vocabulaire binnen te halen.
     license: { '@id': 'schema:license', '@type': '@id' },
+    // "Dit ding is ook bekend onder die URI" -- voor de MusicBrainz-koppeling
+    // van een artiest (shaer-mbz). Bewust NIET alsoKnownAs: dat is in AS2
+    // gereserveerd voor vroegere IDENTITEITEN van dezelfde actor, en een
+    // verhuizing leunt erop (FEP-7628). Een verwijzing naar een register is
+    // iets anders dan een oud account van jezelf, en die twee door elkaar halen
+    // zou een verhuizing kunnen laten mislukken.
+    sameAs: { '@id': 'schema:sameAs', '@type': '@id' },
     position: 'schema:position',
     bitrate: 'schema:bitrate',
@@ -268,2 +275,12 @@
   };
 }
+
+/** Is dit een MBID? Een UUID, en niets anders. */
+export function isMbid(s) {
+  return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(String(s || '').trim());
+}
+
+/** De publieke pagina van een artiest, of null als het geen MBID is. */
+export function artiestUrl(mbid) {
+  return isMbid(mbid) ? `https://musicbrainz.org/artist/${String(mbid).trim().toLowerCase()}` : null;
+}
Index: test/musicbrainz.test.js
===================================================================
--- test/musicbrainz.test.js	(revision 64016815dd12ec86d05866d6d71bca783024094d)
+++ test/musicbrainz.test.js	(revision f59a46cef840d0f6005427b2265304d01d519292)
@@ -124,2 +124,40 @@
   assert.equal(s.mb_artist_name, null, 'anders blijft er een naam staan zonder koppeling');
 });
+
+// ── Stap 3: op de draad ───────────────────────────────────────────────────
+
+test('de actor draagt sameAs zodra er gekoppeld is -- en anders niets', async () => {
+  const db = dbMod.default;
+  const AP = await import('../src/services/ActivityPubService.js');
+  const BASE = 'https://ons.test';
+  const kaal = db.prepare("SELECT * FROM sites WHERE id = 's1'").get();
+  assert.equal(AP.buildActor(BASE, kaal).sameAs, undefined,
+    'zonder koppeling staat er niets -- een lege verwijzing is erger dan geen');
+
+  db.prepare('UPDATE sites SET mb_artist_id = ? WHERE id = ?')
+    .run('8be31978-1884-4773-beae-f73df35b92aa', 's1');
+  const gekoppeld = db.prepare("SELECT * FROM sites WHERE id = 's1'").get();
+  const actor = AP.buildActor(BASE, gekoppeld);
+  assert.equal(actor.sameAs, 'https://musicbrainz.org/artist/8be31978-1884-4773-beae-f73df35b92aa');
+});
+
+test('rommel in de kolom komt NIET op de draad', () => {
+  // Het scherm zeeft al, maar de actor is de laatste deur. Wat hier langskomt
+  // gaat naar iedereen, en een half adres is erger dan geen.
+  const db = dbMod.default;
+  db.prepare('UPDATE sites SET mb_artist_id = ? WHERE id = ?').run('nirvana', 's1');
+  const s = db.prepare("SELECT * FROM sites WHERE id = 's1'").get();
+  assert.equal(MB.artiestUrl(s.mb_artist_id), null);
+});
+
+test('sameAs is een eigen term en NIET alsoKnownAs', async () => {
+  // alsoKnownAs is in AS2 voor vroegere IDENTITEITEN van dezelfde actor, en
+  // FEP-7628 leunt erop bij een verhuizing: het oude adres controleert of het
+  // nieuwe hem daar noemt. Een MBID daar neerzetten zou een verhuizing kunnen
+  // laten mislukken.
+  const core = await import('../src/services/ap-core.js');
+  const term = core.AP_CONTEXT.find((x) => x && typeof x === 'object' && x.sameAs);
+  assert.ok(term, 'de term is gedeclareerd, anders laat een strikte lezer hem vallen');
+  assert.equal(term.sameAs['@id'], 'schema:sameAs');
+  assert.equal(term.sameAs['@type'], '@id', 'het is een URI en geen tekst');
+});
