Files
games/docs/plans/005-V0_3_5_WEBTRANSPORT_QUIC_POC_PLAN.md

31 KiB
Raw Blame History

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, le chemin fiable validé de 0.3.5-alpha.3, la robustesse validée par 0.3.5-alpha.4.fix.1, le smoke natif validé de 0.3.5-alpha.5, le chemin client WASM validé de 0.3.5-alpha.6, le smoke navigateur candidat de 0.3.5-alpha.7 et ses correctifs de build 0.3.5-alpha.7.fix.1 puis 0.3.5-alpha.7.fix.2.

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-lib comme 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, deferred ou rejected pour 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 dun 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 dintégration ; aucun contrat, comportement transport ou scope de alpha.3 nest 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é.

La gate utilisateur de alpha.3 a ensuite confirmé fmt, audits, check workspace, Clippy strict, les sept tests du contrat commun, les sept tests unitaires/établissement WebTransport déjà présents et le round-trip primaire dédié. Le graphe inverse confirme aussi que game-realtime-webtransport-lib reste un backend feuille et ne remonte pas dans engine/gameplay.

Fermeture robustesse et lifecycle en alpha.4

alpha.4 remplace la borne interne figée par WebTransportConfig, partagé par les configs client/server. Les valeurs par défaut restent volontairement conservatrices pour le POC : 1 MiB par message, 10 s pour la connexion/fin de réponse CONNECT, 5 s pour l'ouverture ou l'accept du stream primaire et 5 s pour un envoi complet. Les valeurs nulles ou une taille incompatible avec le champ de longueur u32 sont rejetées avant démarrage.

La notion de deadline est volontairement opérationnelle et non un timeout d'inactivité global :

  • le client borne l'établissement de session ;
  • côté serveur, l'attente du prochain pair reste non bornée comme un listener normal, puis la réponse WebTransport après matérialisation de la requête CONNECT est bornée ;
  • ouverture/accept du stream primaire sont bornés ;
  • un envoi header + payload est borné, de sorte qu'un blocage durable sous flow-control/backpressure devient un TransportErrorKind::Timeout visible ;
  • receive() n'a pas de timeout d'idle implicite : l'absence de message n'est pas une panne de transport et l'appelant peut annuler sa future sans perdre l'état du framing.

Le receiver utilise donc un parseur incrémental persistant fondé sur la lecture cancel-safe du backend. Une future receive() abandonnée après une partie du header ou du payload peut être relancée et reprend au bon octet. EOF au milieu d'une frame devient Protocol, tandis qu'un EOF à frontière de frame reste TransportReceive::Closed. Une longueur supérieure à la borne est refusée avant allocation puis la direction de réception est stoppée.

Le lifecycle distingue désormais explicitement FIN propre et abandon :

  • RealtimeSender::close() émet le FIN propre ;
  • WebTransportSender::abort(code) reset la direction d'envoi ;
  • WebTransportReceiver::abort(code) stoppe la direction de réception ;
  • drop d'un sender/receiver encore actif provoque reset/stop au lieu de synthétiser une fermeture propre ;
  • cancellation d'un send() en cours arme un guard terminal qui reset le stream, car une frame partiellement écrite ne peut pas être reprise sans ambiguïté ;
  • cancellation d'un receive() reste non terminale grâce au parseur incrémental.

Les erreurs amont restent confinées au backend : reset/STOP observables -> Aborted, FIN/stream/session fermé proprement -> Closed, framing/état de stream invalide -> Protocol, dépassement -> MessageTooLarge, deadline -> Timeout, et autres erreurs de session -> Io. Aucun type Quinn/WebTransport n'entre dans game-realtime-transport-lib.

Aucune file applicative n'est ajoutée pour fabriquer artificiellement Backpressure : la pression est celle du flow-control QUIC. Lorsqu'elle empêche un envoi de terminer dans sa deadline, l'erreur observable est Timeout. Un test synthétique de saturation n'est pas imposé tant qu'il ne peut pas être rendu déterministe sans dépendre d'internals amont.

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 :

  1. build/check réel wasm32-unknown-unknown du chemin WebTransport Rust ;
  2. 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 PASS dé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 validée avec :

  • stream bidirectionnel principal ;
  • framing u32 + payload borné 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.md pour la séquence session -> stream primaire -> contrat commun.

