Index: src/routes/admin-audio.js
===================================================================
--- src/routes/admin-audio.js	(revision 19e28cedce7ba0de89244415d0c1f285866cd75a)
+++ src/routes/admin-audio.js	(revision ca260d13a940f7563a540fad1f1945ef28e9160c)
@@ -24,4 +24,6 @@
 import { mediaDir } from '../config/paths.js';
 import * as ActivityPubService from '../services/ActivityPubService.js';
+import { postsEmbeddingTrack } from '../services/music/index.js';
+import { t as i18nT, resolveLang } from '../services/i18n.js';
 
 const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -96,5 +98,6 @@
   const rows = db.prepare(`
     SELECT t.id, t.title, t.artist, t.album, t.duration, t.cover_url,
-           t.position, t.created_at, t.downloadable, m.filename, m.size, m.mime_type
+           t.position, t.created_at, t.downloadable, t.fedi_open, t.media_id,
+           m.filename, m.size, m.mime_type
     FROM audio_tracks t
     LEFT JOIN media m ON m.id = t.media_id
@@ -290,4 +293,60 @@
 });
 
+/**
+ * Een track openbaar op de fediverse zetten, vanuit de mediamanager (Robin,
+ * 30-9: "Fedi open audio ook per track aan kunnen zetten").
+ *
+ * Tot nu toe kon dat alleen per POST, met het vinkje in de editor, dat elke
+ * track in die post opende. De vlag zelf was altijd al per track
+ * (audio_tracks.fedi_open): hij opent het BESTAND en maakt er een echte
+ * Audio-bijlage van. Dit is dezelfde vlag, met een eigen knop.
+ *
+ * EENRICHTINGS, net als in de editor. Een bestand dat al gefedereerd is, staat
+ * op andere servers; het hier weer afschermen zou schijnveiligheid zijn. Er is
+ * dus geen weg terug, en die bouwen we ook niet.
+ *
+ * EN DE POSTS GAAN OPNIEUW DE DEUR UIT. Het vinkje in de editor werkt zichtbaar
+ * omdat het opslaan van de post een Update stuurt, met de audio erin. Een knop
+ * die alleen de vlag zet, laat elke post die al verstuurd is zonder speler
+ * staan tot iemand hem bewerkt. Dus hier dezelfde Update, voor elke
+ * gepubliceerde post waar de track in staat -- rechtstreeks, via een playlist of
+ * via zijn album. De bibliotheek (Funkwhale) hoeft niets: die wordt opgehaald,
+ * en een open track staat er meteen in.
+ *
+ * Alleen een track met een eigen BESTAND: een track die alleen een link is heeft
+ * niets om te openen.
+ *
+ * `deliver` is er voor de toetsen; de route gebruikt de echte deliverUpdate.
+ */
+export function openTrackOnFediverse(site, trackId, { deliver = (s, p) => ActivityPubService.deliverUpdate(s, p) } = {}) {
+  const t = db.prepare('SELECT id, media_id, fedi_open FROM audio_tracks WHERE id = ? AND site_id = ?').get(trackId, site.id);
+  if (!t) return { ok: false, reason: 'not_found' };
+  if (!t.media_id) return { ok: false, reason: 'no_file' };
+  if (t.fedi_open) return { ok: true, changed: false, updated: 0 };
+  db.prepare('UPDATE audio_tracks SET fedi_open = 1 WHERE id = ? AND site_id = ?').run(trackId, site.id);
+  const postIds = postsEmbeddingTrack(site.id, trackId);
+  for (const id of postIds) {
+    const post = db.prepare('SELECT * FROM posts WHERE id = ? AND site_id = ?').get(id, site.id);
+    if (!post) continue;
+    // Op de achtergrond: een trage volger houdt de knop niet vast.
+    Promise.resolve()
+      .then(() => deliver(site, post))
+      .catch((e) => console.warn('[admin-audio] Update na openen mislukt:', post.id, e.message));
+  }
+  return { ok: true, changed: true, updated: postIds.length };
+}
+
+router.post('/:id/fedi-open', requireGod, (req, res) => {
+  const site = res.locals.site;
+  if (!site) return res.status(404).send('Site required');
+  const lang = resolveLang(req, { userLang: req.session?.user?.lang });
+  const r = openTrackOnFediverse(site, req.params.id);
+  if (!r.ok) {
+    const msg = r.reason === 'no_file' ? i18nT(lang, 'aaud.fedi_no_file') : 'Not found';
+    return res.redirect('/admin/audio?error=' + encodeURIComponent(msg));
+  }
+  res.redirect('/admin/audio?success=' + encodeURIComponent(i18nT(lang, 'aaud.fedi_opened', { n: r.updated })));
+});
+
 router.post('/:id/delete', requireGod, (req, res) => {
   const site = res.locals.site;
Index: src/services/i18n.js
===================================================================
--- src/services/i18n.js	(revision 19e28cedce7ba0de89244415d0c1f285866cd75a)
+++ src/services/i18n.js	(revision ca260d13a940f7563a540fad1f1945ef28e9160c)
@@ -400,5 +400,5 @@
     'aaud.delete': 'Verwijderen',
     'aaud.delete_confirm': 'Track verwijderen?',
-    'aaud.dl_on': 'Download-voor-email staat AAN — klik om uit te zetten',
+    'aaud.fedi_open_btn': 'Openbaar delen op de fediverse', 'aaud.fedi_open_on': 'Openbaar gedeeld op de fediverse. Dat is permanent.', 'aaud.fedi_open_confirm': 'Deze track openbaar delen op de fediverse? Hij wordt speelbaar in apps en het bestand is te downloaden. Posts met deze track gaan opnieuw naar je volgers. Dit is permanent en niet terug te draaien.', 'aaud.fedi_opened': 'Openbaar gedeeld op de fediverse. Posts opnieuw verstuurd: {n}.', 'aaud.fedi_no_file': 'Deze track heeft geen eigen bestand, alleen een link. Er valt niets te openen.', 'aaud.dl_on': 'Download-voor-email staat AAN — klik om uit te zetten',
     'aaud.dl_off': 'Download-voor-email staat uit — klik om aan te zetten',
     'aaud.fedi_on': 'Op de fediverse gedeeld (speelt overal inline, bestand downloadbaar) — klik om uit te zetten',
@@ -1410,5 +1410,5 @@
     'aaud.delete': 'Delete',
     'aaud.delete_confirm': 'Delete track?',
-    'aaud.dl_on': 'Download-for-email is ON — click to turn off',
+    'aaud.fedi_open_btn': 'Share openly on the fediverse', 'aaud.fedi_open_on': 'Shared openly on the fediverse. This is permanent.', 'aaud.fedi_open_confirm': 'Share this track openly on the fediverse? It becomes playable in apps and the file can be downloaded. Posts with this track are sent to your followers again. This is permanent and cannot be undone.', 'aaud.fedi_opened': 'Shared openly on the fediverse. Posts sent again: {n}.', 'aaud.fedi_no_file': 'This track has no file of its own, only a link. There is nothing to open.', 'aaud.dl_on': 'Download-for-email is ON — click to turn off',
     'aaud.dl_off': 'Download-for-email is off — click to turn on',
     'aaud.fedi_on': 'Shared on the fediverse (plays inline everywhere, file downloadable) — click to turn off',
@@ -2419,5 +2419,5 @@
     'aaud.delete': 'Löschen',
     'aaud.delete_confirm': 'Track löschen?',
-    'aaud.dl_on': 'Download-für-E-Mail ist AN — zum Ausschalten klicken',
+    'aaud.fedi_open_btn': 'Offen im Fediverse teilen', 'aaud.fedi_open_on': 'Offen im Fediverse geteilt. Das ist dauerhaft.', 'aaud.fedi_open_confirm': 'Diesen Track offen im Fediverse teilen? Er wird in Apps abspielbar und die Datei kann heruntergeladen werden. Beiträge mit diesem Track werden erneut an deine Follower gesendet. Das ist dauerhaft und kann nicht rückgängig gemacht werden.', 'aaud.fedi_opened': 'Offen im Fediverse geteilt. Erneut gesendete Beiträge: {n}.', 'aaud.fedi_no_file': 'Dieser Track hat keine eigene Datei, nur einen Link. Es gibt nichts zu öffnen.', 'aaud.dl_on': 'Download-für-E-Mail ist AN — zum Ausschalten klicken',
     'aaud.dl_off': 'Download-für-E-Mail ist aus — zum Einschalten klicken',
     'aaud.fedi_on': 'Im Fediverse geteilt (spielt überall inline, Datei herunterladbar) — zum Ausschalten klicken',
Index: src/services/music/index.js
===================================================================
--- src/services/music/index.js	(revision 19e28cedce7ba0de89244415d0c1f285866cd75a)
+++ src/services/music/index.js	(revision ca260d13a940f7563a540fad1f1945ef28e9160c)
@@ -134,4 +134,38 @@
   for (const r of rijen) if (!uit.has(r.tid)) uit.set(r.tid, { id: r.post_id, slug: r.post_slug });
   return uit;
+}
+
+/**
+ * Elke GEPUBLICEERDE post waar deze track in staat, op welke manier ook:
+ * rechtstreeks ([[track:id]]), via een playlist of via zijn albumnaam.
+ *
+ * Voor iets anders dan trackHostPosts, dat per track EEN post kiest voor de
+ * bibliotheek. Opent de mediamanager een track (shaer, 30-9), dan moeten ALLE
+ * posts met die track opnieuw de deur uit, anders krijgt een volger de speler
+ * pas bij de volgende bewerking van die post.
+ *
+ * Alleen posts van deze site: playlists.id is een globale sleutel, en het
+ * filter hoort op de post en de track, niet op de playlist (zie de
+ * tenancy-toets bij setAudioFediOpen).
+ */
+export function postsEmbeddingTrack(siteId, trackId) {
+  const t = db.prepare('SELECT id, album FROM audio_tracks WHERE id = ? AND site_id = ?').get(trackId, siteId);
+  if (!t) return [];
+  const rows = db.prepare(`
+    SELECT p.id FROM posts p
+     WHERE p.site_id = ? AND p.status = 'published'
+       AND p.content LIKE '%[[track:' || ? || ']]%'
+    UNION
+    SELECT p.id FROM playlist_tracks pt
+      JOIN posts p ON p.site_id = ? AND p.status = 'published'
+                  AND p.content LIKE '%[[playlist:' || pt.playlist_id || ']]%'
+     WHERE pt.track_id = ?
+    UNION
+    SELECT p.id FROM posts p
+     WHERE p.site_id = ? AND p.status = 'published'
+       AND ? IS NOT NULL AND ? <> ''
+       AND p.content LIKE '%[[album:' || ? || ']]%'
+  `).all(siteId, t.id, siteId, t.id, siteId, t.album, t.album, t.album);
+  return rows.map((r) => r.id);
 }
 
Index: src/views/pages/admin-audio.ejs
===================================================================
--- src/views/pages/admin-audio.ejs	(revision 19e28cedce7ba0de89244415d0c1f285866cd75a)
+++ src/views/pages/admin-audio.ejs	(revision ca260d13a940f7563a540fad1f1945ef28e9160c)
@@ -91,4 +91,7 @@
         var _aaudCopy     = t('aaud.copy_click');
         var _aaudDelConfirm = t('aaud.delete_confirm');
+        var _aaudFediBtn     = t('aaud.fedi_open_btn');
+        var _aaudFediOn      = t('aaud.fedi_open_on');
+        var _aaudFediConfirm = t('aaud.fedi_open_confirm');
       %>
       <ul class="ax-list">
@@ -125,4 +128,19 @@
                         aria-label="<%= t.downloadable ? _aaudDlOn : _aaudDlOff %>" title="<%= t.downloadable ? _aaudDlOn : _aaudDlOff %>"
                         style="<%= t.downloadable ? 'color:var(--accent,#6b8f71)' : 'opacity:.5' %>">⬇</button>
+              <% } %>
+              <%# Openbaar op de fediverse, per track (Robin, 30-9). EENRICHTINGS, net
+                  als het vinkje in de post-editor: open is een slotje en geen knop meer,
+                  want een bestand dat al verspreid is haal je niet terug. Alleen bij een
+                  track met een eigen bestand; een link heeft niets om te openen. Een
+                  formulier en geen fetch: werkt zonder JS, en data-confirm vraagt het
+                  eerst (shell.ejs). %>
+              <% if (t.media_id) { %>
+                <% if (t.fedi_open) { %>
+                  <span class="ax-icon-btn ax-fedi-on" role="img" aria-label="<%= _aaudFediOn %>" title="<%= _aaudFediOn %>"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg></span>
+                <% } else { %>
+                  <form action="/admin/audio/<%= t.id %>/fedi-open" method="post" data-confirm="<%= _aaudFediConfirm %>" class="ax-track-fedi">
+                    <button type="submit" class="ax-icon-btn ax-fedi-off" aria-label="<%= _aaudFediBtn %>" title="<%= _aaudFediBtn %>"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg></button>
+                  </form>
+                <% } %>
               <% } %>
               <form action="/admin/audio/<%= t.id %>/delete" method="post" data-confirm="<%= _aaudDelConfirm %>" class="ax-track-delete">
@@ -247,4 +265,10 @@
 }
 .ax-file-name[data-empty] { color: var(--ink-muted, var(--ink-soft)); }
+/* Openbaar op de fediverse: dicht is gedimd, net als een uitgezette download;
+   open is de accentkleur en geen knop meer, want terug kan niet. */
+.ax-track-fedi { display: inline; }
+.ax-fedi-off { opacity: .5; }
+.ax-fedi-off:hover, .ax-fedi-off:focus-visible { opacity: 1; }
+.ax-fedi-on { color: var(--accent, #6b8f71); cursor: default; }
 
 .ax-form-actions {
