Index: CHANGELOG.de.md
===================================================================
--- CHANGELOG.de.md	(revision c45c1873a0dd747921d35135cbd7286e98789835)
+++ CHANGELOG.de.md	(revision f1956d7c01134c1d4e87af110af984c14a84d8b0)
@@ -7,4 +7,7 @@
 
 ### Hinzugefügt
+- **Erwähne Personen in einem Beitrag.** `@benutzer@server` in einem Beitrag verlinkt jetzt auf ihr
+  Profil und benachrichtigt sie im Fediverse — auch wenn sie dir nicht folgen — wie eine Erwähnung
+  in einer Antwort.
 - **Kurze Videos im Feed spielen automatisch ab und wiederholen sich.** Ein animiertes Cover oder ein
   kurzer (≤30s) Clip im News-Feed wird jetzt automatisch stumm in Schleife abgespielt, wie ein GIF;
Index: CHANGELOG.md
===================================================================
--- CHANGELOG.md	(revision c45c1873a0dd747921d35135cbd7286e98789835)
+++ CHANGELOG.md	(revision f1956d7c01134c1d4e87af110af984c14a84d8b0)
@@ -7,4 +7,6 @@
 
 ### Added
+- **Mention people in a post.** Typing `@user@server` in a post now links to their profile and
+  notifies them on the fediverse — even if they don't follow you — just like a mention in a reply.
 - **Short videos in the feed autoplay and loop.** An animated cover or a short (≤30s) clip in the
   News feed now plays automatically and loops muted, like a GIF; longer videos keep their controls.
Index: CHANGELOG.nl.md
===================================================================
--- CHANGELOG.nl.md	(revision c45c1873a0dd747921d35135cbd7286e98789835)
+++ CHANGELOG.nl.md	(revision f1956d7c01134c1d4e87af110af984c14a84d8b0)
@@ -7,4 +7,6 @@
 
 ### Toegevoegd
+- **Noem mensen in een post.** `@gebruiker@server` in een post linkt nu naar hun profiel en stuurt
+  ze een melding op de fediverse — ook als ze je niet volgen — net als een vermelding in een reactie.
 - **Korte video's in de feed spelen automatisch af en loopen.** Een geanimeerde cover of een korte
   (≤30s) clip in de News-feed speelt nu automatisch geluidloos in een lus, als een GIF; langere
Index: src/services/ActivityPubService.js
===================================================================
--- src/services/ActivityPubService.js	(revision c45c1873a0dd747921d35135cbd7286e98789835)
+++ src/services/ActivityPubService.js	(revision f1956d7c01134c1d4e87af110af984c14a84d8b0)
@@ -352,4 +352,10 @@
   for (const a of openAudio) attachment.push(a); // fedi_open tracks → native Audio players
 
+  // Inline @user@host mentions: the Mention tag objects + the mentioned actor URIs. Only
+  // present when the content was already mention-linked (deliverCreate/Update resolve them
+  // at send time); a plain buildNote (outbox/notes) yields none.
+  const _mentionTags = mentionTags(body);
+  const _mentionCc = _mentionTags.map((t) => t.href);
+
   const note = {
     id,
@@ -362,6 +368,8 @@
     // but not addressed to Public, so Mastodon shows it only to them and can't boost it).
     to: post.fan_only ? [`${aId}/followers`] : [PUBLIC],
-    cc: post.fan_only ? [] : [`${aId}/followers`],
-    tag: buildHashtagList(base, post.tags, body),
+    // Mentioned actors (from inline @user@host links the caller resolved) are addressed in cc
+    // so Mastodon notifies them; empty unless the content was mention-linked (delivery time).
+    cc: [...new Set([...(post.fan_only ? [] : [`${aId}/followers`]), ..._mentionCc])],
+    tag: [...buildHashtagList(base, post.tags, body), ..._mentionTags],
     replies: `${id}/replies`,
     // NSFW → Mastodon-style content warning: sensitive (blurs media) + a summary/spoiler
@@ -1138,10 +1146,14 @@
   const base = (process.env.PUBLIC_BASE_URL || '').replace(/\/+$/, '');
   if (!base || !site || !site.slug) return;
+  // Resolve inline @user@host mentions → link them in the note + collect their inboxes, so a
+  // mentioned person is notified even if they don't follow us (Mastodon-standard mention).
+  const mres = await resolveMentionsInText(base, post.content || '');
+  const post2 = mres.inboxes.length ? { ...post, content: mres.html } : post;
   const followers = fStmts().list.all(site.slug);
-  if (!followers.length) return;
-  const inboxes = [...new Set(followers.map((f) => f.shared_inbox || f.inbox).filter(Boolean))];
+  const inboxes = [...new Set([...followers.map((f) => f.shared_inbox || f.inbox), ...mres.inboxes].filter(Boolean))];
+  if (!inboxes.length) return; // no followers and no one mentioned
   const keys = getOrCreateKeys(site.slug);
   const keyId = `${actorId(base, site.slug)}#main-key`;
-  const create = buildCreate(base, site, post);
+  const create = buildCreate(base, site, post2);
   for (const inbox of inboxes) deliverWithRetry(site.slug, inbox, create, keyId, keys.private_pem);
 }
