0.3.5-alpha.1

This commit is contained in:
2026-09-21 21:21:28 +02:00
parent bf9ead329a
commit 3e24b7840c
8 changed files with 1166 additions and 8 deletions

View File

@@ -0,0 +1,300 @@
<!-- file: docs/studies/026-V0_3_5_WEBTRANSPORT_QUIC_STACK_AUDIT.md -->
<!-- version: 1 -->
# Audit WebTransport/QUIC pour 0.3.5
## Statut et objet
Cette étude cadre `0.3.5-alpha.1` à partir de l'archive taggée `v0.3.4`. Elle reste non normative : les décisions opérationnelles retenues pour la version sont portées par `docs/plans/005-V0_3_5_WEBTRANSPORT_QUIC_POC_PLAN.md`.
L'objectif est de déterminer, sur l'état réel de l'écosystème au 2026-09-21, quelle pile WebTransport/QUIC mérite un POC, comment elle se confronte au contrat `game-realtime-transport-lib` livré en `0.3.4`, quelles preuves navigateur/TLS sont réalistes et où doit vivre le fallback WebSocket.
## Base auditée
L'archive fournie est annoncée comme le téléchargement ZIP du tag Gitea `v0.3.4` et déclare :
```text
workspace.package.version = 0.3.4
17 membres workspace
```
Avant modification, les contrôles statiques exécutables dans l'environnement de génération donnent :
```text
unzip -t : no errors
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
games.sasedev workspace audit: clean
Markdown table audit: clean (5 table(s), 263 file(s))
Distribution layout audit: clean (50 required path(s), 8 forbidden path(s) absent)
```
L'inventaire indépendant confirme :
```text
437 fichiers dans le ZIP
267 fichiers Markdown extraits
68 fichiers Rust
18 Cargo.toml, dont le manifest racine
17 membres workspace
0 symlink
0 chemin absolu ou traversal détecté
0 target/, node_modules/, build/ ou gen/android/ généré
```
La sortie utilisateur fournie à l'ouverture de session confirme également `cargo fmt --all -- --check`, les trois audits et `cargo check --workspace` propres sur son checkout `0.3.4`. Son audit Markdown annonce `279` fichiers contre `263` dans le périmètre scanné du ZIP fourni. La baseline taggée reçue reste l'autorité de génération conformément à `CMD-GIT-003` et `CMD-GIT-004`; l'écart est enregistré sans inventer les fichiers locaux absents de l'archive.
## État hérité de 0.3.4
Les preuves de `history/0.3.4/` et `deltas/0.3.4/rel.001.md` confirment :
- `game-realtime-transport-lib` sans Tokio, WebSocket, QUIC, HTTP ni TLS ;
- `TransportMessage` binaire opaque et contrat message-oriented ;
- split `RealtimeConnection -> Sender + Receiver` ;
- fermeture distante propre distincte d'une erreur ;
- erreurs transport-neutral ;
- backend `game-realtime-websocket-lib` Tokio/tokio-tungstenite ;
- tests loopback et robustesse ;
- smoke runtime `game-realtime-websocket-smoke` hors harness `#[test]` ;
- `beta.1` validée avec 53 tests workspace ;
- `rc.1` validée avec 18 tests realtime ciblés, smoke `PASS` et arbre inverse où seul le launcher de smoke consomme le backend WebSocket.
Cette frontière constitue la baseline de comparaison. `0.3.5` n'a pas besoin de la redessiner avant d'avoir une preuve WebTransport concrète.
## État du protocole au 2026-09-21
WebTransport côté navigateur est désormais classé « Baseline 2026 » par MDN : l'API fonctionne sur les versions récentes des principaux navigateurs depuis mars 2026, avec la réserve habituelle sur les versions anciennes et certaines sous-capacités. L'API exige un contexte sécurisé et expose streams bidirectionnels/unidirectionnels fiables ainsi que datagrams non fiables.
Sources :
- <https://developer.mozilla.org/en-US/docs/Web/API/WebTransport_API>
- <https://developer.mozilla.org/en-US/docs/Web/API/WebTransport>
Le binding WebTransport over HTTP/3 n'est cependant pas encore un RFC final. `draft-ietf-webtrans-http3-16`, daté du 2026-07-06, est toujours un Internet-Draft en WG Last Call avec statut visé Proposed Standard.
Source : <https://datatracker.ietf.org/doc/draft-ietf-webtrans-http3/>
Conséquence pour le POC : l'interopérabilité navigateur est suffisamment réelle pour être testée, mais la version ne doit pas présenter le protocole ni une crate comme une dépendance produit définitivement stabilisée.
## Candidats Rust actuels
### Famille `web-transport`
État observé :
```text
web-transport 0.12.0 (2026-08-20)
web-transport-quinn 0.12.1 (2026-08-20)
web-transport-wasm 0.6.0 (2026-08)
```
`web-transport` fournit une API générique qui sélectionne :
```text
native -> web-transport-quinn
wasm32 -> web-transport-wasm
```
La crate native s'appuie sur Quinn, Rustls, Tokio et expose streams + datagrams. La crate WASM enveloppe l'API WebTransport du navigateur. Le projet amont est sous licence `MIT OR Apache-2.0`.
Sources :
- <https://docs.rs/crate/web-transport/latest>
- <https://docs.rs/crate/web-transport-quinn/latest>
- <https://docs.rs/crate/web-transport-wasm/latest>
- <https://github.com/moq-dev/web-transport>
Point particulièrement pertinent pour `games.sasedev` : la documentation `web-transport` explique explicitement le problème `Send` entre natif et WASM et contourne ce problème par sélection de l'implémentation selon la cible. Cela rejoint la décision prise en `0.3.4` de ne pas imposer `Send` aux futures du contrat transport-neutral.
La voie WASM impose actuellement :
```text
--cfg=web_sys_unstable_apis
```
car les bindings WebTransport de `web-sys` restent derrière ce cfg. Ce flag doit être fourni par le build final et ne peut pas être activé par une dépendance. Une éventuelle modification `.cargo/config.toml` devra donc être ciblée sur `wasm32-unknown-unknown`, pas appliquée globalement au workspace.
### `wtransport`
État observé :
```text
wtransport 0.7.2 (2026-08-11)
```
La crate fournit une implémentation WebTransport/HTTP3 pure Rust, client et serveur natifs, fondée notamment sur Quinn, Rustls et Tokio. Sa documentation est plus riche et elle fournit des helpers explicites pour certificat self-signed, hash SHA-256 et contraintes W3C. La branche `0.7.x` annonce un MSRV au moins Rust 1.88 dans la metadata consultée de `0.7.1`; la compilation exacte de `0.7.2` reste à prouver sur le toolchain du projet.
Sources :
- <https://docs.rs/crate/wtransport/latest>
- <https://docs.rs/wtransport/latest/wtransport/>
Sa limite pour ce projet est l'absence d'une façade Rust WASM équivalente : l'intégration navigateur documentée utilise directement l'API JavaScript `WebTransport`. Cela reste viable pour un serveur Rust, mais apporte moins de valeur au POC si l'objectif est aussi de challenger le contrat Rust commun sur `wasm32`.
### Quinn / H3 de plus bas niveau
Quinn est la base QUIC commune à plusieurs stacks, mais l'utiliser directement imposerait de réimplémenter le binding WebTransport/HTTP3, la négociation et les détails de session déjà possédés par les crates spécialisées.
Cette voie reste un recours si les wrappers retenus bloquent une exigence essentielle ; elle n'est pas retenue comme premier POC, conformément au principe d'éviter une infrastructure disproportionnée.
## Choix de POC retenu
La famille `web-transport` est retenue en premier pour `0.3.5`, avec :
```text
game-realtime-webtransport-lib
-> web-transport
-> native: web-transport-quinn
-> wasm32: web-transport-wasm
```
Raisons :
- façade native + WASM déjà pensée par l'amont ;
- alignement direct avec la contrainte `!Send` possible du contrat `0.3.4` ;
- serveur natif et client natif/WASM disponibles dans la même famille ;
- streams et datagrams disponibles pour le POC ;
- Quinn/Rustls/Tokio restent contenus dans le backend concret ;
- licence compatible avec le dépôt ;
- activité amont récente en août 2026.
`wtransport` reste le candidat de repli technique prioritaire si `web-transport` échoue sur une exigence concrète de `alpha.2` à `alpha.4`, notamment configuration TLS, lifecycle ou interop navigateur. Un échec d'implémentation ne justifie pas de basculer silencieusement : le plan et le delta doivent enregistrer la raison.
## Compatibilité avec le contrat 0.3.4
### Chemin fiable
Le contrat commun est message-oriented alors qu'un stream QUIC/WebTransport est un flux d'octets fiable, ordonné et flow-controlled. La différence de sémantique est réelle mais ne nécessite pas de modifier `RealtimeConnection`.
Le POC retiendra un stream bidirectionnel principal par connexion logique :
```text
WebTransport session
-> one primary bidirectional reliable stream
-> bounded backend framing
-> TransportMessage
```
Le framing interne candidat est :
```text
u32 big-endian payload length
payload bytes
```
avec une limite produit vérifiée avant allocation/écriture. Ce framing sert uniquement à reconstruire les frontières `TransportMessage` sur un byte stream ; il n'est pas le futur wire codec métier, ne porte aucune version de protocole joueur/room et reste privé au backend.
Le client ouvre le stream principal ; le serveur l'accepte avant de considérer la `RealtimeConnection` établie. Ce choix conserve l'ordre des messages et permet au split commun de mapper naturellement le côté write/read du même stream.
### Datagrams
Les datagrams WebTransport sont non fiables, non ordonnés et non flow-controlled. Ils ne satisfont donc pas le contrat fiable/ordonné livré en `0.3.4`.
Ils seront exercés séparément dans le POC, sans élargir `RealtimeConnection` et sans créer une capability commune tant qu'un second consommateur réel et un besoin sémantique durable ne sont pas démontrés.
Une mesure backend-spécifique peut utiliser la session amont directement ou une surface expérimentale locale au launcher de mesure. Elle ne doit pas devenir une API durable uniquement pour permettre le benchmark.
## TLS et certificats de développement
Le navigateur impose une URL `https://` vers le serveur WebTransport. L'option `serverCertificateHashes` permet de faire confiance à un certificat connu sans PKI publique lorsque la connexion est dédiée.
La documentation MDN impose notamment pour ce mode :
- hash SHA-256 ;
- certificat X.509v3 ;
- validité totale inférieure à deux semaines ;
- date courante dans la période de validité ;
- ECDSA P-256 comme choix interopérable minimal.
Source : <https://developer.mozilla.org/en-US/docs/Web/API/WebTransport/WebTransport>
`web-transport-quinn` fournit `ClientBuilder::with_server_certificate_hashes`, ce qui permet de reprendre la même stratégie de pinning côté client natif sans désactiver la validation TLS.
Source : <https://docs.rs/web-transport-quinn/latest/web_transport_quinn/struct.ClientBuilder.html>
Le POC doit donc privilégier une identité self-signed courte générée pour localhost et un pin de hash explicite. Une API « dangerous/no certificate verification » ne devient pas le chemin normal du smoke.
Aucun certificat privé, clé privée durable ou secret ne doit être commité.
## Plateformes
### Linux natif
Cible de référence pour :
- serveur local UDP/QUIC ;
- client natif ;
- tests loopback déterministes ;
- smoke runtime hors harness ;
- benchmark local borné.
### Navigateur / WASM
Cible obligatoire du POC parce que WebTransport apporte une valeur spécifique au navigateur. Deux preuves distinctes sont nécessaires :
1. compilation `wasm32-unknown-unknown` du chemin Rust choisi ;
2. interop runtime d'un navigateur récent avec le serveur Rust local.
Le smoke navigateur ne doit pas être confondu avec un simple `cargo check` WASM.
### Android
La pile native Quinn/Rustls rend Android plausible, mais `0.3.5-alpha.1` ne le déclare pas validé. Le plan réserve au minimum un contrôle de compilation ciblé si la dépendance choisie ne force pas une réouverture disproportionnée du pipeline Android.
Aucun changement Java/Gradle/JNI n'est prévu pour le POC ; la matrice quatre ABI `0.3.3` n'est donc pas rejouée par cérémonie.
### macOS / iOS
La trajectoire est documentée mais non validée en l'absence d'environnement Apple. Une compatibilité supposée depuis Quinn/Rustls ne doit pas être présentée comme un smoke réel.
## Fallback WebSocket
Le fallback est retenu au niveau composition/application du POC, pas dans le gameplay et pas dans `game-realtime-transport-lib`.
La preuve visée est :
```text
attempt WebTransport
success -> use WebTransport path
classified establishment failure / unsupported path -> WebSocket baseline
```
Le POC doit également permettre de forcer chaque branche afin de vérifier le fallback de façon déterministe. Il ne doit pas créer de registry dynamique, `TransportManager` générique ni système de plugins.
Aucune règle n'impose encore que cette logique devienne une crate réutilisable : un launcher technique suffit tant qu'un second consommateur réel ne justifie pas l'extraction.
## Mesures retenues
Les mesures minimales sont :
- temps d'établissement ;
- RTT de petits payloads ;
- débit sur payloads bornés ;
- plusieurs messages en vol ;
- comparaison WebSocket vs stream WebTransport fiable ;
- comparaison datagram uniquement si la preuve datagram est effectivement réalisée ;
- coût opérationnel observé : certificat, UDP, port, debug et build WASM.
Les résultats loopback sont décrits comme des mesures locales contrôlées. Ils ne seront jamais extrapolés en gains Internet/mobile sans test réseau correspondant.
CPU/mémoire peuvent être relevés si la méthode est stable, mais ne sont pas une gate de release.
## Risques principaux
- protocole HTTP/3 WebTransport encore en Internet-Draft ;
- APIs amont actives mais encore susceptibles de casser ;
- `web_sys_unstable_apis` imposé au build WASM ;
- contraintes de certificats plus lourdes que la baseline `ws://` ;
- UDP parfois bloqué par réseau, firewall ou infrastructure ;
- browser smoke nécessitant plusieurs processus/outils locaux ;
- framing message interne nécessaire sur le stream fiable ;
- différences de close/reset entre session WebTransport et stream principal ;
- dépendances crypto natives pouvant compliquer certains targets ;
- Android plausible mais non prouvé ;
- benchmark loopback trop bruité pour porter seul une décision produit.
## Conclusion de cadrage
Le POC est viable et justifie `0.3.5`, mais le forecast initial du prompt est trop grossier pour la règle de 15 à 30 minutes par delta. En particulier, backend natif, robustesse, smoke runtime, WASM, interop navigateur/TLS, fallback et mesures ne doivent pas être agrégés en deux grosses alpha.
Le plan actif découpe ces preuves en tranches verticales indépendantes et ajoute une consolidation explicite avant RC, conformément à `SESSION-009` et `VER-PHASE-010`.