Files
2026-08-22 19:16:41 +02:00

173 lines
6.0 KiB
Markdown

<!-- file: deltas/0.2.7/pre.005.md -->
<!-- version: 1 -->
# Delta `0.2.7-pre.005` — limits adversariales + control frames + shutdown borné
## 1. Base requise
```text
0.2.7-pre.004-fix.001 appliqué
workspace.package.version = 0.2.7-pre.4.fix.1
```
Le checkpoint opérateur reçu avant cette tranche est vert : `cargo fmt --all`, audit Python, `cargo check --workspace`, `cargo clippy --workspace --all-targets` et `cargo test --workspace`. Les 261 tests unitaires Transport de `pre.004-fix.001` passent également.
## 2. Signal technique
Cette prerelease modifie le runtime Rust, les tests et la documentation. Conformément aux règles KSP :
```text
livraison = 0.2.7-pre.005
workspace.package.version = 0.2.7-pre.5
commit = v0.2.7-pre.005
```
Aucun tag prerelease.
## 3. Shutdown explicite `WsSession::close()`
Nouvelle surface publique :
```text
WsSession::close().await
```
Le signal de shutdown est transporté par un canal `watch` distinct de la command queue. Une command queue saturée ne peut donc pas empêcher la demande de fermeture.
Lifecycle :
```text
Active -> Closing -> Closed
remote Close propre -> Closed
I/O ou violation protocolaire -> Failed
```
`close()` :
1. publie la demande de shutdown avec une deadline issue de `close_timeout` ;
2. l'actor passe `Closing` ;
3. toutes les requests JSON-RPC pending reçoivent `ws_session_closed` ;
4. l'actor tente un Close WebSocket best-effort sous la deadline ;
5. l'actor publie `Closed` ;
6. le caller attend `Closed` sans pouvoir rester bloqué indéfiniment.
Le reconnect reste absent jusqu'à `pre.007`.
## 4. Control frames
Ping/Pong/Close sont maintenant testés explicitement.
Pour Ping, Tungstenite met automatiquement le Pong correspondant en file lors de la lecture ; KSP force le `flush` de cette réponse automatique. Aucun Pong duplicatif et aucun heartbeat applicatif périodique ne sont ajoutés.
Un Close distant propre est distingué d'une rupture I/O : il termine la session en `Closed`, alors qu'une rupture physique ou une violation de protocole termine en `Failed`.
## 5. Bornes frame/message/write
Les limites configurées depuis `pre.002` et raccordées à Tungstenite depuis `pre.004` sont désormais couvertes par des fixtures adversariales :
```text
max_message_size
max_frame_size
max_write_buffer_size
```
Un payload JSON-RPC outbound dépassant `max_message_size` ou `max_frame_size` est rejeté avant écriture avec `invalid_rpc_parameters`, sans teardown d'une session saine.
Un frame/message inbound surdimensionné est rejeté par Tungstenite avant que KSP tente le parse JSON ; l'anomalie est classée protocolaire et la session devient `Failed`.
Aucune limite legacy Solana de type 1232 bytes n'est introduite.
## 6. Pending requests et cancellation
Le runtime `pre.005` ajoute les preuves suivantes :
- `max_pending_requests` rejette uniquement la request excédentaire avec `ws_backpressure_overflow` ;
- une request pending silencieuse expire selon `command_timeout`, reçoit `timeout` et libère immédiatement sa capacité ;
- une fermeture explicite annule toutes les pending requests avec `ws_session_closed` ;
- un receiver caller abandonné est purgé du pending map au prochain cycle actor, et reste de toute façon borné par la deadline de request.
La cancellation de subscriptions et les races unsubscribe/reconnect restent dans `pre.006`/`pre.007`.
## 7. Socket I/O borné et shutdown prioritaire
Les writes JSON-RPC et le flush Pong surveillent le signal shutdown pendant leur attente. La fermeture peut donc préempter une opération socket qui serait autrement bloquante.
Un write JSON-RPC dépassant `command_timeout` provoque une erreur caller bornée et termine la session physique, car l'état de l'écriture devient impropre à une poursuite sûre.
## 8. Fixtures adversariales locales
Les tests Transport ajoutent notamment :
```text
pending capacity = 1 avec deuxième request rejetée isolément
oversized outbound request rejetée avant write
oversized inbound frame/message rejeté avant JSON
pending request timeout + purge
Ping -> Pong + session toujours Active
remote Close -> Closed
close explicite + pending cancellation + peer hostile
8 cycles connect/close bornés
```
Toutes les fixtures restent locales et déterministes ; aucun réseau Solana réel n'est requis.
## 9. Logging / sécurité
Toutes les émissions passent exclusivement par `ksp-logging-lib` et réutilisent :
```text
TRACING_TARGET = "ksp-onchain-transport-lib"
```
Les nouveaux diagnostics ne loggent que session ID, endpoint logique, méthode statique, compteurs et tailles numériques. Les URLs, credentials, query tokens et payloads JSON restent absents.
Les erreurs Tungstenite brutes ne sont pas projetées dans les erreurs KSP afin d'éviter une fuite de données provider.
## 10. Public API et documentation
Le canari public API vérifie désormais `WsSession::close` et les états `Closing`/`Closed`.
Synchronisés :
```text
crates/ksp-onchain-transport-lib/README.md
crates/ksp-onchain-transport-lib/USAGE.md
docs/plans/014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md
docs/validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md
```
`ROADMAP.md` et `CHANGELOG.md` restent inchangés pendant la série prerelease.
## 11. Validation de préparation
Le sandbox de génération doit au minimum vérifier :
```text
python3 scripts/audit_rust_workspace_rules.py
absence de tracing direct
présence du TRACING_TARGET existant
absence de question-mark operator dans le runtime modifié
overlay exact du delta
```
Cargo n'est pas disponible dans le sandbox de génération ; aucun résultat Cargo local n'est revendiqué.
## 12. Gates opérateur avant commit
```bash
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-onchain-transport-lib
cargo test --workspace
```
Si le checkpoint est vert :
```text
commit = v0.2.7-pre.005
```
La tranche suivante est `0.2.7-pre.006` : registry subscriptions, IDs locaux stables, moteur generic subscribe/unsubscribe et channels typed bornés.