Meine Aussage "ClamAV kostet mehr als das gesamte Branding zusammen" war so nicht haltbar: gemessen sind es ~130 zu ~104 Zeilen, der groesste Einzelpatch ist sogar Branding (HelpUserSettingsTab.tsx, 71 Zeilen). Der Unterschied liegt woanders: Branding-Patches stehen am Rand und sind nach einem Umbau wiederfindbar, ClamAV-Patches stehen mitten in Entschluesselungs- und Fehlerpfaden. Dazu der eigentliche Haken - verschiebt Upstream die viewmodels/-Dateien, entsteht kein Konflikt, die Zeilen sind schlicht weg und Git meldet nichts. Deshalb ist dort ein Funktionstest maszgeblich, nicht die Merge-Ausgabe. Refs axion1337.chat/ThreadNet-Web#7
8.5 KiB
ThreadNet-Web: Fork-Anpassungen für axion1337.chat
Dieses Dokument beschreibt alles, was dieser Fork gegenüber Upstream Element Web ändert. Der
Rest der docs/-Ordner-Dateien und das Root-README.md sind unverändertes Upstream-Material
und beschreiben absichtlich nicht diese Anpassungen - diese Datei ist der zentrale
Anlaufpunkt dafür.
1. Discord-Style Room-List (Call-Teilnehmer in der Raumliste)
Zeigt aktive Call-Teilnehmer direkt in der Raumliste an (ähnlich Discords Voice-Channel-UI),
statt nur einen generischen "Call läuft"-Indikator. Betroffene Komponenten:
apps/web/src/room-list/RoomListItemView/RoomListItemView.tsx und
RoomListItemView.module.css (vertikale Ausrichtung der Teilnehmer-Avatare).
2. Client-seitiges ClamAV-Content-Scanning (Issue #19-Erweiterung)
Der Server-seitige Content-Scanner (Synapse-Modul, siehe gitops-Repo
docs/deployment-guides/06-moderation-content-scanning.md)
sieht bei Ende-zu-Ende-verschlüsselten Räumen nur Ciphertext - eine strukturelle Grenze, kein
Bug. Da dieser Client bereits geforkt wird, scannt er stattdessen selbst, auf beiden Seiten:
- Senden (
apps/web/src/ContentMessages.ts,uploadFile()): liest die Datei als ArrayBuffer, scannt sie überscanContent()bevor verschlüsselt/hochgeladen wird - der Upload wird bei Treffer gar nicht erst gestartet. - Empfangen (
apps/web/src/utils/DecryptFile.ts,decryptFile()): scannt die entschlüsselten Bytes direkt nach dem Entschlüsseln, bevor das Blob an die UI zurückgegeben wird - schützt auch vor Dateien von unveränderten/fremden Matrix-Clients, die diesen Patch nicht haben. - Gemeinsame Scan-Logik:
apps/web/src/utils/ContentScanner.ts- ruft den (im gitops-Repo deployten)clamav-http-scanner-Dienst perfetch("/_scan", ...)auf, mit dem eigenen Matrix-Access-Token als Bearer-Auth. Fail-open bei Netzwerk-/Scanner-Fehlern (blockiert Uploads/Downloads nicht bei einem Ausfall des Scanners). - Neuer Fehlertyp
ContentScanRejectedError, verdrahtet durch die bestehenden Fehler-Rendering-Pfade inMImageBody.tsx,MAudioBody.tsx,VideoBodyViewModel.ts,FileBodyViewModel.ts. - Live getestet inkl. Hostile-Sender-Simulation (Datei per rohem API-Call ohne diesen Patch gesendet, Empfangs-Hook hat trotzdem geblockt).
⚠️ Bekannte Lücke: Electron/Desktop (Issue #2)
Dieser Fix ist bestätigt nur für den Web-Client-Build wirksam. Der Electron-Desktop-Client
bekommt ihn aktuell nicht automatisch - die CI-Workflow-Kette (build_ew-Job) checkt
weiterhin element-hq/element-web (Upstream) statt diesen Fork aus, und es ist kein GitHub-
Actions-Runner für dieses Repo registriert. Der aktuell veröffentlichte Desktop-Build
(desktop-v1.12.17-clientscan Release) wurde manuell gebaut, nicht automatisiert. Details und
Fix-Plan: Issue #2.
3. Build-Fixes (historisch, Issue #12)
Zwei Bugs blockierten einen vollständigen docker build von Grund auf (mussten für den
Client-Scanning-Rebuild oben behoben werden): (1) mehrere Shell-Skripte waren mit Modus 644
statt 755 committet (nicht ausführbar); (2) der matrix-js-sdk#develop-Git-Ref-Pin in
pnpm-lock.yaml war veraltet (fehlte src/oidc/authorize.ts, das apps/web importiert).
Beide behoben.
4. Merge-Reibung: was ein Upstream-Update wirklich kostet
Arbeitspaket 3 aus ThreadNet-Web#7. Gemessen am 2026-08-06, nicht geschätzt.
Die unangenehme Grundlage zuerst
Dieses Repo enthält keine Upstream-Historie. Element Web 1.12.17 wurde am
2026-05-10 als kompletter Baum importiert — und zwar in 3da3635, zusammen mit dem
ersten eigenen Feature im selben Commit. Davor liegt nur ein Initial commit mit
zwei Dateien.
Daraus folgt das Wesentliche: es gibt keinen gemeinsamen Vorfahren mit
element-hq/element-web. Ein git merge upstream/develop ist nicht möglich; mit
--allow-unrelated-histories erzwungen, kollidiert praktisch jede Datei. Wer „mal
eben Upstream nachziehen" sagt, meint in diesem Repo also: neuen Upstream-Stand
beschaffen und unsere Änderungen darauf neu auftragen.
Das ist der Grund, warum die Zahl unten überhaupt zählt — sie ist der Aufwand jedes Updates.
Unser Delta: 102 Dateien, davon 12 kritische
git diff --name-only 3da3635..main:
| Menge | Bereich | Konfliktrisiko |
|---|---|---|
| 46 | CI/Build (.github/, .gitlab-ci.yml, dockerbuild/) |
gering — eigene Strecke, Upstreams Workflows brauchen wir nicht |
| 22 | Config, Lockfiles, Upstream-Varianten | mittel — pnpm-lock.yaml konfliktet immer, wird aber regeneriert |
| 13 | Web-Assets (apps/web/res/) |
gering — meist eigene Dateien |
| 4 + 3 | eigene Icons und apps/desktop/axion1337/ |
keins — kein Upstream-Pendant |
| 12 | Upstream-Quellcode | hier entsteht die Arbeit |
Die 12 Dateien, und warum sie angefasst wurden
Branding (5) — flach, gut isolierbar:
apps/web/src/SdkConfig.ts— Defaults fürbrand,welcome_background_url,desktopBuildsapps/web/src/vector/index.html—<title>, Favicon-Link, PWA-Namenapps/web/src/async-components/structures/ErrorView.tsx— Logo der Fehlerseiteapps/web/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx— Attribution + Danksagungapps/web/src/i18n/strings/en_EN.json— einzelne Strings
ClamAV-Client-Scanning (7) — tief in der Medien-Pipeline:
apps/web/src/ContentMessages.ts,utils/ContentScanner.ts,utils/DecryptFile.tsapps/web/src/components/views/messages/MImageBody.tsx,MAudioBody.tsxapps/web/src/viewmodels/message-body/FileBodyViewModel.ts,VideoBodyViewModel.ts
Nicht der Umfang entscheidet, sondern die Art des Eingriffs
Gemessen in geänderten Zeilen nehmen sich beide Gruppen wenig: ClamAV ~130 Zeilen,
Branding ~104. Der größte Einzelpatch ist sogar Branding
(HelpUserSettingsTab.tsx, 71 Zeilen). Wer nur zählt, hält beide für gleich teuer.
Sie sind es nicht, und der Grund ist die Art des Eingriffs:
- Branding-Patches stehen am Rand. Ein zusätzlicher Default in
SdkConfig.ts, ein<link>im<head>, ein<li>in einer Settings-Liste. Wird die Datei umgebaut, sieht man sofort, wo das eigene Stück wieder hin muss. - ClamAV-Patches stehen mittendrin — in Entschlüsselungs- und Fehlerpfaden der Medien-Pipeline, verschränkt mit Upstream-Logik. Ein geänderter Kontrollfluss bedeutet nicht „Konflikt lösen", sondern „neu verstehen".
⚠️ Und der eigentliche Haken: viewmodels/. Element baut die Medien-Anzeige
gerade auf MVVM um (docs/MVVM.md; v1 ist dort bereits als deprecated markiert —
der Umbau läuft also schon in zweiter Runde). FileBodyViewModel.ts und
VideoBodyViewModel.ts gab es in älteren Ständen gar nicht. Unsere Änderung darin
ist mit je 6 Zeilen winzig — aber wenn Upstream diese Dateien verschiebt, umbenennt
oder auflöst, entsteht kein Konflikt: die Zeilen sind einfach weg, und Git meldet
nichts. Das ist gefährlicher als ein Konflikt, weil es stillschweigend passiert.
Praktische Folge: Nach einem Upstream-Update ist an ClamAV nicht die Merge-Ausgabe maßgeblich, sondern ein Funktionstest — eine verschlüsselte Datei senden und eine abgelehnte empfangen. Steht so auch in Abschnitt 2.
Was daraus für künftige Änderungen folgt
- Erst prüfen, ob es die Konfiguration schon kann. Auth-Logo,
logo_link_urlundbrandliefen ohne Rebuild über die ConfigMap; das Call-Widget wurde vollständig überVITE_PRODUCT_NAMEumbenannt, ohne eine einzige Quelldatei. Jede so vermiedene Datei ist eine, die beim Update nicht kollidiert. - Eigene Datei schlagen geänderte Datei.
apps/desktop/axion1337/und eigene Assets kosten beim Merge nichts. - Wenn Upstream-Code sein muss: einen Kommentar mit
ThreadNet-Fork:und der Begründung dazu. Beim Neuauftragen auf einen neuen Stand ist die Frage nie „was steht hier", sondern „warum stand das da" — und die beantwortet sonst niemand mehr.
Repo-Topologie (seit 2026-07-31)
Kanonisch ist git.lab/axion1337.chat/ThreadNet-Web (Homelab-GitLab, nur im Lab
auflösbar) — dort laufen Entwicklung und CI (.gitlab-ci.yml). Die Kopie auf
rohana.axion1337.de/sorb/ThreadNet-Web ist ein Push-Mirror (automatisch, GitLab →
Gitea) und dient als Lesekopie plus Standort für Issues, Container-Registry und Releases.
Niemals direkt nach rohana pushen — der Mirror überschreibt divergente Stände.