Index: src/routes/posts.js
===================================================================
--- src/routes/posts.js	(revision 084d1c01710677dc82d3a088187a7e04686895a8)
+++ src/routes/posts.js	(revision e0a1ec1d9cfde10c3acb385c2a8c253a5501b6b9)
@@ -169,4 +169,33 @@
 
 // ==================== CREATE POST ====================
+// ── Per-post audio federation ──────────────────────────────────────────────
+// "Share audio on the fediverse" is a per-post choice in the editor, but the underlying
+// flag is per track (audio_tracks.fedi_open — it gates the file + drives the AS2 Audio
+// attachment). NB: the file gate is per file, so opening a track in one post makes its file
+// fetchable for every post that reuses it.
+function setAudioFediOpen(siteId, content, open) {
+  const val = open ? 1 : 0;
+  const c = content || '';
+  try {
+    for (const m of c.matchAll(/\[\[track:([A-Za-z0-9_-]+)\]\]/g)) db.prepare('UPDATE audio_tracks SET fedi_open = ? WHERE id = ? AND site_id = ?').run(val, m[1], siteId);
+    for (const m of c.matchAll(/\[\[album:([^\]]+)\]\]/g)) db.prepare('UPDATE audio_tracks SET fedi_open = ? WHERE site_id = ? AND album = ?').run(val, siteId, m[1].trim());
+    for (const m of c.matchAll(/\[\[playlist:([A-Za-z0-9_-]+)\]\]/g)) db.prepare('UPDATE audio_tracks SET fedi_open = ? WHERE id IN (SELECT track_id FROM playlist_tracks WHERE playlist_id = ?)').run(val, m[1]);
+  } catch { /* non-fatal */ }
+}
+// True when the post references hosted audio AND all of it is currently fedi_open (drives the
+// editor checkbox's initial state).
+function postAudioFediOpen(siteId, content) {
+  const c = content || '';
+  if (!/\[\[(track|album|playlist):/i.test(c)) return false;
+  let total = 0, open = 0;
+  const tally = (r) => { if (r && r.media_id) { total++; if (r.fedi_open) open++; } };
+  try {
+    for (const m of c.matchAll(/\[\[track:([A-Za-z0-9_-]+)\]\]/g)) tally(db.prepare('SELECT fedi_open, media_id FROM audio_tracks WHERE id = ? AND site_id = ?').get(m[1], siteId));
+    for (const m of c.matchAll(/\[\[album:([^\]]+)\]\]/g)) for (const r of db.prepare('SELECT fedi_open, media_id FROM audio_tracks WHERE site_id = ? AND album = ? AND media_id IS NOT NULL').all(siteId, m[1].trim())) tally(r);
+    for (const m of c.matchAll(/\[\[playlist:([A-Za-z0-9_-]+)\]\]/g)) for (const r of db.prepare('SELECT t.fedi_open, t.media_id FROM playlist_tracks pt JOIN audio_tracks t ON t.id = pt.track_id WHERE pt.playlist_id = ? AND t.media_id IS NOT NULL').all(m[1])) tally(r);
+  } catch { /* non-fatal */ }
+  return total > 0 && open === total;
+}
+
 router.post('/posts/create', requireAuth, (req, res) => {
   const site = res.locals.site;
@@ -228,4 +257,8 @@
   );
 
+  // Per-post "share audio on the fediverse" → set fedi_open on this post's hosted tracks
+  // BEFORE federating, so the Create note carries the right Audio attachments.
+  setAudioFediOpen(site.id, cleanContent, req.body.fedi_open_audio);
+
   if (finalStatus === 'published') {
     try {
@@ -278,4 +311,5 @@
     post,
     isNew: false,
+    fediOpenAudio: postAudioFediOpen(site.id, post.content),
     pageTitle: 'Edit: ' + (post.title || 'Untitled'),
     bodyClass: 'on-special',
@@ -349,4 +383,8 @@
     finalSlug, publishedAt, now, post.id
   );
+
+  // Per-post "share audio on the fediverse" → set fedi_open on this post's hosted tracks
+  // BEFORE federating, so the Update/Create note carries the right Audio attachments.
+  setAudioFediOpen(site.id, cleanContent, req.body.fedi_open_audio);
 
   // Update FTS
Index: src/services/i18n.js
===================================================================
--- src/services/i18n.js	(revision 084d1c01710677dc82d3a088187a7e04686895a8)
+++ src/services/i18n.js	(revision e0a1ec1d9cfde10c3acb385c2a8c253a5501b6b9)
@@ -694,5 +694,5 @@
     'pedit.pin_top': 'bovenaan',
     'pedit.pin_nth_suffix': 'e van boven',
-    'pedit.noindex_label': 'noindex (verberg voor zoekmachines)', 'pedit.nsfw_label': 'NSFW / gevoelige inhoud', 'pedit.nsfw_cw_ph': 'Waarschuwingstekst (optioneel, standaard: Gevoelige inhoud)', 'post.nsfw_warning': 'Gevoelige inhoud', 'post.nsfw_show': 'Tonen',
+    'pedit.noindex_label': 'noindex (verberg voor zoekmachines)', 'pedit.nsfw_label': 'NSFW / gevoelige inhoud', 'pedit.fedi_audio_label': 'Audio openbaar delen op de fediverse (speelt inline in apps; bestand downloadbaar)', 'pedit.nsfw_cw_ph': 'Waarschuwingstekst (optioneel, standaard: Gevoelige inhoud)', 'post.nsfw_warning': 'Gevoelige inhoud', 'post.nsfw_show': 'Tonen',
     'pedit.fan_only_label': 'Alleen voor ingelogde fans',
     'pedit.schedule_label': 'Publicatie inplannen',
@@ -1609,5 +1609,5 @@
     'pedit.pin_top': 'at the top',
     'pedit.pin_nth_suffix': 'th from top',
-    'pedit.noindex_label': 'noindex (hide from search engines)', 'pedit.nsfw_label': 'NSFW / sensitive content', 'pedit.nsfw_cw_ph': 'Warning text (optional, default: Sensitive content)', 'post.nsfw_warning': 'Sensitive content', 'post.nsfw_show': 'Show',
+    'pedit.noindex_label': 'noindex (hide from search engines)', 'pedit.nsfw_label': 'NSFW / sensitive content', 'pedit.fedi_audio_label': 'Share audio openly on the fediverse (plays inline in apps; file downloadable)', 'pedit.nsfw_cw_ph': 'Warning text (optional, default: Sensitive content)', 'post.nsfw_warning': 'Sensitive content', 'post.nsfw_show': 'Show',
     'pedit.fan_only_label': 'Logged-in fans only',
     'pedit.schedule_label': 'Schedule publication',
@@ -2524,5 +2524,5 @@
     'pedit.pin_top': 'ganz oben',
     'pedit.pin_nth_suffix': '. von oben',
-    'pedit.noindex_label': 'noindex (vor Suchmaschinen verbergen)', 'pedit.nsfw_label': 'NSFW / sensibler Inhalt', 'pedit.nsfw_cw_ph': 'Warntext (optional, Standard: Sensibler Inhalt)', 'post.nsfw_warning': 'Sensibler Inhalt', 'post.nsfw_show': 'Anzeigen',
+    'pedit.noindex_label': 'noindex (vor Suchmaschinen verbergen)', 'pedit.nsfw_label': 'NSFW / sensibler Inhalt', 'pedit.fedi_audio_label': 'Audio offen im Fediverse teilen (spielt inline in Apps; Datei herunterladbar)', 'pedit.nsfw_cw_ph': 'Warntext (optional, Standard: Sensibler Inhalt)', 'post.nsfw_warning': 'Sensibler Inhalt', 'post.nsfw_show': 'Anzeigen',
     'pedit.fan_only_label': 'Nur für angemeldete Fans',
     'pedit.schedule_label': 'Veröffentlichung planen',
Index: src/views/pages/post-edit.ejs
===================================================================
--- src/views/pages/post-edit.ejs	(revision 084d1c01710677dc82d3a088187a7e04686895a8)
+++ src/views/pages/post-edit.ejs	(revision e0a1ec1d9cfde10c3acb385c2a8c253a5501b6b9)
@@ -245,4 +245,8 @@
                placeholder="<%= t('pedit.nsfw_cw_ph') %>"
                style="width:100%;box-sizing:border-box;margin:6px 0 2px;font-size:13px;padding:7px 9px">
+        <label class="pe-checkbox">
+          <input type="checkbox" name="fedi_open_audio" value="1" <%= (typeof fediOpenAudio !== 'undefined' && fediOpenAudio) ? 'checked' : '' %>>
+          <span>🌐 <%= t('pedit.fedi_audio_label') %></span>
+        </label>
         <script>
         (function () {