@@ -1195,15 +1207,17 @@
   const base = (process.env.PUBLIC_BASE_URL || '').replace(/\/+$/, '');
   if (!base || !site || !site.slug || !post || !post.id) return;
+  const mres = await resolveMentionsInText(base, post.content || ''); // link mentions + collect inboxes
+  const post2 = mres.inboxes.length ? { ...post, content: mres.html } : post;
   const followers = fStmts().list.all(site.slug);
-  if (!followers.length) return;
-  const inboxes = [...new Set(followers.map((f) => f.shared_inbox || f.inbox).filter(Boolean))];
+  const inboxes = [...new Set([...followers.map((f) => f.shared_inbox || f.inbox), ...mres.inboxes].filter(Boolean))];
+  if (!inboxes.length) return;
   const keys = getOrCreateKeys(site.slug);
   const me = actorId(base, site.slug);
-  const note = buildNote(base, site, post);
+  const note = buildNote(base, site, post2);
   note.updated = new Date().toISOString();
   const update = {
     '@context': AP_CONTEXT,
     id: `${noteId(base, post.id)}#update-${Date.now()}-${rid()}`,
-    type: 'Update', actor: me, to: [PUBLIC], cc: [`${me}/followers`],
+    type: 'Update', actor: me, to: [PUBLIC], cc: note.cc,
     object: note,
   };
Index: test/mentions.test.js
===================================================================
--- test/mentions.test.js	(revision f1956d7c01134c1d4e87af110af984c14a84d8b0)
+++ test/mentions.test.js	(revision f1956d7c01134c1d4e87af110af984c14a84d8b0)
@@ -0,0 +1,44 @@
+// Inline @mentions in posts — buildNote turns already-linked @user@host mentions into AS2
+// Mention tags and addresses the mentioned actor in cc (so Mastodon notifies them). The
+// WebFinger resolution/linking happens at delivery time; here we cover the sync build step.
+import { test } from 'node:test';
+import assert from 'node:assert/strict';
+
+process.env.DATABASE_PATH = ':memory:';
+process.env.PUBLIC_BASE_URL = 'https://test.example';
+
+const dbMod = await import('../src/config/database.js');
+const db = dbMod.default;
+dbMod.initializeDatabase();
+const AP = (await import('../src/services/ActivityPubService.js')).default;
+
+const BASE = 'https://test.example';
+db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)').run('u1', 'u1', 'u1@test', 'x', 'god');
+db.prepare('INSERT INTO sites (id, slug, title, owner_id, is_primary) VALUES (?,?,?,?,?)').run('s1', 'demo', 'Demo', 'u1', 1);
+const site = db.prepare('SELECT * FROM sites WHERE id = ?').get('s1');
+site.primary_slug = 'demo';
+
+// A post whose content already carries a resolved mention link (as deliverCreate produces).
+const linked = '<p>hi <a href="https://mastodon.social/@bob" class="u-url mention" data-actor="https://mastodon.social/users/bob">@bob@mastodon.social</a></p>';
+const post = { id: 'm1', slug: 'x', title: '', content: linked, tags: '[]', created_at: '2026-01-01T00:00:00Z' };
+
+test('a mention becomes an AS2 Mention tag with the actor URI as href', () => {
+  const note = AP.buildNote(BASE, site, post);
+  const m = note.tag.find((t) => t.type === 'Mention');
+  assert.ok(m, 'note.tag has a Mention');
+  assert.equal(m.href, 'https://mastodon.social/users/bob');
+  assert.equal(m.name, '@bob@mastodon.social');
+});
+
+test('the mentioned actor is addressed in cc', () => {
+  const note = AP.buildNote(BASE, site, post);
+  assert.ok(note.cc.includes('https://mastodon.social/users/bob'), 'mentioned actor in cc');
+  assert.ok(note.cc.includes(`${BASE}/ap/users/demo/followers`), 'followers still in cc');
+});
+
+test('a post without mentions has no Mention tag and an unchanged cc', () => {
+  const plain = { id: 'm2', slug: 'y', title: '', content: '<p>just text</p>', tags: '[]', created_at: '2026-01-01T00:00:00Z' };
+  const note = AP.buildNote(BASE, site, plain);
+  assert.equal(note.tag.filter((t) => t.type === 'Mention').length, 0);
+  assert.deepEqual(note.cc, [`${BASE}/ap/users/demo/followers`]);
+});
