Files
Thore Cimbal fa5dcc5fbc docs(fork): the import is not the v1.12.17 tag, and that changes the graft plan
Adding upstream as a second remote was meant to make updates viewable as a diff. The
first measurement contradicted what this document has claimed since August: diffing
v1.12.17 against the import commit yields 783 files, 115 of which exist only on our
side and 62 only in the tag.

The telling ones are MVVM files - TextualBodyViewModel.tsx and RovingTabIndex.tsx -
absent from the v1.12.17 tag, present in v1.12.26 and present in our import. The
changelog stops at 1.12.17 because it is only written at release time. So the base is
a develop state after the 1.12.17 release, not the tag.

That matters for the plan rather than for the code: grafting onto v1.12.17 would build
every future merge on an invented ancestry. Finding the real ancestor means searching
develop history, which a shallow tag fetch cannot do.

The remote is local configuration, so the setup is written down here instead of
committed.
2026-08-19 12:00:00 +00:00

12 KiB
Raw Permalink Blame History

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 über scanContent() 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 per fetch("/_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 in MImageBody.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. Am 2026-05-10 wurde ein kompletter Element-Web-Baum importiert — in 3da3635, zusammen mit dem ersten eigenen Feature im selben Commit. Davor liegt nur ein Initial commit mit zwei Dateien.

⚠️ Korrektur 2026-08-19: Der Import ist NICHT der Tag v1.12.17. Gemessen nach Aufnahme von upstream als zweitem Remote (management #0099, Schritt 2):

  • git diff v1.12.17 3da3635 ergibt 783 Dateien, +17443/10371 — weit mehr als unser damaliges Feature.
  • 115 Dateien existieren nur bei uns, 62 nur im Tag. Darunter MVVM-Dateien wie apps/web/src/viewmodels/room/timeline/event-tile/body/TextualBodyViewModel.tsx und packages/shared-components/src/core/roving/RovingTabIndex.tsx: im Tag v1.12.17 nicht vorhanden, in v1.12.26 und in unserem Import vorhanden.
  • Der CHANGELOG.md unseres Imports endet bei 1.12.17 (2026-04-30) — er wird erst beim Release fortgeschrieben.

Zusammen heißt das: Die Grundlage ist ein develop-Stand nach dem 1.12.17-Release (1.12.18 erschien am 2026-05-12), nicht der Tag. Praktische Folge: Ein Graft auf v1.12.17 wäre falsch und würde jede künftige Zusammenführung auf eine erfundene Ahnenreihe stellen. Wer den gemeinsamen Vorfahren nachträglich herstellen will, muss den passenden develop-Commit suchen — das braucht die volle Upstream-Historie, ein flacher Fetch der Tags genügt dafür nicht.

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ür brand, welcome_background_url, desktopBuilds
  • apps/web/src/vector/index.html<title>, Favicon-Link, PWA-Namen
  • apps/web/src/async-components/structures/ErrorView.tsx — Logo der Fehlerseite
  • apps/web/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx — Attribution + Danksagung
  • apps/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.ts
  • apps/web/src/components/views/messages/MImageBody.tsx, MAudioBody.tsx
  • apps/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

  1. Erst prüfen, ob es die Konfiguration schon kann. Auth-Logo, logo_link_url und brand liefen ohne Rebuild über die ConfigMap; das Call-Widget wurde vollständig über VITE_PRODUCT_NAME umbenannt, ohne eine einzige Quelldatei. Jede so vermiedene Datei ist eine, die beim Update nicht kollidiert.
  2. Eigene Datei schlagen geänderte Datei. apps/desktop/axion1337/ und eigene Assets kosten beim Merge nichts.
  3. 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.

Element Call anheben (@sorb/threadnet-call-embedded)

Das Call-Widget kommt als npm-Paket aus der Gitea-Registry auf rohana, nicht von npmjs. .npmrc im Wurzelverzeichnis bindet den Scope fest:

@sorb:registry=https://rohana.axion1337.de/api/packages/sorb/npm/

Zum Anheben genügt damit der normale Weg — kein Zusatzschritt, keine Umgebungsvariable:

# Version in apps/web/package.json setzen, dann:
pnpm install --lockfile-only     # Lockfile zieht die neue Tarball-URL von rohana
pnpm --dir apps/web build        # NICHT --filter web: das endet mit exit 0, ohne zu bauen

Danach muss die Tarball-URL im pnpm-lock.yaml auf rohana.axion1337.de zeigen. Tut sie das nicht, ist die .npmrc verlorengegangen: ohne sie löst pnpm gegen npmjs auf, und das geht heute nur deshalb laut aus (404), weil der Name @sorb dort noch frei ist — verlässt man sich darauf, ist es ein dependency-confusion-Weg. Siehe management#0055.

Vor dem Anheben liegt das Paket erst dann in der Registry, wenn in threadnet-call der manuelle Job publish_npm gelaufen ist — der ist bewusst manuell und wird von sorb ausgelöst.

Upstream als zweiter Remote (seit 2026-08-19)

Der Remote ist lokale Konfiguration, er wird nicht mitcommittet. Wer den Vergleich braucht, richtet ihn einmal ein:

git remote add upstream https://github.com/element-hq/element-web.git
git fetch --depth=1 upstream tag v1.12.26     # aktueller Stand
git fetch --depth=1 upstream tag v1.12.17     # zum Vergleich

Damit lässt sich ein Update als Diff betrachten, statt Dateien blind zu kopieren — zum Beispiel für die zwölf Dateien, die wir angefasst haben:

git diff v1.12.26 main -- apps/web/src/...   # unser Delta gegen Upstream
git diff v1.12.17 v1.12.26 -- <datei>        # was Upstream seither geaendert hat

⚠️ Ein flacher Fetch liefert keine Historie: git merge-base, git log über Upstream und ein Graft-Versuch brauchen git fetch upstream ohne --depth.