Index: src/assets/css/audio.css
===================================================================
--- src/assets/css/audio.css	(revision fef781eb8f9efb0e0be591707a4e79aa60191494)
+++ src/assets/css/audio.css	(revision 0d7acdfd541ca8374f3adc7a5f67f2ca00f4f4ef)
@@ -69,4 +69,13 @@
   font-size: 0.85rem;
   color: var(--ink-muted, var(--ink-soft));
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+/* Eigenaar/credit · licentie — subtiele regel onder de track. */
+.post-audio-track .pat-credit {
+  font-size: 0.72rem;
+  color: var(--ink-faint, var(--ink-soft));
+  margin-top: 0.1rem;
   white-space: nowrap;
   overflow: hidden;
Index: src/config/database.js
===================================================================
--- src/config/database.js	(revision fef781eb8f9efb0e0be591707a4e79aa60191494)
+++ src/config/database.js	(revision 0d7acdfd541ca8374f3adc7a5f67f2ca00f4f4ef)
@@ -90,4 +90,6 @@
   ensureColumn('audio_tracks', 'play_count', 'INTEGER DEFAULT 0');  // plays per track
   ensureColumn('audio_tracks', 'downloadable', 'INTEGER DEFAULT 0'); // download-voor-email (premium #2)
+  ensureColumn('audio_tracks', 'credit', 'TEXT');   // eigenaar/credit (copyright-houder)
+  ensureColumn('audio_tracks', 'license', 'TEXT');  // licentie (bv. "CC BY 4.0", "Alle rechten voorbehouden")
 
   // Playlists (v9 feature) — first-class entity. CREATE IF NOT EXISTS is
Index: src/routes/admin-audio.js
===================================================================
--- src/routes/admin-audio.js	(revision fef781eb8f9efb0e0be591707a4e79aa60191494)
+++ src/routes/admin-audio.js	(revision 0d7acdfd541ca8374f3adc7a5f67f2ca00f4f4ef)
@@ -20,5 +20,5 @@
 import { toWebp } from '../services/ImageWebpService.js';
 import { requireGod } from '../middleware/auth.js';
-import { transcodeToMp3 } from '../services/AudioTranscoder.js';
+import { transcodeToMp3, retagMp3 } from '../services/AudioTranscoder.js';
 import { audioUrl } from '../services/AudioStreamService.js';
 
@@ -168,4 +168,8 @@
     const finalArtist = artist?.trim() || null;
     const finalAlbum  = album?.trim() || null;
+    // Eigenaarschap/licentie. credit valt terug op de artiest; deze gaan zowel de
+    // DB in als de ID3-tags van de mp3 (copyright + comment).
+    const finalCredit  = (req.body.credit  || '').trim() || finalArtist || null;
+    const finalLicense = (req.body.license || '').trim() || null;
 
     console.log('[admin-audio] upload received:', {
@@ -186,4 +190,6 @@
           artist: finalArtist || undefined,
           album: finalAlbum || undefined,
+          copyright: finalCredit || undefined,
+          comment: finalLicense || undefined,
         },
       });
