Update 2026-07-29 — Real umgesetzt und live getestet, Issue geschlossen
Der ursprünglich geplante matrix-content-scanner + ClamAV-Ansatz erwies sich als Sackgasse:
das ist ein Proxy, den der Client explizit statt der normalen Media-Endpunkte aufrufen
muss - weder aktuelles Element Web noch Element X unterstützen das (geprüft: kein
Content-Scanner-Hook im offenen element-x-android-Repo). Element hat echtes serverseitiges
Scanning nur in der kommerziellen Element Pro + ESS Pro-Kombination, nicht in unserer offenen
ESS-Community-Installation.
Stattdessen umgesetzt: ein eigenes, kleines Synapse-Modul
(apps/production/clamav_spam_checker.py), das Synapses echten, dokumentierten Hook check_media_file_for_spam nutzt - läuft serverseitig, transparent für jeden Client,
ganz ohne Client-Mitwirkung. Kein fertiges Modul dafür existiert (auch synapse-http-antispam
schließt genau diesen Callback aus), daher selbst geschrieben.
Architektur: ClamAV als eigener Pod + PVC. Modul per ConfigMap gemounted, über PYTHONPATH importierbar (synapse.extraVolumes/extraVolumeMounts/extraEnv) - kein
Custom-Synapse-Image nötig. Fail-open bei Scanner-Fehlern, damit ein ClamAV-Ausfall nicht alle
Uploads blockiert.
Ein echter Bug unterwegs gefunden und gefixt: erster Versuch nutzte asyncio.open_connection/ wait_for für die Verbindung zu clamd - schlug live mit RuntimeError: no running event loop
fehl, da Synapse auf Twisteds Reactor läuft, nicht auf einer laufenden asyncio-Event-Loop. Durch
das eigene Fail-Open-Verhalten blieb das zunächst unbemerkt (die EICAR-Testdatei wurde beim
ersten Versuch nicht erkannt, lief einfach durch). Nach Umstellung auf Twisteds eigene
Netzwerk-Primitives (HostnameEndpoint/connectProtocol) funktioniert es sauber.
Live-Tests bestätigt (2026-07-29):
Normale Datei in unverschlüsseltem Raum → durchgelassen (kein Regressionsschaden)
EICAR-Testdatei in unverschlüsseltem Raum → zuverlässig blockiert
(ClamAV rejected an upload: Eicar-Test-Signature, Client erhält 400 Bad content)
EICAR-Testdatei in verschlüsseltem Raum/DM → läuft durch (erwartete, strukturelle Grenze -
Synapse hat bei E2EE nie den Entschlüsselungsschlüssel, nur Ciphertext sichtbar)
Bekannte Deckungslücke: schützt nur unverschlüsselte Räume/DMs. Für E2EE bräuchte es einen
kooperierenden Client (existiert aktuell nicht offen verfügbar) - das ist eine strukturelle
Grenze des Matrix-Protokolls selbst, kein Implementierungsdetail.
Details: docs/deployment-guides/06-moderation-content-scanning.md, Wiki Moderation-Content-Scanning. Folgeidee (Grafana-Dashboard über bestehende Loki-Logs für
Erkennungen/Ausfälle): Issue #43.
**Update 2026-07-29 — Real umgesetzt und live getestet, Issue geschlossen**
Der ursprünglich geplante `matrix-content-scanner` + ClamAV-Ansatz erwies sich als Sackgasse:
das ist ein Proxy, den der **Client** explizit statt der normalen Media-Endpunkte aufrufen
muss - weder aktuelles Element Web noch Element X unterstützen das (geprüft: kein
Content-Scanner-Hook im offenen `element-x-android`-Repo). Element hat echtes serverseitiges
Scanning nur in der kommerziellen Element Pro + ESS Pro-Kombination, nicht in unserer offenen
ESS-Community-Installation.
**Stattdessen umgesetzt**: ein eigenes, kleines Synapse-Modul
(`apps/production/clamav_spam_checker.py`), das Synapses echten, dokumentierten Hook
`check_media_file_for_spam` nutzt - läuft **serverseitig**, transparent für jeden Client,
ganz ohne Client-Mitwirkung. Kein fertiges Modul dafür existiert (auch `synapse-http-antispam`
schließt genau diesen Callback aus), daher selbst geschrieben.
**Architektur**: ClamAV als eigener Pod + PVC. Modul per ConfigMap gemounted, über
`PYTHONPATH` importierbar (`synapse.extraVolumes`/`extraVolumeMounts`/`extraEnv`) - kein
Custom-Synapse-Image nötig. Fail-open bei Scanner-Fehlern, damit ein ClamAV-Ausfall nicht alle
Uploads blockiert.
**Ein echter Bug unterwegs gefunden und gefixt**: erster Versuch nutzte `asyncio.open_connection`/
`wait_for` für die Verbindung zu clamd - schlug live mit `RuntimeError: no running event loop`
fehl, da Synapse auf Twisteds Reactor läuft, nicht auf einer laufenden asyncio-Event-Loop. Durch
das eigene Fail-Open-Verhalten blieb das zunächst unbemerkt (die EICAR-Testdatei wurde beim
ersten Versuch nicht erkannt, lief einfach durch). Nach Umstellung auf Twisteds eigene
Netzwerk-Primitives (`HostnameEndpoint`/`connectProtocol`) funktioniert es sauber.
**Live-Tests bestätigt** (2026-07-29):
- Normale Datei in unverschlüsseltem Raum → durchgelassen (kein Regressionsschaden)
- EICAR-Testdatei in unverschlüsseltem Raum → zuverlässig blockiert
(`ClamAV rejected an upload: Eicar-Test-Signature`, Client erhält `400 Bad content`)
- EICAR-Testdatei in verschlüsseltem Raum/DM → läuft durch (erwartete, strukturelle Grenze -
Synapse hat bei E2EE nie den Entschlüsselungsschlüssel, nur Ciphertext sichtbar)
**Bekannte Deckungslücke**: schützt nur unverschlüsselte Räume/DMs. Für E2EE bräuchte es einen
kooperierenden Client (existiert aktuell nicht offen verfügbar) - das ist eine strukturelle
Grenze des Matrix-Protokolls selbst, kein Implementierungsdetail.
Details: `docs/deployment-guides/06-moderation-content-scanning.md`, Wiki
[[Moderation-Content-Scanning]]. Folgeidee (Grafana-Dashboard über bestehende Loki-Logs für
Erkennungen/Ausfälle): Issue #43.
Update 2026-07-29 (später) — Client-seitige Erweiterung für verschlüsselte Räume
Nach der ersten Umsetzung (Synapse-Modul, siehe Kommentar oben) kam die Nachfrage, ob auch
E2EE-Räume abgedeckt werden können, da der Nutzer bereits ThreadNet-Web forkt. Umgesetzt:
Neuer Dienst: apps/production/clamav-http-scanner.py - macht den bestehenden
ClamAV-Pod für Browser-JS erreichbar (https://axion1337.chat/_scan), da clamd nur rohes
TCP spricht. Auth via Synapses eigenem /whoami-Endpunkt, fail-open bei Scanner-Fehlern
(gleiche Philosophie wie das Synapse-Modul).
Zwei Patch-Stellen im ThreadNet-Web-Fork (nicht in diesem Repo):
Empfang: apps/web/src/utils/DecryptFile.ts (decryptFile()) - einziger Punkt für alle
Anhangstypen.
Versand: apps/web/src/ContentMessages.ts (uploadFile()) - eine Funktion für alle
Uploads (Datei, Thumbnails, Sprachnachrichten), unabhängig von Raumverschlüsselung.
Live getestet:
EICAR in verschlüsseltem Gruppenraum + 1:1-DMs → vor Upload blockiert (vorher lief das
durch, da Synapse E2EE-Inhalte nie sieht).
Empfangsseite unabhängig vom Absender: EICAR über einen echten, ungepatchten Client
(app.element.io) in denselben Raum geschickt (simuliert föderierten/fremden Absender) -
beim Download im gepatchten Client greift der Scanner trotzdem zuverlässig.
Korrigierte Annahme zu Electron/Desktop: ursprünglich angenommen, der Fix gelte
automatisch nicht für Electron. Tatsächlich hat apps/desktop einen webapp-artifact-Mechanismus, der den eigenen Fork-Build übernehmen würde - aber kein
laufender CI-Runner, und der Build-Job checkt noch das Upstream-Repo aus. Die bestehende
Desktop-Build (mit der Discord-Style-Raumliste) stammt aus einem manuellen, nicht
wiederholten Build-Vorgang. Neues Issue dafür: #44. Element X (Mobile)
komplett unberührt (eigene Codebasis, matrix-rust-sdk).
Deckung jetzt: Web-Client (bestätigt, beide Richtungen, verschlüsselt + unverschlüsselt) +
Synapse-Modul (alle Clients, nur unverschlüsselt). Electron/Desktop und Element X bleiben
offene Lücken, erstere mit konkretem Fahrplan in #44.
**Update 2026-07-29 (später) — Client-seitige Erweiterung für verschlüsselte Räume**
Nach der ersten Umsetzung (Synapse-Modul, siehe Kommentar oben) kam die Nachfrage, ob auch
E2EE-Räume abgedeckt werden können, da der Nutzer bereits `ThreadNet-Web` forkt. Umgesetzt:
**Neuer Dienst**: `apps/production/clamav-http-scanner.py` - macht den bestehenden
ClamAV-Pod für Browser-JS erreichbar (`https://axion1337.chat/_scan`), da clamd nur rohes
TCP spricht. Auth via Synapses eigenem `/whoami`-Endpunkt, fail-open bei Scanner-Fehlern
(gleiche Philosophie wie das Synapse-Modul).
**Zwei Patch-Stellen im `ThreadNet-Web`-Fork** (nicht in diesem Repo):
- Empfang: `apps/web/src/utils/DecryptFile.ts` (`decryptFile()`) - einziger Punkt für alle
Anhangstypen.
- Versand: `apps/web/src/ContentMessages.ts` (`uploadFile()`) - eine Funktion für alle
Uploads (Datei, Thumbnails, Sprachnachrichten), unabhängig von Raumverschlüsselung.
**Live getestet**:
- EICAR in verschlüsseltem Gruppenraum + 1:1-DMs → vor Upload blockiert (vorher lief das
durch, da Synapse E2EE-Inhalte nie sieht).
- Normale Datei verschlüsselt/unverschlüsselt → durchgelassen (kein Regressionsschaden).
- **Empfangsseite unabhängig vom Absender**: EICAR über einen echten, ungepatchten Client
(app.element.io) in denselben Raum geschickt (simuliert föderierten/fremden Absender) -
beim Download im gepatchten Client greift der Scanner trotzdem zuverlässig.
**Korrigierte Annahme zu Electron/Desktop**: ursprünglich angenommen, der Fix gelte
automatisch nicht für Electron. Tatsächlich hat `apps/desktop` einen
`webapp-artifact`-Mechanismus, der den eigenen Fork-Build übernehmen würde - aber kein
laufender CI-Runner, und der Build-Job checkt noch das Upstream-Repo aus. Die bestehende
Desktop-Build (mit der Discord-Style-Raumliste) stammt aus einem manuellen, nicht
wiederholten Build-Vorgang. Neues Issue dafür:
[#44](https://rohana.axion1337.de/sorb/axion1337.chat-gitops/issues/44). Element X (Mobile)
komplett unberührt (eigene Codebasis, matrix-rust-sdk).
Deckung jetzt: Web-Client (bestätigt, beide Richtungen, verschlüsselt + unverschlüsselt) +
Synapse-Modul (alle Clients, nur unverschlüsselt). Electron/Desktop und Element X bleiben
offene Lücken, erstere mit konkretem Fahrplan in #44.
Details: `docs/deployment-guides/06-moderation-content-scanning.md`, Wiki
[[Moderation-Content-Scanning]].
Migriert nach git.lab: axion1337.chat/axion1337.chat-gitops#19 (nur im Lab bzw. via VPN erreichbar — das Lab ist seit 2026-08-01 die Quelle der Wahrheit, siehe gitops#48). Weiterarbeit dort; dieses Gitea-Issue bleibt als Verweis stehen.
**Migriert nach git.lab**: [axion1337.chat/axion1337.chat-gitops#19](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/19) (nur im Lab bzw. via VPN erreichbar — das Lab ist seit 2026-08-01 die Quelle der Wahrheit, siehe gitops#48). Weiterarbeit dort; dieses Gitea-Issue bleibt als Verweis stehen.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
matrix-content-scanner + ClamAV antivirus scanning for uploaded media, blocking suspicious files. Optional but good practice given federation is open.
Update 2026-07-29 — Real umgesetzt und live getestet, Issue geschlossen
Der ursprünglich geplante
matrix-content-scanner+ ClamAV-Ansatz erwies sich als Sackgasse:das ist ein Proxy, den der Client explizit statt der normalen Media-Endpunkte aufrufen
muss - weder aktuelles Element Web noch Element X unterstützen das (geprüft: kein
Content-Scanner-Hook im offenen
element-x-android-Repo). Element hat echtes serverseitigesScanning nur in der kommerziellen Element Pro + ESS Pro-Kombination, nicht in unserer offenen
ESS-Community-Installation.
Stattdessen umgesetzt: ein eigenes, kleines Synapse-Modul
(
apps/production/clamav_spam_checker.py), das Synapses echten, dokumentierten Hookcheck_media_file_for_spamnutzt - läuft serverseitig, transparent für jeden Client,ganz ohne Client-Mitwirkung. Kein fertiges Modul dafür existiert (auch
synapse-http-antispamschließt genau diesen Callback aus), daher selbst geschrieben.
Architektur: ClamAV als eigener Pod + PVC. Modul per ConfigMap gemounted, über
PYTHONPATHimportierbar (synapse.extraVolumes/extraVolumeMounts/extraEnv) - keinCustom-Synapse-Image nötig. Fail-open bei Scanner-Fehlern, damit ein ClamAV-Ausfall nicht alle
Uploads blockiert.
Ein echter Bug unterwegs gefunden und gefixt: erster Versuch nutzte
asyncio.open_connection/wait_forfür die Verbindung zu clamd - schlug live mitRuntimeError: no running event loopfehl, da Synapse auf Twisteds Reactor läuft, nicht auf einer laufenden asyncio-Event-Loop. Durch
das eigene Fail-Open-Verhalten blieb das zunächst unbemerkt (die EICAR-Testdatei wurde beim
ersten Versuch nicht erkannt, lief einfach durch). Nach Umstellung auf Twisteds eigene
Netzwerk-Primitives (
HostnameEndpoint/connectProtocol) funktioniert es sauber.Live-Tests bestätigt (2026-07-29):
(
ClamAV rejected an upload: Eicar-Test-Signature, Client erhält400 Bad content)Synapse hat bei E2EE nie den Entschlüsselungsschlüssel, nur Ciphertext sichtbar)
Bekannte Deckungslücke: schützt nur unverschlüsselte Räume/DMs. Für E2EE bräuchte es einen
kooperierenden Client (existiert aktuell nicht offen verfügbar) - das ist eine strukturelle
Grenze des Matrix-Protokolls selbst, kein Implementierungsdetail.
Details:
docs/deployment-guides/06-moderation-content-scanning.md, WikiModeration-Content-Scanning. Folgeidee (Grafana-Dashboard über bestehende Loki-Logs für
Erkennungen/Ausfälle): Issue #43.
Update 2026-07-29 (später) — Client-seitige Erweiterung für verschlüsselte Räume
Nach der ersten Umsetzung (Synapse-Modul, siehe Kommentar oben) kam die Nachfrage, ob auch
E2EE-Räume abgedeckt werden können, da der Nutzer bereits
ThreadNet-Webforkt. Umgesetzt:Neuer Dienst:
apps/production/clamav-http-scanner.py- macht den bestehendenClamAV-Pod für Browser-JS erreichbar (
https://axion1337.chat/_scan), da clamd nur rohesTCP spricht. Auth via Synapses eigenem
/whoami-Endpunkt, fail-open bei Scanner-Fehlern(gleiche Philosophie wie das Synapse-Modul).
Zwei Patch-Stellen im
ThreadNet-Web-Fork (nicht in diesem Repo):apps/web/src/utils/DecryptFile.ts(decryptFile()) - einziger Punkt für alleAnhangstypen.
apps/web/src/ContentMessages.ts(uploadFile()) - eine Funktion für alleUploads (Datei, Thumbnails, Sprachnachrichten), unabhängig von Raumverschlüsselung.
Live getestet:
durch, da Synapse E2EE-Inhalte nie sieht).
(app.element.io) in denselben Raum geschickt (simuliert föderierten/fremden Absender) -
beim Download im gepatchten Client greift der Scanner trotzdem zuverlässig.
Korrigierte Annahme zu Electron/Desktop: ursprünglich angenommen, der Fix gelte
automatisch nicht für Electron. Tatsächlich hat
apps/desktopeinenwebapp-artifact-Mechanismus, der den eigenen Fork-Build übernehmen würde - aber keinlaufender CI-Runner, und der Build-Job checkt noch das Upstream-Repo aus. Die bestehende
Desktop-Build (mit der Discord-Style-Raumliste) stammt aus einem manuellen, nicht
wiederholten Build-Vorgang. Neues Issue dafür:
#44. Element X (Mobile)
komplett unberührt (eigene Codebasis, matrix-rust-sdk).
Deckung jetzt: Web-Client (bestätigt, beide Richtungen, verschlüsselt + unverschlüsselt) +
Synapse-Modul (alle Clients, nur unverschlüsselt). Electron/Desktop und Element X bleiben
offene Lücken, erstere mit konkretem Fahrplan in #44.
Details:
docs/deployment-guides/06-moderation-content-scanning.md, WikiModeration-Content-Scanning.
Migriert nach git.lab: axion1337.chat/axion1337.chat-gitops#19 (nur im Lab bzw. via VPN erreichbar — das Lab ist seit 2026-08-01 die Quelle der Wahrheit, siehe gitops#48). Weiterarbeit dort; dieses Gitea-Issue bleibt als Verweis stehen.