# Delta 0.3.5-alpha.7 ## Base requise `0.3.5-alpha.6`, validée par l'utilisateur le 2026-09-22 avec fmt, audits, `cargo check`, Clippy strict, les sept tests du contrat realtime commun, les vingt tests WebTransport, le smoke natif `PASS`, puis check/Clippy/build réels de `game-realtime-webtransport-lib` en `wasm32-unknown-unknown`. Le graphe WASM confirmé ne tire ni `web-transport-quinn`, ni rcgen, ni Tokio. La preuve est enregistrée dans `history/0.3.5/alpha.6.md`. ## Objectif Fermer l'interop navigateur réelle du POC WebTransport sans introduire encore le fallback WebSocket : - appliquer réellement les deadlines `WebTransportConfig` côté browser/WASM ; - charger le client Rust/WASM dans un host Vite technique isolé ; - connecter un navigateur récent à un serveur Rust WebTransport local ; - transmettre explicitement le hash SHA-256 du certificat court P-256 ; - effectuer un round-trip binaire bidirectionnel via le stream primaire ; - fermer proprement les deux directions ; - produire un verdict `PASS` reproductible côté navigateur et côté serveur. ## Version La version workspace passe à : ```text 0.3.5-alpha.7 ``` ## Deadlines navigateur Le target `wasm32` de `game-realtime-webtransport-lib` ajoute uniquement : ```text gloo-timers 0.4.0 futures-util 0.3.34 avec feature std ``` Les dépendances restent target-specific et ne changent pas le backend natif. Les opérations navigateur suivantes sont maintenant bornées : ```text connect_timeout primary_stream_timeout send_timeout ``` La politique reste identique au natif : - `connect` expiré -> `TransportErrorKind::Timeout` ; - ouverture du stream primaire expirée -> `Timeout` ; - send expiré -> `Timeout` puis reset terminal du stream ; - `receive()` idle reste sans timeout implicite ; - cancellation de `receive()` reste reprenable grâce au parseur incrémental. Le timer browser prend des millisecondes `u32`. Une deadline qui devient zéro après conversion ou dépasse cette plage est rejetée comme `InvalidConfiguration` avant établissement. ## Smoke navigateur Nouveau package technique : ```text crates/apps/game-realtime-webtransport-browser-smoke ``` Le même package possède : - un binary natif qui génère l'identité loopback P-256 en mémoire, bind un serveur WebTransport sur port éphémère, affiche endpoint + SHA-256 et attend exactement le round-trip navigateur ; - une lib `cdylib`/`rlib` dont l'export `run_browser_smoke(...)` compile en WASM et consomme réellement `game-realtime-webtransport-lib`. Nouveau host direct : ```text Web/game-realtime-webtransport-browser-smoke ``` Il utilise Vite + TypeScript, génère les bindings avec `wasm-bindgen` hors du repository et sert la page sur : ```text http://127.0.0.1:1435/main.html ``` Le loopback HTTP est un secure context de navigateur. La page vérifie néanmoins `window.isSecureContext` et la présence de `WebTransport` avant d'exécuter le WASM. Le transport cible toujours : ```text https://127.0.0.1:/browser-smoke ``` avec `serverCertificateHashes` SHA-256 ; aucune vérification TLS n'est désactivée. ## Round-trip prouvé par le smoke La séquence attendue est : ```text browser WASM -> connect WebTransport avec pin SHA-256 -> open primary bidirectional stream -> send browser binary payload native Rust peer -> receive exact payload -> send server binary payload browser WASM -> receive exact payload -> FIN send direction native Rust peer -> observe Closed -> FIN send direction browser WASM -> observe Closed ``` Le navigateur doit afficher : ```text game-realtime-webtransport-browser-smoke: PASS ``` et le binary serveur doit ensuite terminer avec la même ligne. Aucun fallback WebSocket n'existe dans cette preuve ; un échec WebTransport doit rester visible. ## Audit de distribution `scripts/audit_distribution_layout.py` possède désormais le host technique et son adapter Rust. Il vérifie notamment : - génération WASM hors repository ; - host Vite strictement loopback sur le port 1435 ; - contrôle secure-context/WebTransport côté page ; - appel réel à `run_browser_smoke(...)` ; - dépendance de l'adapter vers `game-realtime-webtransport-lib` ; - verdict `PASS` déterministe du peer natif. ## Fichiers modifiés ```text Cargo.toml README.md crates/common/game-realtime-webtransport-lib/Cargo.toml crates/common/game-realtime-webtransport-lib/README.md crates/common/game-realtime-webtransport-lib/USAGE.md crates/common/game-realtime-webtransport-lib/src/webtransport_wasm.rs docs/plans/005-V0_3_5_WEBTRANSPORT_QUIC_POC_PLAN.md scripts/audit_distribution_layout.py ``` Nouveaux fichiers : ```text crates/apps/game-realtime-webtransport-browser-smoke/Cargo.toml crates/apps/game-realtime-webtransport-browser-smoke/src/browser.rs crates/apps/game-realtime-webtransport-browser-smoke/src/lib.rs crates/apps/game-realtime-webtransport-browser-smoke/src/main.rs crates/apps/game-realtime-webtransport-browser-smoke/src/shared.rs Web/game-realtime-webtransport-browser-smoke/package.json Web/game-realtime-webtransport-browser-smoke/tsconfig.json Web/game-realtime-webtransport-browser-smoke/vite.config.ts Web/game-realtime-webtransport-browser-smoke/frontend/main.html Web/game-realtime-webtransport-browser-smoke/frontend/ts/main.ts deltas/0.3.5/alpha.7.md history/0.3.5/alpha.6.md ``` ## Validation attendue Gate Rust native de non-régression : ```bash cargo fmt --all cargo fmt --all -- --check python3 scripts/audit_rust_workspace_rules.py python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates Android Web deltas history python3 scripts/audit_distribution_layout.py cargo check --workspace cargo clippy --workspace --all-targets --all-features -- -D warnings cargo test -p game-realtime-transport-lib --all-targets --all-features cargo test -p game-realtime-webtransport-lib --all-targets --all-features cargo run -p game-realtime-webtransport-smoke ``` Gate WASM : ```bash cargo check -p game-realtime-webtransport-lib --target wasm32-unknown-unknown cargo clippy -p game-realtime-webtransport-lib --target wasm32-unknown-unknown --lib -- -D warnings cargo build -p game-realtime-webtransport-lib --target wasm32-unknown-unknown cargo check -p game-realtime-webtransport-browser-smoke cargo clippy -p game-realtime-webtransport-browser-smoke --all-targets --all-features -- -D warnings cargo build -p game-realtime-webtransport-browser-smoke --lib --target wasm32-unknown-unknown ``` Build du host Vite direct : ```bash (cd Web/game-realtime-webtransport-browser-smoke && npm install && npm run build) ``` Smoke runtime réel, avec deux terminaux : Terminal A : ```bash cargo run -p game-realtime-webtransport-browser-smoke ``` Le launcher affiche une ligne `OPEN=...` contenant l'endpoint éphémère et le hash SHA-256. Terminal B : ```bash (cd Web/game-realtime-webtransport-browser-smoke && npm run dev) ``` Ouvrir dans un navigateur récent l'URL `OPEN=...` imprimée par le Terminal A. Si le navigateur demande l'autorisation d'accès loopback/local, l'accorder pour cette preuve locale. Résultat attendu dans la page puis dans le Terminal A : ```text game-realtime-webtransport-browser-smoke: PASS ``` Vérifier enfin le graphe cible : ```bash cargo tree -p game-realtime-webtransport-browser-smoke --target wasm32-unknown-unknown --edges normal cargo tree -i game-realtime-webtransport-lib --workspace --edges normal cargo tree -i gloo-timers --workspace --target wasm32-unknown-unknown --edges normal ``` Le backend navigateur doit rester indépendant de Quinn/rcgen/Tokio ; `gloo-timers` ne doit apparaître que sur la cible WASM. ## Après validation Créer `history/0.3.5/alpha.7.md` à partir des sorties réellement fournies, puis ouvrir `0.3.5-alpha.8` pour le fallback WebSocket au niveau composition avec branches WebTransport, fallback classifié et erreur non-fallback visible.