2026-07-29 12:00:00 +00:00
|
|
|
|
# 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`](https://rohana.axion1337.de/sorb/axion1337.chat-gitops/src/branch/main/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](https://rohana.axion1337.de/sorb/ThreadNet-Web/issues/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.
|
2026-07-31 12:00:00 +00:00
|
|
|
|
|
2026-08-06 12:00:00 +00:00
|
|
|
|
## 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
|
|
|
|
|
|
|
2026-08-19 12:00:00 +00:00
|
|
|
|
**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.
|
2026-08-06 12:00:00 +00:00
|
|
|
|
|
|
|
|
|
|
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`
|
|
|
|
|
|
|
2026-08-06 12:00:00 +00:00
|
|
|
|
### 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.
|
2026-08-06 12:00:00 +00:00
|
|
|
|
|
|
|
|
|
|
### 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.
|
|
|
|
|
|
|
2026-07-31 12:00:00 +00:00
|
|
|
|
## 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.
|
2026-08-18 12:00:00 +00:00
|
|
|
|
|
|
|
|
|
|
## 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**:
|
|
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
|
# 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.
|
2026-08-19 12:00:00 +00:00
|
|
|
|
|
|
|
|
|
|
## 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:
|
|
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
|
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`.
|