La gate utilisateur du 2026-09-21 est conservée dans history/0.3.5/alpha.3.md.

0.3.5-alpha.4 — robustesse et lifecycle

Tranche candidate matérialisée avec :

  • WebTransportConfig et borne de message configurable, validée avant démarrage ;
  • deadlines de connexion/réponse CONNECT, stream primaire et send ;
  • flow-control QUIC conservé comme backpressure naturelle, avec timeout observable si un send reste bloqué ;
  • FIN propre distinct des reset/stop explicites ;
  • drop actif -> abort au lieu de FIN synthétique ;
  • cancellation d'un send partiel -> reset terminal ;
  • receive incrémental cancel-safe et reprenable ;
  • EOF au milieu d'une frame et framing invalide -> Protocol ;
  • dépassement entrant refusé avant allocation ;
  • mapping backend -> Timeout, MessageTooLarge, Closed, Protocol, Aborted ou Io ;
  • tests ciblés sur limites, reset/drop, deadline du stream, cancellation de receive et frame tronquée.

Un timeout d'idle de receive() n'est volontairement pas imposé au contrat : l'appelant peut borner/annuler l'attente sans corrompre l'état du frame parser. Un test artificiel de saturation/backpressure n'est pas ajouté tant qu'il n'est pas déterministe.

La gate utilisateur de alpha.4 a confirmé fmt, audits, cargo check, tous les tests du contrat commun et les vingt tests WebTransport, mais Clippy strict a détecté un unique clippy::implicit-return dans la future retournée par RealtimeReceiver::receive(). alpha.4.fix.1 a explicité ce return sans changement sémantique, d'API, de framing, de lifecycle ou de dépendance. Sa gate complète est ensuite passée : audits, check workspace, Clippy strict, sept tests du contrat commun et vingt tests WebTransport. La preuve est conservée dans history/0.3.5/alpha.4.fix.1.md.

0.3.5-alpha.5 — smoke natif hors harness

Tranche validée avec :

  • nouvelle app technique game-realtime-webtransport-smoke ;
  • bind UDP loopback sur port éphémère ;
  • identité TLS ECDSA P-256 locale générée en mémoire et pin SHA-256 dérivé de cette identité ;
  • établissement WebTransport client/server réel sur https://127.0.0.1:<port>/smoke ;
  • ouverture/accept du stream fiable principal via l'API publique du backend ;
  • round-trip binaire bidirectionnel via RealtimeConnection ;
  • FIN propre client puis serveur, chacun observé comme TransportReceive::Closed par le pair ;
  • timeout global du launcher et verdict terminal déterministe game-realtime-webtransport-smoke: PASS ;
  • aucune nouvelle dépendance externe et aucun couplage engine/gameplay.

Cette tranche reste distincte pour ne pas mélanger robustesse de bibliothèque et preuve runtime publique. La gate utilisateur du 2026-09-21 a confirmé fmt, audits, check workspace, Clippy strict, les sept tests du contrat commun, les vingt tests WebTransport, le smoke hors harness avec verdict PASS et les graphes direct/inverse attendus. La preuve est conservée dans history/0.3.5/alpha.5.md.

0.3.5-alpha.6 — chemin WASM compilable

Tranche candidate matérialisée avec :

  • web-transport-wasm 0.6.0 comme dépendance exclusivement wasm32 ;
  • web_sys_unstable_apis ciblé par Cargo uniquement sur wasm32-unknown-unknown, y compris rustdoc ;
  • dépendances Quinn/Tokio/rcgen du backend déplacées dans la branche native afin que le build WASM ne tire pas la stack serveur ;
  • même surface client publique qu'en natif pour hash SHA-256, config endpoint, connect, session et stream primaire ;
  • pin navigateur transmis via serverCertificateHashes ;
  • même framing fiable u32 big-endian + payload et même limite avant allocation/écriture ;
  • RealtimeConnection, sender et receiver adaptés à des types navigateur !Send, ce que le contrat commun autorise déjà ;
  • FIN, reset/STOP, cancellation de send terminale et réception incrémentale conservés sur le chemin navigateur ;
  • aucun changement Snake/Reflex et aucun host frontend.