@@ -216,6 +222,6 @@
       console.log('[admin-audio] inserting audio_tracks row (duration=' + finalDuration + ')');
       db.prepare(`
-        INSERT INTO audio_tracks (id, site_id, title, artist, album, duration, cover_url, media_id, position)
-        VALUES (?, ?, ?, ?, ?, ?, ?, ?, COALESCE(
+        INSERT INTO audio_tracks (id, site_id, title, artist, album, duration, cover_url, credit, license, media_id, position)
+        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, COALESCE(
           (SELECT MAX(position) + 1 FROM audio_tracks WHERE site_id = ?),
           0
@@ -226,4 +232,5 @@
         finalDuration,
         coverUrl,
+        finalCredit, finalLicense,
         mediaId, site.id
       );
@@ -371,5 +378,5 @@
   const t = db.prepare(`
     SELECT t.id, t.title, t.artist, t.album, t.duration, t.cover_url,
-           t.position, t.created_at, m.filename
+           t.credit, t.license, t.position, t.created_at, m.filename
     FROM audio_tracks t LEFT JOIN media m ON m.id = t.media_id
     WHERE t.id = ? AND t.site_id = ?
@@ -388,5 +395,5 @@
  * fallback keeps working.
  */
-router.post('/api/:id', requireGod, express.json(), (req, res) => {
+router.post('/api/:id', requireGod, express.json(), async (req, res) => {
   const site = res.locals.site;
   if (!site) return res.status(404).json({ error: 'Site required' });
@@ -433,4 +440,10 @@
     fields.push('downloadable = ?'); values.push(body.downloadable ? 1 : 0);
   }
+  if (Object.prototype.hasOwnProperty.call(body, 'credit')) {
+    fields.push('credit = ?'); values.push(String(body.credit || '').trim() || null);
+  }
+  if (Object.prototype.hasOwnProperty.call(body, 'license')) {
+    fields.push('license = ?'); values.push(String(body.license || '').trim() || null);
+  }
 
   if (fields.length === 0) {
@@ -445,10 +458,29 @@
   }
 
-  // Return fresh row so the caller can update its UI without reloading
+  // Verse rij + (als tag-velden wijzigden) de mp3 her-taggen, zodat de eigenaar/
+  // licentie ook IN het bestand staat (ID3) en meereist bij een download.
   const fresh = db.prepare(`
-    SELECT id, title, artist, album, duration, cover_url
-    FROM audio_tracks WHERE id = ? AND site_id = ?
+    SELECT t.id, t.title, t.artist, t.album, t.duration, t.cover_url, t.credit, t.license, m.storage_path
+    FROM audio_tracks t LEFT JOIN media m ON m.id = t.media_id
+    WHERE t.id = ? AND t.site_id = ?
   `).get(req.params.id, site.id);
-  res.json({ ok: true, track: fresh });
+
+  const tagsChanged = ['title', 'artist', 'album', 'credit', 'license']
+    .some((f) => Object.prototype.hasOwnProperty.call(body, f));
+  if (fresh && fresh.storage_path && tagsChanged) {
+    try {
+      await retagMp3({ filePath: fresh.storage_path, tags: {
+        title: fresh.title || undefined,
+        artist: fresh.artist || undefined,
+        album: fresh.album || undefined,
+        copyright: fresh.credit || undefined,
+        comment: fresh.license || undefined,
+      } });
+    } catch (e) {
+      console.warn('[admin-audio] ID3 her-taggen mislukt (DB is wel bijgewerkt):', e.message);
+    }
+  }
+  const { storage_path, ...trackOut } = fresh || {};
+  res.json({ ok: true, track: trackOut });
 });
 
Index: src/routes/posts.js
===================================================================
--- src/routes/posts.js	(revision fef781eb8f9efb0e0be591707a4e79aa60191494)
+++ src/routes/posts.js	(revision 0d7acdfd541ca8374f3adc7a5f67f2ca00f4f4ef)
@@ -529,5 +529,5 @@
       const placeholders = trackIds.map(() => '?').join(',');
       const rows = db.prepare(`
-        SELECT t.id, t.title, t.artist, t.cover_url, m.filename
+        SELECT t.id, t.title, t.artist, t.cover_url, t.credit, t.license, m.filename
         FROM audio_tracks t LEFT JOIN media m ON m.id = t.media_id
         WHERE t.site_id = ? AND t.id IN (${placeholders})
@@ -542,4 +542,6 @@
           artist: r.artist,
           cover: r.cover_url,
+          credit: r.credit || '',
+          license: r.license || '',
           url: audioUrl(r.filename),
         };
Index: src/services/AudioEmbedService.js
===================================================================
--- src/services/AudioEmbedService.js	(revision fef781eb8f9efb0e0be591707a4e79aa60191494)
+++ src/services/AudioEmbedService.js	(revision 0d7acdfd541ca8374f3adc7a5f67f2ca00f4f4ef)
@@ -253,8 +253,12 @@
         artist: t.artist || '',
         cover: t.cover || '',
+        credit: t.credit || '',
+        license: t.license || '',
       });
       const titleH = this.escape(t.title || 'Untitled');
       const artistH = this.escape(t.artist || '');
       const urlH = this.escape(t.url);
+      // Zichtbare eigenaar/licentie-regel onder de track.
+      const creditBits = [this.escape(t.credit || ''), this.escape(t.license || '')].filter(Boolean).join(' · ');
       const dataAttr = trackJson
         .replace(/&/g, '&amp;').replace(/'/g, '&#39;').replace(/</g, '&lt;');
@@ -267,4 +271,5 @@
     <div class="pat-title">${titleH}</div>
     ${artistH ? `<div class="pat-artist">${artistH}</div>` : ''}
+    ${creditBits ? `<div class="pat-credit">${creditBits}</div>` : ''}
   </div>
 </div>`;
Index: src/services/AudioTranscoder.js
===================================================================
--- src/services/AudioTranscoder.js	(revision fef781eb8f9efb0e0be591707a4e79aa60191494)
+++ src/services/AudioTranscoder.js	(revision 0d7acdfd541ca8374f3adc7a5f67f2ca00f4f4ef)
@@ -138,4 +138,43 @@
 
 /**
+ * Herschrijf de ID3-tags van een BESTAANDE mp3 zonder her-encoden (`-c copy`).
+ * Gebruikt bij het bewerken van track-metadata (titel/artiest/album/credit/licentie)
+ * zodat de eigendomsinfo in het bestand zelf meereist bij een download.
+ * ffmpeg kan niet in-place editen → schrijf naar tmp en hernoem atomisch terug.
+ */
+export async function retagMp3({ filePath, tags = {} }) {
+  if (!filePath) throw new Error('retagMp3: filePath required');
+  await stat(filePath); // throws als 't bestand mist
+  const dir = path.dirname(filePath);
+  const base = path.basename(filePath, path.extname(filePath));
+  const tmpPath = path.join(dir, `${base}.retag-${process.pid}.mp3`);
+  try {
+    await new Promise((resolve, reject) => {
+      const cmd = ffmpeg(filePath)
+        .audioCodec('copy')        // geen her-encode → snel, geen kwaliteitsverlies
+        .format('mp3')
+        .outputOptions('-id3v2_version', '3')
+        .outputOptions('-map_metadata', '-1')
+        .outputOptions('-vn');
+      if (tags.title)     cmd.outputOptions('-metadata', `title=${tags.title}`);
+      if (tags.artist)    cmd.outputOptions('-metadata', `artist=${tags.artist}`);
+      if (tags.album)     cmd.outputOptions('-metadata', `album=${tags.album}`);
+      if (tags.copyright) cmd.outputOptions('-metadata', `copyright=${tags.copyright}`);
+      if (tags.comment)   cmd.outputOptions('-metadata', `comment=${tags.comment}`);
+      cmd.on('error', (err, so, se) => reject(new Error(((err && err.message) || 'ffmpeg') + (se ? ' | ' + se : ''))))
+         .on('end', () => resolve())
+         .save(tmpPath);
+    });
+    const s = await stat(tmpPath);
+    if (s.size === 0) throw new Error('retag output is leeg');
+    await rename(tmpPath, filePath);
+    return { filePath, size: s.size };
+  } catch (err) {
+    try { await unlink(tmpPath); } catch { /* tmp bestaat mogelijk niet */ }
+    throw err;
+  }
+}
+
+/**
  * Run a single ffmpeg pass: input -> tmp output.
  * Returns a promise that resolves when ffmpeg exits cleanly, rejects otherwise.
@@ -167,7 +206,9 @@
     // breaks any value containing a space (e.g. "Test Artist" gets parsed
     // as a separate output filename).
-    if (tags.title)  cmd.outputOptions('-metadata', `title=${tags.title}`);
-    if (tags.artist) cmd.outputOptions('-metadata', `artist=${tags.artist}`);
-    if (tags.album)  cmd.outputOptions('-metadata', `album=${tags.album}`);
+    if (tags.title)     cmd.outputOptions('-metadata', `title=${tags.title}`);
+    if (tags.artist)    cmd.outputOptions('-metadata', `artist=${tags.artist}`);
+    if (tags.album)     cmd.outputOptions('-metadata', `album=${tags.album}`);
+    if (tags.copyright) cmd.outputOptions('-metadata', `copyright=${tags.copyright}`); // ID3 TCOP — eigenaar/credit
+    if (tags.comment)   cmd.outputOptions('-metadata', `comment=${tags.comment}`);     // ID3 COMM — licentie
 
     cmd
Index: src/views/partials/track-editor.ejs
===================================================================
--- src/views/partials/track-editor.ejs	(revision fef781eb8f9efb0e0be591707a4e79aa60191494)
+++ src/views/partials/track-editor.ejs	(revision 0d7acdfd541ca8374f3adc7a5f67f2ca00f4f4ef)
@@ -385,4 +385,30 @@
                      value="${track.duration || ''}" placeholder="auto">
             </label>
+
+            <div class="te-row te-row-2">
+              <label class="te-field">
+                <span>Eigenaar / credit <small>(copyright-houder)</small></span>
+                <input type="text" id="te-credit" maxlength="200"
+                       autocomplete="off" spellcheck="false"
+                       placeholder="bv. © 2025 Robin Genis"
+                       value="${esc(track.credit || '')}">
+              </label>
+              <label class="te-field">
+                <span>Licentie</span>
+                <input type="text" id="te-license" maxlength="120"
+                       autocomplete="off" spellcheck="false" list="te-license-list"
+                       placeholder="Alle rechten voorbehouden"
+                       value="${esc(track.license || '')}">
+                <datalist id="te-license-list">
+                  <option value="Alle rechten voorbehouden"></option>
+                  <option value="CC BY 4.0"></option>
+                  <option value="CC BY-SA 4.0"></option>
+                  <option value="CC BY-NC 4.0"></option>
+                  <option value="CC BY-NC-SA 4.0"></option>
+                  <option value="CC BY-ND 4.0"></option>
+                  <option value="CC0 1.0 (publiek domein)"></option>
+                </datalist>
+              </label>
+            </div>
 
             <div class="te-field">
@@ -603,4 +629,6 @@
           artist: $('#te-artist').value.trim() || null,
           album:  $('#te-album').value.trim() || null,
+          credit:  $('#te-credit').value.trim() || null,
+          license: $('#te-license').value.trim() || null,
           duration: $('#te-duration').value ? Number($('#te-duration').value) : null,
           cover_url: urlInput.value.trim() || null,
Index: src/views/shell.ejs
===================================================================
--- src/views/shell.ejs	(revision fef781eb8f9efb0e0be591707a4e79aa60191494)
+++ src/views/shell.ejs	(revision 0d7acdfd541ca8374f3adc7a5f67f2ca00f4f4ef)
@@ -168,5 +168,5 @@
      anywhere (admin previews, post embeds, etc). The player itself is
      a singleton — see the script tag near </body>. -->
-<link rel="stylesheet" href="/assets/css/audio.css?v=6">
+<link rel="stylesheet" href="/assets/css/audio.css?v=7">
 <!-- Eigen custom media-embeds (YouTube/SoundCloud/Spotify) in huisstijl. -->
 <link rel="stylesheet" href="/assets/css/embed.css?v=8">
