Index: src/services/ActivityPubService.js
===================================================================
--- src/services/ActivityPubService.js	(revision 3d7312ab39fe3619ae0a9b953d94789dded655a0)
+++ src/services/ActivityPubService.js	(revision 2826bb97323aa77bce570c916487709c4266732a)
@@ -121,4 +121,7 @@
   for (const m of body.matchAll(/<img\b[^>]*\bsrc="([^"]+)"[^>]*>/gi)) urls.push(abs(m[1]));
   body = body.replace(/<img\b[^>]*>/gi, '');
+  // Strip Klonkt audio shortcodes ([[track:…]] etc.) — they'd federate raw as
+  // ugly text (audio federation itself is a later phase).
+  body = body.replace(/\[\[(track|album|playlist):[^\]]+\]\]/gi, '');
   const seen = new Set();
   const attachment = urls.filter(Boolean)
@@ -488,4 +491,8 @@
   const actor = await fetchActor(actorUri).catch(() => null);
   const ai = actorInfo(actor, actorUri);
+  const rawHtml = String(note.content || '').replace(/\[\[(track|album|playlist):[^\]]+\]\]/gi, '');
+  const images = (Array.isArray(note.attachment) ? note.attachment : [])
+    .filter((a) => a && a.url && (!a.mediaType || /^image\//i.test(a.mediaType)))
+    .map((a) => a.url);
   return {
     object_uri: note.id,
@@ -495,4 +502,7 @@
     actor_name: ai.name,
     actor_icon: ai.icon,
+    url: note.url || url,
+    content: HtmlSanitizerService.sanitize(rawHtml),       // full, sanitized
+    images,
     preview: HtmlSanitizerService.toPlainText(note.content || '').slice(0, 240),
   };
