Eleven resolved decision tickets plus two research notes, produced with /wayfinder before any code was written. They live next to the code they explain rather than in the issue tracker: they are reasoning documents, not open work, and in a tracker they would drift from the code with no diff and no review. The value is in the rejected alternatives. Why a counter under a row lock and not BIGSERIAL, why an event log and not a column on messages, why resync is a 200 with a discriminated body and not a 4xx, why the fan-out trait was deleted again, why voice is peer-to-peer with a hard six-person cap and no speculative SFU seam, and why Linux needs a native media stack while the other three platforms do not. Two entries record where the spec was wrong and got corrected: created_at was never a safe sort key, and an unread count cannot be derived without fetching because seq counts events rather than messages. Open work is tracked as issues, not here.
166 lines
9.1 KiB
Markdown
166 lines
9.1 KiB
Markdown
# Voice auf Linux
|
|
|
|
Type: grilling
|
|
Status: resolved
|
|
Blocked by: —
|
|
Parent: ../map.md
|
|
|
|
## Question
|
|
|
|
Was ist die Zusage an Linux-Nutzer bezüglich Sprachanrufen — und wenn Voice dort laufen
|
|
soll, über welchen Medien-Stack?
|
|
|
|
Aufgeworfen von [*Recherche: WebRTC im Tauri-WebView (Mobile)*](01-research-webrtc-tauri-mobile.md).
|
|
Die erwartete schlechte Nachricht kam nicht von Mobile, sondern von Linux.
|
|
|
|
### Befundlage (belegt, siehe §5 der Recherche)
|
|
|
|
WebRTC ist in Tauris Linux-WebView (WebKitGTK 4.1) an **drei unabhängigen Gates** blockiert:
|
|
|
|
1. **Compile-Time.** `ENABLE_WEB_RTC` folgt `ENABLE_EXPERIMENTAL_FEATURES`, das auf `OFF`
|
|
steht. Debian/Ubuntu und Alpine setzen es belegbar nicht → `RTCPeerConnection` existiert
|
|
im JS-Global gar nicht. Arch/Fedora unverifiziert (Experiment E5 in der Recherche).
|
|
2. **Runtime-Setting.** `WebKitSettings:enable-webrtc` ist Default `FALSE` und wird von wry
|
|
nie gesetzt.
|
|
3. **Permission.** `permission-request` ist Default Deny; wry 0.55.1 verbindet das Signal
|
|
überhaupt nicht.
|
|
|
|
Gate 2 und 3 wären aus dem App-Code über `WebviewWindow::with_webview` lösbar (bzw. ab
|
|
Tauri 2.12 sauber über `on_permission_request`). **Gate 1 hängt am Distro-Paket des
|
|
Nutzers und ist von uns nicht lösbar.**
|
|
|
|
Das trifft ausgerechnet die Gruppe, die für dieses Projekt zentral ist: Selbsthoster sind
|
|
linux-lastig.
|
|
|
|
### Optionen, alle unattraktiv
|
|
|
|
1. **Voice auf Linux als nicht unterstützt deklarieren.** Chat läuft voll, Voice nicht.
|
|
Ehrlich und billig — und wahrscheinlich der Weg, der die Simplizitätsdoktrin am wenigsten
|
|
verletzt. Aber schmerzhaft für die Kernzielgruppe.
|
|
2. **Nativer Medien-Stack nur auf Linux.** `livekit/rust-sdks` (`libwebrtc`-Crate, echtes
|
|
libwebrtc mit NetEQ/AEC/Opus, laut README inkl. Linux) im Tauri-Rust-Layer. Bricht die
|
|
Architektur-Einheitlichkeit: Voice läge auf drei Plattformen im WebView und auf einer in
|
|
Rust. Zusätzlich ein großer vorkompilierter nativer Blob pro Plattform.
|
|
*(`webrtc-rs` ist hierfür **kein** Ersatz — es ist ein reiner Protokoll-Stack ohne
|
|
Geräte-Capture, ohne Codecs, ohne Jitter-Buffer, ohne Echo-Cancellation.)*
|
|
3. **Nativer Stack auf allen Plattformen.** Einheitlich, aber verwirft den kostenlosen
|
|
WebView-Pfad auf drei von vier Zielen und zieht den nativen Blob überallhin.
|
|
4. **Eigenes WebKitGTK bündeln.** Widerspricht der Simplizitätsdoktrin frontal.
|
|
5. **Auf Upstream warten.** Löst Gate 2 und 3, **nie Gate 1**. Also keine Lösung.
|
|
|
|
### Zu entscheiden
|
|
|
|
- Welche Option — und wie wird die Einschränkung dem Nutzer gegenüber **sichtbar** gemacht,
|
|
statt ihn in einen Timeout laufen zu lassen? (Feature-Detection `typeof RTCPeerConnection`
|
|
ist trivial und sollte in jedem Fall passieren.)
|
|
- Falls (2) oder (3): wo verläuft dann die Rust↔WebView-Naht? Bei nativem Audio ist der
|
|
WebView **nicht mehr der Medien-Endpunkt** — Capture und Wiedergabe liegen in Rust, und
|
|
die UI bekommt nur heruntergerechnete Metering-Werte über IPC, niemals rohes PCM.
|
|
Zurück-Injizieren von PCM in den WebView geht in Chromium, aber **nicht** in WebKitGTK.
|
|
Rückkopplung auf [*Wo lebt die WS-Verbindung im Client*](05-wo-lebt-die-ws-verbindung.md).
|
|
**Beantwortet (14.08.2026): Linux ist eine vollwertige Client-Plattform.** Damit ist der
|
|
billige Ausweg verbaut — Option 1 bedeutet, auf einer erklärten Zielplattform ein
|
|
Kernfeature nicht anzubieten. Sie bleibt formal wählbar, muss aber als bewusster
|
|
Produktverzicht begründet werden, nicht als technische Notwendigkeit.
|
|
|
|
Realistisch bleiben damit **(2)** nativer Stack nur auf Linux und **(3)** nativer Stack
|
|
überall. Die eigentliche Frage dieses Tickets ist die Abwägung zwischen diesen beiden:
|
|
Einheitlichkeit gegen Kostenminimierung.
|
|
|
|
### Kopplung
|
|
|
|
Wirkt auf [*Voice-Topologie*](04-voice-topologie.md) zurück: fällt hier ein nativer Pfad,
|
|
ändern sich dort die Optionen. Die beiden Tickets können in beliebiger Reihenfolge laufen,
|
|
aber das zweite muss das Ergebnis des ersten kennen.
|
|
|
|
---
|
|
|
|
## Answer
|
|
|
|
Entschieden am 14.08.2026.
|
|
|
|
### 0. Zusätzlich geprüft: Flatpak löst Gate 1 nicht
|
|
|
|
Die Recherche hatte Gate 1 als „hängt am Distro-Paket" eingestuft. Das gilt aber nur, solange
|
|
der Nutzer das Distro-Paket benutzt — ein Flatpak bringt WebKitGTK aus seinem Runtime mit.
|
|
Geprüft, Ergebnis negativ:
|
|
|
|
- Das **GNOME-Runtime baut WebKitGTK ohne den WebRTC-Flag**; die Antwort im Flathub-Forum auf
|
|
die Frage nach dem Patchen lautete sinngemäß „macht ein Issue bei gnome-build-meta auf"
|
|
([Flathub Discourse](https://discourse.flathub.org/t/patching-gnome-runtime/10682)).
|
|
- Das einzige dokumentierte funktionierende Rezept verlangt, **WebKitGTK selbst zu
|
|
kompilieren** (`-DENABLE_MEDIA_STREAM=ON -DENABLE_WEB_RTC=ON`) **und zusätzlich wry zu
|
|
patchen** — erprobt auf NixOS unter X11, mit GBM-Fehlern unter Wayland und nicht
|
|
aufgelisteten v4l2loopback-Geräten
|
|
([Tauri Discussion #8426](https://github.com/tauri-apps/tauri/discussions/8426)).
|
|
|
|
Für ein Entwicklungssystem machbar, für Endnutzer nicht. **Gate 1 hat heute keine
|
|
nutzerseitige Lösung.**
|
|
|
|
### 1. WebView auf drei Plattformen, nativer Stack nur auf Linux
|
|
|
|
Windows, Android und iOS nutzen den kostenlosen, funktionierenden WebView-Pfad. Linux bekommt
|
|
`livekit/rust-sdks` (`libwebrtc`-Crate) im Rust-Layer — echtes libwebrtc mit NetEQ, AEC/AGC/NS
|
|
und Opus, laut README inklusive Linux.
|
|
|
|
`webrtc-rs` ist **kein** Ersatz: reiner Protokoll-Stack ohne Geräte-Capture, ohne Codecs, ohne
|
|
Jitter-Buffer, ohne Echo-Cancellation.
|
|
|
|
Die Alternative „nativ überall" war knapp und hat ein starkes Argument — ein Pfad statt zwei,
|
|
eine Berechtigungslogik, eine Testmatrix, und die wry-Eigenheit (Mikrofonrechte werden auf iOS
|
|
bedingungslos an jeden Origin vergeben) wäre komplett aus dem Spiel. Sie verliert, weil sie
|
|
einen auf drei Plattformen funktionierenden Gratis-Pfad wegwirft. **Der akzeptierte Preis sind
|
|
zwei Medienpfade auf Dauer**, mit zwei Berechtigungsmodellen und zwei Fehlerbildern.
|
|
|
|
**Nützliche Nebenwirkung:** weil Linux den nativen Stack *unbedingt* nutzt und nicht nur dort,
|
|
wo die WebView-Variante versagt, spielt es keine Rolle mehr, ob eine Distribution WebKitGTK
|
|
mit WebRTC gebaut hat. **Experiment E5 der Recherche (Arch/Fedora prüfen) entfällt damit.**
|
|
Und weil es hier nur um Audio geht, ist der GBM-/Wayland-Fehler aus #8426 gegenstandslos — er
|
|
betrifft die Videoausgabe des WebViews.
|
|
|
|
### 2. Die Naht ist eine explizite Verzweigung im Frontend, ganz oben
|
|
|
|
Rust meldet dem Frontend eine Fähigkeit — sinngemäß `mediaOwner: "webview" | "native" | "none"`
|
|
— und das Voice-Modul verzweigt **einmal**, an seiner Spitze. Darunter zwei klar getrennte
|
|
Implementierungen: `RTCPeerConnection` im WebView, oder Tauri-Commands in den nativen Stack.
|
|
|
|
Das ist ehrlich gegenüber der Realität, dass sich die Fälle nicht nur mechanisch
|
|
unterscheiden, sondern auch in der Oberfläche: **auf Linux gibt es kein `<audio>`-Element**,
|
|
weil PCM sich in WebKitGTK nicht zurückinjizieren lässt (in Chromium ginge es). Der native
|
|
Pfad besitzt dort die Wiedergabe vollständig, und Pegelanzeigen kommen als
|
|
heruntergerechnete Werte über IPC — **niemals rohes PCM**.
|
|
|
|
Verworfen: ein `MediaEngine`-Trait in Rust. Es wäre die sauberere Fassade, verlangte aber eine
|
|
Steuerungsumkehr — die WebView-Implementierung müsste ihre eigentliche Arbeit per IPC nach
|
|
oben ans Frontend delegieren. Ebenfalls verworfen: eine Naht nur auf Signaling-Ebene, weil die
|
|
Duplizierung dann bis in die Oberfläche reicht und Verhaltensunterschiede versehentlich statt
|
|
absichtlich entstehen.
|
|
|
|
### 3. Gestaffelt: Voice erscheint zuerst auf den WebView-Plattformen
|
|
|
|
Linux zeigt Voice solange sauber als „noch nicht verfügbar" an — über die gemeldete Fähigkeit
|
|
`none`, **nicht** über eine WebView-Feature-Erkennung, weil die auf Linux dauerhaft negativ
|
|
bliebe und nichts über den nativen Pfad aussagt. Chat läuft dort voll.
|
|
|
|
Das Argument ist Risikoreihenfolge: der WebView-Pfad validiert das Signaling-Protokoll billig
|
|
auf drei Plattformen, und der native Stack steckt danach in ein bereits bewährtes Protokoll.
|
|
Den schweren, unerprobten Medien-Stack gegen ein gleichzeitig unerprobtes Protokoll zu bauen,
|
|
verdoppelt die Unbekannten in einem Schritt — und bei einem Fehler wüsste niemand, welche
|
|
Seite schuld ist.
|
|
|
|
**Bewusst in Kauf genommen:** die Zielgruppe ist linuxlastig, und „Voice, aber nicht bei dir"
|
|
ist keine gute erste Begegnung. Wer es einmal als „geht auf Linux nicht" abgespeichert hat,
|
|
prüft später nicht nach. Das ist der Preis dafür, das Feature überhaupt in Nutzerhände zu
|
|
bringen, bevor Monate in den nativen Stack fließen — und er ist erträglich, weil die Ansage
|
|
ehrlich und sichtbar ist statt stillschweigend.
|
|
|
|
### Folgen für andere Tickets
|
|
|
|
- [*Voice-Topologie*](04-voice-topologie.md): der native Stack liefert auf Linux echtes
|
|
libwebrtc, Mesh funktioniert dort also genauso. Die Topologie-Entscheidung wird durch dieses
|
|
Ticket **nicht** eingeschränkt.
|
|
- [*Voice-Signaling-Protokoll*](09-voice-signaling-protokoll.md): der signierte
|
|
DTLS-Fingerprint muss auf **beiden** Pfaden erzeugbar sein — im WebView über
|
|
`RTCPeerConnection.generateCertificate()`, nativ über die Zertifikats-API von libwebrtc. Die
|
|
Naht für das Pinning darf also nicht an einer WebView-API hängen.
|