--- a/server.ts
+++ b/server.ts
@@ -22,6 +22,7 @@
 import { readFileSync, writeFileSync, mkdirSync, readdirSync, rmSync, statSync, renameSync, realpathSync, chmodSync } from 'fs'
 import { homedir } from 'os'
 import { join, extname, sep } from 'path'
+import { ariaGateway } from './aria-gateway.ts' // aria-v3-gateway (Phase E): ARIA_GATEWAY_MODE=off (Default) = Prod-Verhalten
 
 const STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join(homedir(), '.claude', 'channels', 'telegram')
 const ACCESS_FILE = join(STATE_DIR, 'access.json')
@@ -958,11 +959,19 @@
 
   const imagePath = downloadImage ? await downloadImage() : undefined
 
+  // aria-v3-gateway (Phase E): einziger Einhängepunkt, nach gate()=deliver und Permission-Intercept.
+  // off: kein Aufruf. shadow: forward=true (nur Protokoll). active: forward=false = nicht weiterleiten.
+  // forward=true + params!=null: GENAU diese vom Gateway freigegebene Nutzlast zustellen (Review #11,
+  // bei Einmal-Befehlen nur der klassifizierte Rest). params=null (off/shadow/Ausfall): Status quo.
+  const ariaDecision = ariaGateway.enabled
+    ? await ariaGateway.inbound({ bot, ctx, text, imagePath, attachment })
+    : { forward: true, params: null as null | { content: string; meta: Record<string, string> } }
+
   // image_path goes in meta only — an in-content "[image attached — read: PATH]"
   // annotation is forgeable by any allowlisted sender typing that string.
-  mcp.notification({
+  if (ariaDecision.forward) mcp.notification({
     method: 'notifications/claude/channel',
-    params: {
+    params: ariaDecision.params ?? {
       content: text,
       meta: {
         chat_id,
