docs(realtime): record the design decisions behind the realtime layer

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.
This commit is contained in:
2026-08-14 19:04:41 +02:00
parent 099315a538
commit 55a2af4756
14 changed files with 4470 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
# Recherche: WebRTC im Tauri-WebView (Mobile)
Type: research
Status: resolved
Blocked by: —
Parent: ../map.md
## Question
Funktioniert WebRTC — insbesondere `getUserMedia` für Mikrofonzugriff und `RTCPeerConnection` —
im WebView einer Tauri-v2-App auf **Android** und **iOS**? Und wenn ja, unter welchen
Bedingungen?
Das ist die riskanteste Unbekannte des gesamten Efforts. Fällt die Antwort negativ aus,
ist das Voice-Signaling-Design für die Hälfte der Zielplattformen wertlos, und die
Voice-Topologie-Frage sieht völlig anders aus.
### Zu klären
- **iOS**: Tauri v2 rendert in `WKWebView`, nicht in Safari. `getUserMedia` ist dort
historisch problematisch gewesen. Gilt das noch? Ab welcher iOS-Version? Braucht es
Entitlements, `Info.plist`-Einträge (`NSMicrophoneUsageDescription`), oder
WebView-Konfiguration, die Tauri nicht standardmäßig setzt?
- **Android**: Tauri v2 nutzt Android System WebView. Wie werden
`PermissionRequest`-Callbacks (`RESOURCE_AUDIO_CAPTURE`) durchgereicht? Muss die
Tauri-App das abfangen, oder passiert es automatisch?
- **Berechtigungen**: Wie beantragt eine Tauri-v2-App Mikrofonrechte auf beiden
Plattformen? Gibt es dafür ein offizielles Plugin oder Muster?
- **Alternative**: Falls WebView-WebRTC ausfällt — gibt es einen brauchbaren nativen
Pfad? (`webrtc-rs` im Tauri-Rust-Layer, Audio-Streams zwischen Rust und WebView).
Reifegrad, Mobile-Tauglichkeit, Aufwand.
- **Desktop-Gegenprobe**: Läuft es auf Windows (WebView2) und Linux (WebKitGTK)?
Auf Linux ist WebRTC in WebKitGTK-Builds nicht selbstverständlich einkompiliert.
### Quellenqualität
Primärquellen bevorzugen: Tauri-v2-Dokumentation und Issue-Tracker, WebKit-/Chromium-
Release-Notes, Apple-Entwicklerdoku. Blogposts nur als Hinweisgeber, nicht als Beleg —
und immer mit Datum, dieses Feld bewegt sich.
### Erwartetes Ergebnis
Eine Markdown-Datei im Repo mit: klarer Ja/Nein/Teilweise-Antwort pro Plattform,
den nötigen Konfigurationsschritten, den Versionsuntergrenzen, und den Quellen. Wenn
irgendwo „unklar" steht, muss dabeistehen, welches Experiment die Frage entscheiden würde.
## Answer
Vollständiges Ergebnis: [`../research/01-webrtc-tauri-mobile.md`](../research/01-webrtc-tauri-mobile.md)
| Plattform | Antwort | Aufwand |
|---|---|---|
| **iOS** (WKWebView) | **Ja**, ab 14.3 (praktisch 15.0) | eine Zeile: `NSMicrophoneUsageDescription` in `Info.ios.plist` |
| **Android** (System WebView) | **Ja**, ab API 21 | zwei Manifest-Zeilen: `RECORD_AUDIO` **und** `MODIFY_AUDIO_SETTINGS` |
| **Windows** (WebView2) | **Ja**, ohne Konfiguration | — |
| **Linux** (WebKitGTK 4.1) | **Nein** | drei unabhängige Gates, eines davon distro-seitig unlösbar |
**Council-Risiko 2 ist positiv aufgelöst.** Mobile trägt WebRTC im WebView; das
Voice-Signaling-Design ist für Android und iOS nicht wertlos. *Voice-Topologie* ist entblockt.
Belegt gegen die tatsächlich aufgelösten Versionen (`wry 0.55.1` über
`tauri-runtime-wry 2.11.4` mit `^0.55.0`), nicht gegen `dev` — wichtig, weil wry 0.56
die Permission-Lage geändert hat, aber von Tauri 2.11.x nicht gezogen werden kann.
### Drei Befunde, die andere Tickets verändern
1. **Der Problemfall ist Linux, nicht Mobile.** `ENABLE_WEB_RTC` folgt
`ENABLE_EXPERIMENTAL_FEATURES` (= OFF); Debian/Ubuntu und Alpine setzen es belegbar
nicht. Dazu `WebKitSettings:enable-webrtc` Default FALSE (von wry nie gesetzt) und
`permission-request` Default Deny (in 0.55.1 nicht verbunden). Gate 2 und 3 wären per
`with_webview` lösbar — Gate 1 hängt am Distro-Paket des Nutzers.
→ eigenes Ticket *Voice auf Linux*.
2. **Der Signaling-Server kann DTLS-Fingerprints tauschen** (RFC 8827) und damit den
Audio-Stream vollständig MITMen. Der spezifizierte Gegenmechanismus (W3C Identity for
WebRTC, IdP) ist in **keiner** der vier Ziel-Engines implementiert. Fingerprints müssen
durch einen E2EE-authentifizierten Kanal übertragen und gepinnt werden.
→ harte Randbedingung für *Voice-Signaling-Protokoll*.
3. **wry vergibt Mikrofonrechte ohne App-Beteiligung** — auf iOS in 0.55.1 bedingungslos
`Grant` für jeden Origin. Unkritisch, solange nur eigener gebündelter Code im WebView
läuft; sofort kritisch, sobald fremde Origins (iframe, Preview-Einbettung) dazukommen.
→ Architekturregel in den Notes der Map.
### Offen (blockiert die Spec nicht, gehört vor die Implementierung)
Sieben benannte Experimente in §8 der Ergebnisdatei. Höchste Priorität E1: `getUserMedia`
auf einem physischen iPhone, je einmal mit und ohne Info.plist-Key — entscheidet zugleich
das offene Upstream-Issue tauri#12068.