21 KiB
Plan 0.3.5 — POC WebTransport/QUIC et fallback WebSocket
Statut
Plan actif créé pendant 0.3.5-alpha.1 à partir de l'archive taggée v0.3.4, puis réconcilié pour l'implémentation native de 0.3.5-alpha.2, son correctif de validation 0.3.5-alpha.2.fix.1 et le chemin fiable candidat de 0.3.5-alpha.3.
Le cadrage détaillé et la comparaison des stacks actuelles sont conservés dans docs/studies/026-V0_3_5_WEBTRANSPORT_QUIC_STACK_AUDIT.md. Le présent document porte les décisions opérationnelles, le scope, les gates et le forecast vivant de la version.
Mission
0.3.5 doit déterminer par des preuves reproductibles si WebTransport/QUIC mérite de devenir un second backend realtime aux côtés de WebSocket.
La version doit :
- conserver
game-realtime-transport-libcomme frontière transport-neutral tant qu'aucun besoin commun ne justifie son évolution ; - implémenter un chemin WebTransport fiable compatible avec
TransportMessage; - prouver client/server natifs en loopback ;
- prouver un chemin navigateur/WASM réel si la stack retenue reste viable ;
- prouver le traitement des certificats de développement sans désactivation permanente de TLS ;
- exercer le fallback WebSocket au niveau composition ;
- comparer WebSocket et WebTransport avec des mesures locales bornées ;
- challenger séparément les datagrams sans les forcer dans le contrat fiable ;
- conclure
retained,deferredourejectedpour la trajectoire produit.
La frontière reste :
transport
↓
wire codec
↓
session protocol
↓
synchronization
↓
authoritative simulation
Aucune couche supérieure n'est introduite dans 0.3.5.
Décisions acquises en alpha.1
Stack POC primaire
Famille retenue :
web-transport 0.12.x
native -> web-transport-quinn 0.12.x
wasm32 -> web-transport-wasm 0.6.x
La version exacte résolue par Cargo sera enregistrée dans le delta qui introduit les dépendances. Les contraintes restent centralisées sous [workspace.dependencies] conformément à RUST-DEP-001 et les features sont choisies localement par la crate consommatrice.
wtransport 0.7.x reste le candidat de repli prioritaire si une exigence concrète bloque la famille primaire. Quinn/H3 brut n'est pas le premier choix.
Fermeture de dépendances en alpha.2
La première tranche native utilise directement :
web-transport-quinn 0.12.1, default-features = false, feature ring
rcgen 0.14.10, default-features = false, feature ring
url 2.5.8
La façade web-transport n'est pas ajoutée avant le chemin WASM : alpha.2 ne possède qu'un backend natif et n'a pas besoin d'une abstraction multiplateforme encore inutilisée. ring est choisi explicitement et seul pour éviter le backend crypto par défaut aws-lc-rs de web-transport-quinn et garder le graphe POC plus petit et déterministe.
L'identité locale générée est ECDSA P-256/SHA-256, contient les SAN localhost, 127.0.0.1 et ::1, vit sept jours avec une petite marge de clock skew et reste uniquement en mémoire. Une identité X.509 DER + PKCS#8 DER peut aussi être injectée. Le client accepte uniquement le hash SHA-256 explicitement configuré ; aucune API de désactivation de validation TLS n'est exposée.
alpha.2 retourne une session WebTransport établie mais n'ouvre encore aucun stream applicatif et n'implémente pas RealtimeConnection. Cette séparation ferme la preuve QUIC/TLS avant le framing de alpha.3.
La gate de alpha.2 a confirmé compilation, Clippy, configuration TLS/pinning et rejet d’un mauvais pin, mais le test positif comparait strictement 127.0.0.1:port à sa représentation IPv4-mapped IPv6 [::ffff:127.0.0.1]:port remontée par Quinn. alpha.2.fix.1 normalise uniquement cette représentation dans le test d’intégration ; aucun contrat, comportement transport ou scope de alpha.3 n’est modifié.
Fermeture du chemin fiable en alpha.3
alpha.3 conserve game-realtime-transport-lib inchangé et adapte le backend concret à ses traits. Une WebTransportSession devient une WebTransportConnection après sélection d'un unique stream bidirectionnel primaire : le client l'ouvre, le serveur l'accepte. Le split() conserve une copie de la session dans chaque moitié afin que la session QUIC/WebTransport ne soit pas fermée au moment où l'objet connexion est consommé.
Le framing privé est exactement :
u32 big-endian payload length
payload bytes
La borne POC est fixée à 1 MiB par message dans cette tranche. Elle est vérifiée avant écriture et, surtout, avant allocation côté réception. Cette limite reste interne : alpha.4 décide sa configuration produit avec deadlines, backpressure, reset/abort/cancellation et mapping d'erreurs détaillé.
Le FIN du stream primaire constitue la fermeture logique de base de alpha.3 et produit TransportReceive::Closed après consommation des messages déjà écrits. La fermeture de session complète et les scénarios de lifecycle avancés restent explicitement réservés à alpha.4.
web-transport-quinn écrit l'en-tête WebTransport du stream durant open_bi() avant de rendre le stream au code appelant. L'accept serveur peut donc terminer avant la première frame applicative ; le framing games.sasedev commence directement au premier octet applicatif et n'ajoute aucun préambule de visibilité.
Ownership physique
Nouveau backend durable candidat :
crates/common/game-realtime-webtransport-lib
Responsabilités :
- établissement WebTransport natif et, si viable, client WASM ;
- adaptation du chemin fiable vers
game-realtime-transport-lib; - framing binaire privé du stream principal ;
- TLS/certificat/hash nécessaires au transport ;
- limites, timeouts, close/reset/cancellation et mapping d'erreurs ;
- tracing
games::realtime::webtransport; - tests natifs déterministes ;
- aucune notion joueur/room/tick/snapshot.
Launchers techniques candidats :
crates/apps/game-realtime-webtransport-smoke
crates/apps/game-realtime-transport-benchmark
Un launcher séparé de fallback n'est créé que si le smoke WebTransport ou le benchmark ne peut pas porter proprement cette preuve. Ne pas créer plusieurs exécutables uniquement pour refléter chaque alpha.
Pour le navigateur, le frontend technique exact est décidé au moment de alpha.7 après validation du chemin WASM. S'il faut un host Vite direct, il doit rester explicitement technique et ne pas contaminer Web/game-snake-poc ou le gameplay Snake.
Contrat commun
game-realtime-transport-lib reste inchangé dans le plan initial.
Le chemin fiable utilise :
one WebTransport session
-> one primary bidirectional reliable stream
-> u32 big-endian length
-> payload bytes
Le framing est privé au backend, borné avant allocation et distinct du futur wire codec.
Le client ouvre le stream principal ; le serveur l'accepte avant de retourner une connexion utilisable. Le split commun mappe ensuite write/read du stream principal vers RealtimeSender/RealtimeReceiver.
Toute modification du contrat commun requiert un besoin impossible à satisfaire proprement par WebSocket et WebTransport autrement ; elle n'est pas autorisée pour harmoniser les noms d'API.
Datagrams
Les datagrams restent hors RealtimeConnection parce qu'ils sont non fiables et non ordonnés.
Le POC les exerce uniquement comme capacité backend-spécifique/mesure. Aucune API commune durable n'est créée sans second consommateur réel.
TLS de développement
Le POC privilégie :
- certificat self-signed X.509v3 court ;
- ECDSA P-256 ;
- validité totale inférieure à deux semaines ;
- pin SHA-256 côté client natif et navigateur ;
- aucune clé privée durable versionnée ;
- aucune désactivation globale de validation TLS.
Le mode PKI publique, ACME et reverse proxy HTTP/3 restent hors scope de 0.3.5.
Fallback
Le fallback vit au niveau composition/application :
WebTransport attempt
-> success: WebTransport
-> classified unavailable/establishment failure: WebSocket
Le test doit pouvoir forcer les deux branches.
Pas de TransportManager, registry de plugins ou sélection dynamique générique dans game-realtime-transport-lib.
Plateformes et preuves
Linux natif — obligatoire
- compilation backend ;
- client/server loopback sur adresse/port éphémère ;
- round-trip binaire ;
- limites/timeouts/close/reset ;
- smoke runtime hors
#[test]; - mesures locales WebSocket vs WebTransport.
WASM/navigateur — obligatoire si la stack primaire reste viable
Deux gates distinctes :
- build/check réel
wasm32-unknown-unknowndu chemin WebTransport Rust ; - smoke runtime dans un navigateur récent vers le serveur Rust local.
Le cfg web_sys_unstable_apis doit être ciblé sur WASM uniquement.
Android — trajectoire, pas intégration produit
- vérifier la compatibilité des dépendances et, si raisonnable, compiler le backend pour au moins
aarch64-linux-android; - ne modifier ni Java, ni Gradle, ni JNI sans besoin concret découvert ;
- ne pas rejouer APK/AAB quatre ABI si aucun chemin Android produit n'est touché.
Apple — documentation uniquement
macOS/iOS restent non validés sans environnement Apple. La version documente la dépendance supposée mais n'attribue aucun smoke.
Métriques
Mesures obligatoires si les deux transports fonctionnent :
establishment latency
RTT: small payload
throughput: bounded medium payload
several messages in flight
Jeu d'essai candidat :
32 B
256 B
1 KiB
16 KiB
La taille exacte peut évoluer selon les limites observées, mais reste identique entre transports.
Les résultats doivent inclure au minimum nombre d'itérations et une statistique robuste simple, par exemple médiane et p95. Aucun benchmark loopback ne conclut à lui seul sur Internet/mobile.
CPU/mémoire sont optionnels si la mesure n'est pas stable/reproductible dans la session.
Smoke tests prévus
Smoke WebSocket hérité
cargo run -p game-realtime-websocket-smoke
Reste le témoin de fallback et doit être rejoué aux jalons larges où le fallback est concerné.
Smoke WebTransport natif
Cible prévue :
cargo run -p game-realtime-webtransport-smoke
Il doit :
- générer/charger uniquement l'identité de développement nécessaire ;
- binder localement sans port fixe ;
- établir un client WebTransport ;
- échanger au moins un payload dans chaque sens ;
- fermer proprement ;
- afficher un résultat
PASSdéterministe ; - ne dépendre ni d'Internet ni d'un secret versionné.
Smoke navigateur
Le smoke navigateur doit prouver réellement :
- chargement du client WASM retenu ;
- création WebTransport vers le serveur Rust ;
- hash certificat transmis explicitement ;
- round-trip binaire ;
- close propre ;
- absence de fallback silencieux vers WebSocket pendant la preuve WebTransport.
Le workflow exact est fixé dans alpha.7 après la preuve de compilation WASM.
Smoke fallback
La preuve de fallback doit couvrir :
- WebTransport disponible -> chemin WebTransport ;
- WebTransport volontairement indisponible/endpoint invalide -> WebSocket ;
- erreur non classée comme fallback -> erreur visible, pas masquée.
Cette preuve peut être intégrée à un launcher technique existant si cela garde le code plus petit et plus clair.
Tests automatisés prévus
Backend natif
Au minimum :
- configuration valide/invalide ;
- établissement loopback ;
- payload vide si le contrat l'autorise ;
- payload binaire normal ;
- plusieurs messages ordonnés ;
- taille maximale et dépassement ;
- longueur de frame malformée/overflow impossible ;
- timeout d'établissement ;
- timeout send/receive lorsque reproductible ;
- fermeture locale ;
- fermeture distante ;
- reset/abort du stream principal ;
- drop/cancellation sans task détachée ;
- mapping des erreurs sans fuite des types amont dans le contrat commun.
WASM
Les tests unitaires qui n'exigent pas de navigateur restent ciblés. L'interop navigateur est un smoke runtime distinct et ne doit pas être simulée par un test natif.
Fallback
Tester la classification et la décision de composition sans créer une abstraction runtime générique.
Graphe de dépendances attendu
game-realtime-transport-lib
↑
├── game-realtime-websocket-lib
└── game-realtime-webtransport-lib
Aucune crate sous :
crates/engines/
crates/games/
doit dépendre de web-transport, Quinn, Rustls ou d'un backend concret.
Les launchers techniques peuvent dépendre des backends nécessaires à leur preuve.
Hors scope
- wire codec définitif ;
- session joueur/room ;
- matchmaking/auth ;
- snapshots/deltas gameplay ;
- prediction/reconciliation/rollback ;
- simulation authoritative ;
- Uroburas Mode 3 ;
- persistence gameplay ;
- cluster/sharding/regions ;
- Redis/NATS/Kafka ;
- PKI/ACME produit ;
- reverse proxy HTTP/3 définitif ;
- CDN/edge ;
- framework générique multi-transport ;
- réécriture Actix Web ;
- modification du gameplay pour sélectionner un transport.
Risques et critères de replanification
La version est replanifiée avant exécution lorsqu'une tranche dépasse clairement 30 minutes de scope attendu.
Déclencheurs explicites :
- compilation de la stack primaire impossible avec les règles Rust du dépôt ;
- besoin d'un fork amont ;
- browser smoke exigeant une infrastructure externe ou PKI disproportionnée ;
- dépendance crypto rendant Android ou Linux non praticable ;
- évolution du contrat commun requise ;
- framing fiable beaucoup plus complexe que prévu ;
- fallback nécessitant une abstraction partagée nouvelle ;
- benchmark devenant un sous-projet de performance.
Dans ces cas, le delta courant reste fermé sur sa preuve et le plan est révisé avant la tranche suivante.
Forecast révisé
Chaque tranche vise environ 15 à 30 minutes de travail effectif et une preuve indépendante. Les numéros restent prévisionnels conformément à SESSION-007.
0.3.5-alpha.1 — cadrage, audit et plan
- audit archive/règles/historique
0.3.4; - recherche stacks WebTransport/QUIC ;
- choix primaire + fallback technique ;
- décision contrat/framing/datagram ;
- matrice TLS/plateforme ;
- smoke/tests/metrics ;
- plan vivant.
Aucun backend ni dépendance WebTransport ajouté.
0.3.5-alpha.2 — crate backend, dépendances et établissement natif
- créer
game-realtime-webtransport-lib; - ajouter uniquement les dépendances/features nécessaires ;
- config native client/server ;
- génération ou injection d'identité de test ;
- hash pinning ;
- établir une session native client/server ;
- tests d'établissement/configuration ;
- README initial de responsabilité/frontières.
Pas encore d'implémentation complète RealtimeConnection si cela rend la tranche trop lourde.
0.3.5-alpha.3 — stream fiable et contrat commun
Tranche candidate matérialisée avec :
- stream bidirectionnel principal ;
- framing
u32 + payloadborné avant allocation ; - borne POC interne de 1 MiB ;
RealtimeConnection, sender et receiver ;- round-trip ordonné multi-message, y compris payload vide et octets non UTF-8 ;
- fermeture distante de base par FIN du stream primaire ;
- tests loopback ciblés ;
USAGE.mdpour la séquence session -> stream primaire -> contrat commun.
La gate utilisateur reste requise avant d'ouvrir alpha.4.
0.3.5-alpha.4 — robustesse et lifecycle
- limites ;
- deadlines ;
- backpressure/erreurs observables ;
- close/reset/abort ;
- cancellation/drop ;
- cas négatifs de framing ;
- mapping d'erreurs ;
- tests de robustesse ciblés.
0.3.5-alpha.5 — smoke natif hors harness
- créer ou finaliser
game-realtime-webtransport-smoke; - round-trip réel localhost ;
- certificat/hash de développement reproductible ;
PASSdéterministe ;- contrôle du graphe de dépendances.
Cette tranche reste distincte pour ne pas mélanger robustesse de bibliothèque et preuve runtime publique.
0.3.5-alpha.6 — chemin WASM compilable
- activer
web_sys_unstable_apisuniquement pourwasm32-unknown-unknown; - implémenter/adapter le client WASM ;
- prouver le build/check WASM ciblé ;
- ne pas modifier Snake ou Reflex ;
- documenter les différences native/WASM réellement rencontrées.
Si la famille web-transport échoue ici, évaluer wtransport serveur + API navigateur directe avant de conclure.
0.3.5-alpha.7 — interop navigateur et TLS local
- host technique minimal si nécessaire ;
- navigateur récent -> serveur Rust WebTransport ;
- hash certificat W3C ;
- round-trip binaire ;
- close propre ;
- smoke documenté et reproductible ;
- aucune désactivation permanente de TLS.
Cette tranche est volontairement séparée de alpha.6 car certificat, serveur local et browser tooling constituent un risque opérationnel distinct.
0.3.5-alpha.8 — fallback WebSocket au niveau composition
- tentative WebTransport ;
- fallback WebSocket uniquement sur erreurs classifiées ;
- branche WebTransport forcée ;
- branche fallback forcée ;
- erreur non-fallback visible ;
- aucun couplage gameplay ;
- pas de registry/framework générique.
0.3.5-alpha.9 — datagram POC isolé
Uniquement si le backend fiable et le navigateur sont suffisamment stables :
- émission/réception datagram ;
- pertes/ordre non garantis documentés ;
- preuve locale bornée ;
- aucune modification du contrat commun ;
- décision explicite : utile pour future capability ou simple constat.
Si la valeur est déjà claire sans API supplémentaire, cette tranche peut être fusionnée avec la mesure ou supprimée.
0.3.5-alpha.10 — mesures comparatives bornées
- launcher/outil minimal de mesure ;
- WebSocket vs WebTransport fiable ;
- établissement, RTT, throughput, messages en vol ;
- datagram seulement s'il existe réellement ;
- résultats et limites de méthode documentés ;
- conclusion technique provisoire
retain/defer/reject.
0.3.5-beta.1 — validation large
Jalon rare de full workspace :
cargo test --workspace --all-targets --all-features
Plus :
- fmt/check/Clippy/audits ;
- tests ciblés realtime ;
- smoke WebSocket ;
- smoke WebTransport natif ;
- smoke navigateur si retenu ;
- preuve fallback ;
cargo treedirect/inverse des deux backends ;- vérification qu'aucun gameplay/engine ne dépend d'un backend concret.
Toute capacité fonctionnelle majeure manquante réouvre une alpha ; un défaut fermé produit beta.1.fix.N.
0.3.5-beta.2 — consolidation pré-RC
Tranche explicitement réservée par SESSION-009 / VER-PHASE-010 :
- réconcilier README/USAGE et docs durables ;
- figer la conclusion WebTransport ;
- documenter plateformes réellement validées/non validées ;
- mettre à jour
CHANGELOG.mdsi la transition vers RC est préparée ; - réconcilier
ROADMAP.mdseulement si le statut macro change ; - écrire le prompt
0.3.6; - enregistrer l'historique
beta.1après validation ; - aucune nouvelle fonctionnalité majeure.
0.3.5-rc.1 — candidate gelée
- aucun comportement volontaire nouveau ;
- full workspace conformément à
CMD-RC-002; - gates realtime de publication ;
- smokes retenus ;
- graphe de dépendances ;
- vérification du prompt
0.3.6; - corrections uniquement selon
VER-RC-*.
0.3.5 — stable
Promotion mécanique autant que possible :
- version stable ;
- historique RC ;
- changelog stable ;
- clôture du plan ;
- roadmap si nécessaire ;
- delta final ;
- ajustement mécanique du prompt
0.3.6.
Gates Cargo planifiées
Tranches Rust ordinaires
Dès qu'un fichier Rust/Cargo change :
cargo fmt --all
cargo fmt --all -- --check
python3 scripts/audit_rust_workspace_rules.py
cargo check --workspace
cargo clippy --workspace --all-targets --all-features -- -D warnings
Puis tests ciblés des crates touchées/consommateurs affectés.
Full workspace
cargo test --workspace --all-targets --all-features est réservé explicitement à :
beta.1;rc.1;- éventuellement une tranche plus tôt uniquement si un changement transverse rend les tests ciblés insuffisants.
Il n'est pas répété à chaque alpha.
Cargo tree
À exécuter :
- après introduction/changement de dépendances WebTransport ;
- à
alpha.5pour prouver la frontière ; - à
beta.1etrc.1pour les graphes de publication.
Nettoyage Cargo
Aucun cargo clean n'est requis dans alpha.1.
Le plan réserve un nettoyage complet au plus tard avant la validation RC si l'accumulation du target-dir ou un doute de reproductibilité le justifie. Un nettoyage ciblé reste préférable pendant les alphas.
Critère de réussite de 0.3.5
La version est réussie si elle fournit une conclusion reproductible parmi :
retained -> second backend utile, conserver pour 0.4.x
deferred -> viable mais bénéfice/portabilité/infrastructure insuffisants aujourd'hui
rejected -> coût ou incompatibilité disproportionnés pour la trajectoire actuelle
Aucune conclusion n'est imposée à l'avance.
Même en cas de report/rejet, la baseline WebSocket 0.3.4 reste fonctionnelle et le POC ne doit pas laisser une abstraction commune artificiellement déformée.