La gate utilisateur du 2026-09-22 a confirmé le host natif inchangé, les sept tests du contrat commun, les vingt tests WebTransport, le smoke natif PASS, puis cargo check, Clippy --lib et build réel de game-realtime-webtransport-lib sur wasm32-unknown-unknown. Le graphe WASM ne tire ni Quinn, ni rcgen, ni Tokio. La preuve est conservée dans history/0.3.5/alpha.6.md.

Différence fermée dans alpha.7 : les deadlines WebTransportConfig sont matérialisées côté navigateur avec un timer WASM. connect_timeout, primary_stream_timeout et send_timeout sont bornés ; un send expiré reset le stream comme en natif. receive() reste volontairement sans timeout d'idle implicite. Les durées navigateur doivent tenir dans u32 millisecondes, plage du timer retenu, sinon la configuration est refusée avant établissement.

0.3.5-alpha.7 — interop navigateur et TLS local

Tranche candidate matérialisée avec :

  • gloo-timers target-specific WASM pour appliquer les deadlines sans runtime Tokio navigateur ;
  • game-realtime-webtransport-browser-smoke, package technique avec pair serveur natif et adapter cdylib WASM ;
  • host direct Web/game-realtime-webtransport-browser-smoke en Vite/TypeScript sur 127.0.0.1:1435 ;
  • page loopback vérifiant window.isSecureContext et la présence de WebTransport avant d'appeler le Rust/WASM ;
  • serveur Rust sur port QUIC éphémère, certificat P-256 court en mémoire et SHA-256 imprimé explicitement ;
  • endpoint/hash passés au frontend par query string ou saisie manuelle ;
  • navigateur -> serveur Rust WebTransport sans fallback WebSocket ;
  • round-trip binaire dans les deux sens via le stream primaire et RealtimeConnection ;
  • FIN navigateur observé côté serveur, puis FIN serveur observé côté navigateur ;
  • verdict game-realtime-webtransport-browser-smoke: PASS des deux côtés ;
  • aucune désactivation de TLS et aucun secret/certificat versionné.

La page Vite peut rester servie en HTTP sur loopback : 127.0.0.1 est un origin potentiellement fiable/secure context dans les navigateurs conformes. Le transport lui-même reste impérativement https:// et utilise serverCertificateHashes avec le certificat X.509v3 court ECDSA P-256 déjà produit par le backend.

Cette tranche reste volontairement séparée de alpha.8 : le smoke WebTransport navigateur ne doit contenir aucun fallback silencieux. La sélection/fallback WebSocket est prouvée ensuite au niveau composition.

Correctif 0.3.5-alpha.7.fix.1 : la première gate utilisateur a validé fmt, audits, check/Clippy natifs, tests, smoke natif, check/Clippy/build WASM et la compilation de l'adapter navigateur, puis a échoué uniquement dans npm run build lorsque wasm-bindgen cherchait l'artefact Cargo sous ../../builds/.... Depuis Web/game-realtime-webtransport-browser-smoke, le target-dir workspace ../builds/sasedev-games/target se trouve en réalité sous ../../../builds/.... Le fix corrige les scripts wasm:dev/wasm:build et l'audit de distribution associé, sans changement Rust/runtime.

Correctif 0.3.5-alpha.7.fix.2 : la gate du premier fix valide fmt, audits, check/Clippy natifs et le build WASM de l'adapter, puis npm run build atteint TypeScript et échoue uniquement parce que l'alias @webtransport-browser-smoke-wasm cherche encore les déclarations wasm-bindgen sous ../../builds/.... Le chemin paths du tsconfig.json est aligné sur ../../../builds/...; l'audit de distribution ancre désormais aussi cette résolution. Les erreurs implicit any observées sur les callbacks sont une conséquence de ce module non résolu et disparaissent lorsque la déclaration générée est chargee. Aucun code Rust/runtime ni protocole n'est modifié.

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 tree direct/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.md si la transition vers RC est préparée ;
  • réconcilier ROADMAP.md seulement si le statut macro change ;
  • écrire le prompt 0.3.6 ;
  • enregistrer l'historique beta.1 aprè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.5 pour prouver la frontière ;
  • à beta.1 et rc.1 pour 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.