From 9e8fd53291b55aa7f1fe1064f1ba2d4fd34eb34e Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Sat, 22 Aug 2026 15:34:03 +0200 Subject: [PATCH] v0.2.7-pre.001 --- Cargo.toml | 4 +- deltas/0.2.7/pre.001.md | 245 ++++++ docs/000-README.md | 10 +- docs/plans/000-README.md | 3 +- docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md | 10 +- .../014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md | 791 ++++++++++++++++++ docs/validation/000-README.md | 3 +- .../010-V0_2_7_ONCHAIN_WEBSOCKET.md | 260 ++++++ 8 files changed, 1315 insertions(+), 11 deletions(-) create mode 100644 deltas/0.2.7/pre.001.md create mode 100644 docs/plans/014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md create mode 100644 docs/validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md diff --git a/Cargo.toml b/Cargo.toml index ae9d56a..1cc7582 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 191 +# version: 192 [workspace] resolver = "3" members = ["crates/ksp-app-config-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-logging-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-wallet-lib"] [workspace.package] -version = "0.2.6" +version = "0.2.7-pre.1" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/deltas/0.2.7/pre.001.md b/deltas/0.2.7/pre.001.md new file mode 100644 index 0000000..14ec1a3 --- /dev/null +++ b/deltas/0.2.7/pre.001.md @@ -0,0 +1,245 @@ + + + +# Delta `0.2.7-pre.001` — audit WebSocket Solana, threat model, dependencies et sizing + +## Base requise + +Release stable attendue et auditée : + +```text +v0.2.6 +workspace.package.version = 0.2.6 avant ouverture +``` + +L'archive Gitea fournie contient `deltas/0.2.6/rel.001.md` et annonce `0.2.7 — WebSocket Solana standard` comme prochaine release. Elle est utilisée comme autorité primaire. + +## Type de livraison + +```text +ksp-general-0.2.7-pre.001.zip +``` + +L'archive d'échange est un delta applicable depuis la racine de `v0.2.6` et contient uniquement les fichiers ajoutés/modifiés par cette tranche. + +## Objet + +`pre.001` reste volontairement un gate de lecture/audit/conception. Aucun client WebSocket, session runtime, wrapper subscribe ou dependency réseau nouvelle n'est encore ajouté. + +Le gate : + +- relit les règles, architecture, plans, validations et contrats réels requis ; +- vérifie la stabilité `v0.2.6` et l'héritage HTTP/Wallet Desk ; +- réaudite `ksp-onchain-transport-lib` et l'adapter Config actuel ; +- constate que `std.transport` V1 est strictement HTTP et décide un V2 explicite HTTP+WS avec backward V1 ; +- audite l'archive bot3 comme référence historique seulement ; +- réaudite la documentation Solana WebSocket officielle du 2026-08-22 et cross-checke Agave `v3.1.8` sur les ambiguïtés ; +- documente notamment `accountSubscribe.minContextSlot` comme option partagée mais ignorée en PubSub, et `vote.timestamp` comme `Option` ; +- compte exactement **18 méthodes = 9 subscribe + 9 unsubscribe** ; +- classe `block`, `slotsUpdates` et `vote` comme paires unstable ; +- crée la matrice compliance initiale `010` ; +- audite les crates candidates et retient `tokio-tungstenite 0.30.0` + `futures-util 0.3.34` pour une tranche ultérieure ; +- fixe le modèle actor/session, IDs locaux, state machines, reconnect/resubscribe, continuity gaps, backpressure et shutdown ; +- fixe les exigences de redaction URL/credentials et de bornes de ressources ; +- regranularise la release jusqu'à un forecast nominal `pre.014` sans imposer ce numéro comme deadline. + +## Décisions principales + +### Cardinalité + +```text +endpoint -> N sessions physiques explicites -> N subscriptions par session +aucun pool/scheduler automatique en 0.2.7 +``` + +### Identités + +```text +WsSessionId stable local KSP +WsSubscriptionId stable local KSP +remote id éphémère et interne, remappé après reconnect +``` + +### Reconnect / resubscribe + +```text +budget fini +backoff exponentiel borné +pas de jitter en 0.2.7 +policy Never | ActiveSubscriptions +ordre de restore déterministe par ID local +continuity gap explicite après toute reconnexion +aucune garantie lossless / aucun backfill HTTP Transport +``` + +### Backpressure + +```text +command queue bounded +notification queue bounded par subscription +overflow -> subscription Failed explicite + best-effort unsubscribe +aucun drop silencieux +les autres subscriptions restent actives +``` + +### Config + +```text +std.transport V1 reste strict et lisible +std.transport V2 = HTTP existant + ws_defaults + profiles[].ws_endpoints +Config -> Transport uniquement +``` + +### Dependencies + +```text +tokio-tungstenite ^0.30, default-features=false, connect + rustls-tls-webpki-roots +futures-util ^0.3, default-features=false, std + sink +``` + +Ces dependencies sont **planifiées seulement** ; le graphe n'est pas modifié dans `pre.001`. + +## Prévision souple recalibrée + +```text +pre.001 audit + matrice + threat model + dependencies + sizing +pre.002 settings/IDs/states/snapshots/redaction +pre.003 std.transport V2 + adapter Config +pre.004 actor session physique + deps WS + local server +pre.005 limits/control/cancellation/shutdown +pre.006 registry + generic subscribe/unsubscribe + channels typed +pre.007 reconnect/resubscribe/gap/races +pre.008 backpressure/limits/leaks adversarial +pre.009 account/program/logs +pre.010 signature/slot/root +pre.011 block/slotsUpdates/vote unstable +pre.012 compliance 18/18 + Config composition + HTTP regression +pre.013 smoke live + README/USAGE + cargo trees +pre.014 workspace final + docs/compliance + prompt 0.2.8 +rel.001 publication stable +``` + +Le sizing reste positif : la release n'est pas scindée fonctionnellement, mais le forecast initial `pre.008` est volontairement décompressé. + +## Fichiers ajoutés + +```text +docs/plans/014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md +docs/validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md +deltas/0.2.7/pre.001.md +``` + +## Fichiers modifiés + +```text +Cargo.toml +docs/000-README.md +docs/plans/000-README.md +docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md +docs/validation/000-README.md +``` + +## Fichiers volontairement inchangés + +```text +CHANGELOG.md +ROADMAP.md +.env.example +config/** +crates/** +docs/architecture/** +crates/ksp-onchain-transport-lib/README.md +crates/ksp-onchain-transport-lib/USAGE.md +``` + +`ROADMAP.md` reste global et possède déjà l'entrée `0.2.7`. README/USAGE Transport ne sont pas modifiés avant qu'une surface runtime WebSocket existe réellement. + +## Version technique + +Conformément au workflow non-fix : + +```text +workspace.package.version = 0.2.7-pre.1 +commit = v0.2.7-pre.001 +aucun tag prerelease +``` + +## Audit officiel WebSocket + +Index : + +```text +https://solana.com/docs/rpc/websocket +``` + +Inventaire exact au 2026-08-22 : + +```text +accountSubscribe/accountUnsubscribe +blockSubscribe/blockUnsubscribe unstable pair +logsSubscribe/logsUnsubscribe +programSubscribe/programUnsubscribe +rootSubscribe/rootUnsubscribe +signatureSubscribe/signatureUnsubscribe +slotSubscribe/slotUnsubscribe +slotsUpdatesSubscribe/slotsUpdatesUnsubscribe unstable pair +voteSubscribe/voteUnsubscribe unstable pair +``` + +Aucune méthode de cet index n'est marquée Deprecated. + +## Audit bot3 + +Référence inspectée : + +```text +ks-onchain-transport/src/standard_ws.rs +ks-onchain-transport/src/ws_client.rs +ks-onchain-transport/src/ws_pool.rs +ks-onchain-transport/src/ws_session.rs +``` + +Repris comme concepts : session multiplexée, ID local/remote séparé, reconnect/resubscribe borné. Rejetés : Transport -> Config, tracing direct, scheduler/pool, unsubscribe public par remote ID et broadcast data comme contrat principal. + +## Validations exécutées avant modification + +```text +archive stable v0.2.6 extraite/auditée OK +documents internes obligatoires relus OK +inventory Transport + Config OK +archive bot3 auditée OK +documentation Solana WebSocket actuelle auditée OK +dependencies Rust candidates auditée OK +python3 scripts/audit_rust_workspace_rules.py OK +``` + +Sortie audit Python : + +```text +General Rust rule audit: clean +Rust export completeness audit: 0 candidate(s) +KSP workspace Rust rule audit: clean +``` + +## Validations tentées mais impossibles dans le sandbox + +Le binaire `cargo` n'est pas installé. Tentatives avant modification : + +```text +cargo fmt --all code 127 +cargo check --workspace code 127 +cargo clippy --workspace --all-targets code 127 +``` + +Aucune de ces commandes n'est déclarée réussie. + +## Validation opérateur requise avant commit + +```bash +cargo fmt --all +python3 scripts/audit_rust_workspace_rules.py +cargo check --workspace +cargo clippy --workspace --all-targets +``` + +Aucun test Transport, Config, workspace ou smoke live n'est déclaré vert dans ce sandbox tant qu'il n'a pas été effectivement exécuté par l'opérateur. Aucun build Tauri n'est requis pour `0.2.7-pre.001`. diff --git a/docs/000-README.md b/docs/000-README.md index c63d7bf..0685e0d 100644 --- a/docs/000-README.md +++ b/docs/000-README.md @@ -1,5 +1,5 @@ - + # Documentation KSP @@ -49,7 +49,8 @@ docs/ │ ├── 010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md │ ├── 011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md │ ├── 012-V0_2_5_WALLET_FOUNDATION_PLAN.md -│ └── 013-V0_2_6_WALLET_DESK_PLAN.md +│ ├── 013-V0_2_6_WALLET_DESK_PLAN.md +│ └── 014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md ├── validation/ │ ├── 000-README.md │ ├── 001-V0_1_4_CONFIG_DESKTOP.md @@ -60,7 +61,8 @@ docs/ │ ├── 006-V0_2_3_HTTP_TRANSACTIONS.md │ ├── 007-V0_2_4_HTTP_FINAL_COMPLIANCE.md │ ├── 008-V0_2_5_WALLET_SECURITY_COMPLIANCE.md -│ └── 009-V0_2_6_WALLET_DESK_COMPLIANCE.md +│ ├── 009-V0_2_6_WALLET_DESK_COMPLIANCE.md +│ └── 010-V0_2_7_ONCHAIN_WEBSOCKET.md └── rules/ ├── FILE_CONTRACTS.md ├── PROMPT_STRUCTURE.md @@ -77,7 +79,7 @@ D'autres sous-répertoires seront ajoutés uniquement lorsque leur rôle aura é ## Documents de planification -Le plan historique de la phase fondatrice clôturée est conservé dans [`plans/001-V0_0_3_PLAN.md`](plans/001-V0_0_3_PLAN.md). La séquence active des premières releases fonctionnelles est définie dans [`plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md`](plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md). Le plan détaillé de la release stable `0.1.1` est conservé comme historique clôturé dans [`plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md`](plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md). Le plan détaillé de la release stable `0.1.2` est conservé comme historique clôturé dans [`plans/004-V0_1_2_LOGGING_FOUNDATION_PLAN.md`](plans/004-V0_1_2_LOGGING_FOUNDATION_PLAN.md). Le plan détaillé de la release stable `0.1.3 — Configuration foundation` est conservé comme historique clôturé dans [`plans/005-V0_1_3_CONFIG_FOUNDATION_PLAN.md`](plans/005-V0_1_3_CONFIG_FOUNDATION_PLAN.md). Le plan détaillé de la release stable `0.1.4 — ksp-app-config-desk` est conservé comme historique clôturé dans [`plans/006-V0_1_4_CONFIG_DESKTOP_PLAN.md`](plans/006-V0_1_4_CONFIG_DESKTOP_PLAN.md), avec sa matrice finale [`validation/001-V0_1_4_CONFIG_DESKTOP.md`](validation/001-V0_1_4_CONFIG_DESKTOP.md). Son prompt d'ouverture historique reste [`../prompts/004-V0_1_4_START_PROMPT.md`](../prompts/004-V0_1_4_START_PROMPT.md). La release stable `0.2.0` clôt l'audit de bot3 et le découpage de la série. Son plan directeur est conservé comme historique clôturé dans [`plans/007-V0_2_0_SERIES_PLANNING.md`](plans/007-V0_2_0_SERIES_PLANNING.md), avec sa matrice finale [`validation/002-V0_2_0_SERIES_PLANNING.md`](validation/002-V0_2_0_SERIES_PLANNING.md). La release stable `0.2.1 — HTTP Solana foundation` a été ouverte par [`../prompts/006-V0_2_1_START_PROMPT.md`](../prompts/006-V0_2_1_START_PROMPT.md). Son gate de sizing et sa matrice exhaustive sont conservés dans [`plans/008-V0_2_1_ONCHAIN_HTTP_PLAN.md`](plans/008-V0_2_1_ONCHAIN_HTTP_PLAN.md), avec la validation finale [`validation/003-V0_2_1_ONCHAIN_HTTP.md`](validation/003-V0_2_1_ONCHAIN_HTTP.md), README/USAGE Transport et le smoke Devnet opt-in de composition Config -> Transport. Le prompt [`../prompts/007-V0_2_2_START_PROMPT.md`](../prompts/007-V0_2_2_START_PROMPT.md) a ouvert la release stable `0.2.2 — HTTP Accounts + Tokens + Cluster`. Son plan clôturé [`plans/009-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER_PLAN.md`](plans/009-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER_PLAN.md) conserve l'audit et l'implémentation des 22 wrappers typés, tandis que [`validation/004-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER.md`](validation/004-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER.md) enregistre les validations déterministes, les graphes Cargo et les deux smokes Devnet passés avant publication. Le prompt [`../prompts/008-V0_2_3_START_PROMPT.md`](../prompts/008-V0_2_3_START_PROMPT.md) a ouvert la release stable `0.2.3 — HTTP Transactions`. Son plan clôturé [`plans/010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md`](plans/010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md) conserve l'audit et l'implémentation des 11 wrappers ; le réaudit [`validation/005-V0_2_3_KSP_TRANSPORT_007_RETRO_AUDIT.md`](validation/005-V0_2_3_KSP_TRANSPORT_007_RETRO_AUDIT.md) confirme la complétude des 37 wrappers HTTP typés et [`validation/006-V0_2_3_HTTP_TRANSACTIONS.md`](validation/006-V0_2_3_HTTP_TRANSACTIONS.md) enregistre les validations finales, graphes Cargo et deux smokes Devnet passés avant publication. Le prompt [`../prompts/009-V0_2_4_START_PROMPT.md`](../prompts/009-V0_2_4_START_PROMPT.md) a ouvert la release stable `0.2.4 — HTTP Blocks + Economics + compliance HTTP finale`. Son plan clôturé [`plans/011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md`](plans/011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md) conserve l’implémentation des 15 wrappers et la compliance `52/52 + 14/14`; la matrice finale [`validation/007-V0_2_4_HTTP_FINAL_COMPLIANCE.md`](validation/007-V0_2_4_HTTP_FINAL_COMPLIANCE.md) enregistre le réaudit SIMD/inventaire, les canaries globales et les preuves opérateur avant publication. Le prompt [`../prompts/010-V0_2_5_START_PROMPT.md`](../prompts/010-V0_2_5_START_PROMPT.md), finalisé par `0.2.4-pre.009-fix.001`, ouvre `0.2.5 — Wallet foundation` sur la base stable `v0.2.4`. Son plan historique clôturé [`plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md`](plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md) part du gate `pre.001` (héritage, threat model offline, VIEW/OWNER indépendants et niveau B read-only), puis matérialise la crate en `pre.002`, le wire/transcript en `pre.003`, les primitives Argon2id/XChaCha20-Poly1305 en `pre.004`, les payloads/create/open en `pre.005`, la persistence en `pre.006`, l'administration/signature en `pre.007` et les adapters transfer en `pre.008`. `pre.009` ferme l'audit adversarial/interoperability/compliance dans [`validation/008-V0_2_5_WALLET_SECURITY_COMPLIANCE.md`](validation/008-V0_2_5_WALLET_SECURITY_COMPLIANCE.md) avant la documentation finale `pre.010` ; `pre.010` finalise [`../crates/ksp-wallet-lib/README.md`](../crates/ksp-wallet-lib/README.md), [`../crates/ksp-wallet-lib/USAGE.md`](../crates/ksp-wallet-lib/USAGE.md), la spec, les graphes et la matrice ; `pre.010-fix.001`–`fix.003` ferment ensuite la mise à niveau Dalek et la normalisation Rust/audit structurel. `0.2.5-rel.001` publie la release stable et [`../prompts/011-V0_2_6_START_PROMPT.md`](../prompts/011-V0_2_6_START_PROMPT.md) ouvre `0.2.6 — Wallet Desk`. Le gate `0.2.6-pre.001` est conservé dans [`plans/013-V0_2_6_WALLET_DESK_PLAN.md`](plans/013-V0_2_6_WALLET_DESK_PLAN.md) : il réaudite Config Desk et les APIs finales, retient le gabarit desktop, fixe `std.wallet`, la composition Config/Wallet/HTTP/Logging, les secrets `KSP_SECRET_WALLET_PASS_*`, les frontières VIEW/OWNER et la trajectoire de validation. `pre.002`–`pre.014` matérialisent ensuite le shell Tauri, Config Wallet/composite, inventory, create/open, balance HTTP, import/export, metadata, rotations, révocation VIEW forte, compliance et polish desktop. `pre.015` fige le wire binaire `.kspwallet` V2, `pre.016` matérialise les APIs génériques/versionnées et le runtime V2, puis `pre.017` ajoute la migration explicite OWNER-authentifiée V1 -> V2. `pre.018` ferme le runtime Tauri packagé Config/resources et la documentation candidate ; `pre.018-fix.001` corrige le canari d'ownership Config, après quoi le gate workspace et le build final Linux sont verts. `pre.018-fix.002` renforce uniquement le contrat de reprise `0.2.7`. `0.2.6-rel.001` publie cette surface stable et [`../prompts/012-V0_2_7_START_PROMPT.md`](../prompts/012-V0_2_7_START_PROMPT.md) devient le prochain point d'entrée. +Le plan historique de la phase fondatrice clôturée est conservé dans [`plans/001-V0_0_3_PLAN.md`](plans/001-V0_0_3_PLAN.md). La séquence active des premières releases fonctionnelles est définie dans [`plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md`](plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md). Le plan détaillé de la release stable `0.1.1` est conservé comme historique clôturé dans [`plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md`](plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md). Le plan détaillé de la release stable `0.1.2` est conservé comme historique clôturé dans [`plans/004-V0_1_2_LOGGING_FOUNDATION_PLAN.md`](plans/004-V0_1_2_LOGGING_FOUNDATION_PLAN.md). Le plan détaillé de la release stable `0.1.3 — Configuration foundation` est conservé comme historique clôturé dans [`plans/005-V0_1_3_CONFIG_FOUNDATION_PLAN.md`](plans/005-V0_1_3_CONFIG_FOUNDATION_PLAN.md). Le plan détaillé de la release stable `0.1.4 — ksp-app-config-desk` est conservé comme historique clôturé dans [`plans/006-V0_1_4_CONFIG_DESKTOP_PLAN.md`](plans/006-V0_1_4_CONFIG_DESKTOP_PLAN.md), avec sa matrice finale [`validation/001-V0_1_4_CONFIG_DESKTOP.md`](validation/001-V0_1_4_CONFIG_DESKTOP.md). Son prompt d'ouverture historique reste [`../prompts/004-V0_1_4_START_PROMPT.md`](../prompts/004-V0_1_4_START_PROMPT.md). La release stable `0.2.0` clôt l'audit de bot3 et le découpage de la série. Son plan directeur est conservé comme historique clôturé dans [`plans/007-V0_2_0_SERIES_PLANNING.md`](plans/007-V0_2_0_SERIES_PLANNING.md), avec sa matrice finale [`validation/002-V0_2_0_SERIES_PLANNING.md`](validation/002-V0_2_0_SERIES_PLANNING.md). La release stable `0.2.1 — HTTP Solana foundation` a été ouverte par [`../prompts/006-V0_2_1_START_PROMPT.md`](../prompts/006-V0_2_1_START_PROMPT.md). Son gate de sizing et sa matrice exhaustive sont conservés dans [`plans/008-V0_2_1_ONCHAIN_HTTP_PLAN.md`](plans/008-V0_2_1_ONCHAIN_HTTP_PLAN.md), avec la validation finale [`validation/003-V0_2_1_ONCHAIN_HTTP.md`](validation/003-V0_2_1_ONCHAIN_HTTP.md), README/USAGE Transport et le smoke Devnet opt-in de composition Config -> Transport. Le prompt [`../prompts/007-V0_2_2_START_PROMPT.md`](../prompts/007-V0_2_2_START_PROMPT.md) a ouvert la release stable `0.2.2 — HTTP Accounts + Tokens + Cluster`. Son plan clôturé [`plans/009-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER_PLAN.md`](plans/009-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER_PLAN.md) conserve l'audit et l'implémentation des 22 wrappers typés, tandis que [`validation/004-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER.md`](validation/004-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER.md) enregistre les validations déterministes, les graphes Cargo et les deux smokes Devnet passés avant publication. Le prompt [`../prompts/008-V0_2_3_START_PROMPT.md`](../prompts/008-V0_2_3_START_PROMPT.md) a ouvert la release stable `0.2.3 — HTTP Transactions`. Son plan clôturé [`plans/010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md`](plans/010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md) conserve l'audit et l'implémentation des 11 wrappers ; le réaudit [`validation/005-V0_2_3_KSP_TRANSPORT_007_RETRO_AUDIT.md`](validation/005-V0_2_3_KSP_TRANSPORT_007_RETRO_AUDIT.md) confirme la complétude des 37 wrappers HTTP typés et [`validation/006-V0_2_3_HTTP_TRANSACTIONS.md`](validation/006-V0_2_3_HTTP_TRANSACTIONS.md) enregistre les validations finales, graphes Cargo et deux smokes Devnet passés avant publication. Le prompt [`../prompts/009-V0_2_4_START_PROMPT.md`](../prompts/009-V0_2_4_START_PROMPT.md) a ouvert la release stable `0.2.4 — HTTP Blocks + Economics + compliance HTTP finale`. Son plan clôturé [`plans/011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md`](plans/011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md) conserve l’implémentation des 15 wrappers et la compliance `52/52 + 14/14`; la matrice finale [`validation/007-V0_2_4_HTTP_FINAL_COMPLIANCE.md`](validation/007-V0_2_4_HTTP_FINAL_COMPLIANCE.md) enregistre le réaudit SIMD/inventaire, les canaries globales et les preuves opérateur avant publication. Le prompt [`../prompts/010-V0_2_5_START_PROMPT.md`](../prompts/010-V0_2_5_START_PROMPT.md), finalisé par `0.2.4-pre.009-fix.001`, ouvre `0.2.5 — Wallet foundation` sur la base stable `v0.2.4`. Son plan historique clôturé [`plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md`](plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md) part du gate `pre.001` (héritage, threat model offline, VIEW/OWNER indépendants et niveau B read-only), puis matérialise la crate en `pre.002`, le wire/transcript en `pre.003`, les primitives Argon2id/XChaCha20-Poly1305 en `pre.004`, les payloads/create/open en `pre.005`, la persistence en `pre.006`, l'administration/signature en `pre.007` et les adapters transfer en `pre.008`. `pre.009` ferme l'audit adversarial/interoperability/compliance dans [`validation/008-V0_2_5_WALLET_SECURITY_COMPLIANCE.md`](validation/008-V0_2_5_WALLET_SECURITY_COMPLIANCE.md) avant la documentation finale `pre.010` ; `pre.010` finalise [`../crates/ksp-wallet-lib/README.md`](../crates/ksp-wallet-lib/README.md), [`../crates/ksp-wallet-lib/USAGE.md`](../crates/ksp-wallet-lib/USAGE.md), la spec, les graphes et la matrice ; `pre.010-fix.001`–`fix.003` ferment ensuite la mise à niveau Dalek et la normalisation Rust/audit structurel. `0.2.5-rel.001` publie la release stable et [`../prompts/011-V0_2_6_START_PROMPT.md`](../prompts/011-V0_2_6_START_PROMPT.md) ouvre `0.2.6 — Wallet Desk`. Le gate `0.2.6-pre.001` est conservé dans [`plans/013-V0_2_6_WALLET_DESK_PLAN.md`](plans/013-V0_2_6_WALLET_DESK_PLAN.md) : il réaudite Config Desk et les APIs finales, retient le gabarit desktop, fixe `std.wallet`, la composition Config/Wallet/HTTP/Logging, les secrets `KSP_SECRET_WALLET_PASS_*`, les frontières VIEW/OWNER et la trajectoire de validation. `pre.002`–`pre.014` matérialisent ensuite le shell Tauri, Config Wallet/composite, inventory, create/open, balance HTTP, import/export, metadata, rotations, révocation VIEW forte, compliance et polish desktop. `pre.015` fige le wire binaire `.kspwallet` V2, `pre.016` matérialise les APIs génériques/versionnées et le runtime V2, puis `pre.017` ajoute la migration explicite OWNER-authentifiée V1 -> V2. `pre.018` ferme le runtime Tauri packagé Config/resources et la documentation candidate ; `pre.018-fix.001` corrige le canari d'ownership Config, après quoi le gate workspace et le build final Linux sont verts. `pre.018-fix.002` renforce uniquement le contrat de reprise `0.2.7`. `0.2.6-rel.001` publie cette surface stable et [`../prompts/012-V0_2_7_START_PROMPT.md`](../prompts/012-V0_2_7_START_PROMPT.md) devient le prochain point d'entrée. Le gate `0.2.7-pre.001` ouvre désormais la release WebSocket standard dans [`plans/014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md`](plans/014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md) ; la matrice normative initiale est conservée dans [`validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md`](validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md). ## Spécifications de formats diff --git a/docs/plans/000-README.md b/docs/plans/000-README.md index 962483b..7e8374c 100644 --- a/docs/plans/000-README.md +++ b/docs/plans/000-README.md @@ -1,5 +1,5 @@ - + # Plans KSP @@ -22,6 +22,7 @@ Un plan décrit le périmètre, les décisions déjà acquises, les questions ou - [`011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md`](011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md) — plan historique clôturé de la release stable `0.2.4`, ouvert par `pre.001`, exécuté jusqu’à `pre.009`, complété par le fix documentaire Wallet `pre.009-fix.001` puis publié par `rel.001`; il couvre les 10 Blocks + 5 Economics et la compliance finale `52/52 + 14/14` sous `KSP-TRANSPORT-007`. - [`012-V0_2_5_WALLET_FOUNDATION_PLAN.md`](012-V0_2_5_WALLET_FOUNDATION_PLAN.md) — plan historique clôturé de la release stable `0.2.5 — Wallet foundation`, ouvert par `pre.001`, livré jusqu’à `pre.010`, renforcé par `pre.010-fix.001`–`fix.003` pour Dalek 3 et la normalisation Rust/audit structurel, puis publié par `rel.001`; il couvre `.kspwallet` V1, VIEW/OWNER, crypto, persistence, administration, transfer et compliance. - [`013-V0_2_6_WALLET_DESK_PLAN.md`](013-V0_2_6_WALLET_DESK_PLAN.md) — plan historique clôturé de la release stable `0.2.6 — Wallet Desk`, ouvert par `pre.001`, étendu en `pre.015`–`pre.017` au wire binaire `.kspwallet` V2, aux APIs multi-version et à la migration V1 -> V2, puis fermé par `pre.018`/`fix.001` avec le runtime Tauri packagé et le build final vert avant publication `rel.001`. +- [`014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md`](014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md) — plan actif de `0.2.7 — WebSocket Solana standard`, ouvert par `pre.001`; il conserve l'inventaire officiel 18 méthodes, le modèle session/subscription, le threat model, le choix de dependencies et le forecast recalibré. Le `pre.001` de chaque release fonctionnelle peut introduire son propre plan détaillé lorsque la release s'ouvre. diff --git a/docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md b/docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md index 6779598..57abad2 100644 --- a/docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md +++ b/docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md @@ -1,5 +1,5 @@ - + # Séquence des releases fonctionnelles KSP @@ -455,9 +455,13 @@ La tranche historique `pre.014` a traité les défauts visuels/templating observ ### `0.2.7` — WebSocket Solana standard -Mission : couvrir la surface WebSocket standard officielle ciblée. +Mission : couvrir exhaustivement la surface WebSocket Solana standard officielle ciblée, avec sessions physiques explicites, subscriptions typées, lifecycle borné, reconnexion/resubscribe déterministes et observabilité sûre. Le plan actif est [`014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md`](014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md) et la matrice de compliance initiale est [`../validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md`](../validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md). -Une URL peut avoir plusieurs sessions physiques ; une session peut avoir plusieurs subscriptions. Un pool automatique de sessions est reporté jusqu'à besoin concret. +Le gate `0.2.7-pre.001`, audité le 22 août 2026, inventorie exactement 18 opérations WebSocket documentées : 9 subscribe + 9 unsubscribe. `blockSubscribe`, `slotsUpdatesSubscribe` et `voteSubscribe` sont actuellement marquées unstable ; aucune méthode de l'index officiel courant n'est marquée Deprecated. + +Une URL peut avoir plusieurs sessions physiques explicites ; une session peut avoir plusieurs subscriptions. Un pool/scheduler automatique de sessions reste reporté jusqu'à besoin concret. Les IDs de session/subscription KSP sont locaux et stables ; les IDs serveur restent internes et peuvent être remappés après reconnexion. + +Le forecast initial allant jusqu'à `pre.008` est décompressé par `pre.001` jusqu'à environ `pre.014` afin de conserver des tranches intermédiaires nominales de 15–20 minutes ; ce numéro reste un forecast et non une contrainte de clôture. ### `0.2.8` — Helius LaserStream WebSocket diff --git a/docs/plans/014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md b/docs/plans/014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md new file mode 100644 index 0000000..7f2fe33 --- /dev/null +++ b/docs/plans/014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md @@ -0,0 +1,791 @@ + + + +# Plan `0.2.7` — WebSocket Solana standard + +> **Statut : actif, gate `0.2.7-pre.001`.** Cette première tranche fixe l'inventaire normatif, les frontières, le threat model, le modèle session/subscription, la stratégie de dépendances et le sizing. Elle ne matérialise pas encore le client WebSocket runtime. + +## 1. Objet et base vérifiée + +`0.2.7` étend `ksp-onchain-transport-lib` avec le transport WebSocket Solana standard sans créer un second composant on-chain et sans déplacer de logique Config, Store, Program, worker ou métier dans Transport. + +Base opérateur auditée : + +```text +archive Gitea fournie : khadhroony-solana-project-v0.2.6-from-gitea.zip +workspace.package.version avant ouverture = 0.2.6 +delta stable présent = deltas/0.2.6/rel.001.md +prochaine release annoncée = 0.2.7 — WebSocket Solana standard +``` + +La clôture `0.2.6` confirme l'héritage : Wallet Desk, `.kspwallet` V1 historique + V2 binaire par défaut, APIs Wallet multi-version, migration V1 -> V2 explicite OWNER-authentifiée, runtime packagé commun aux Desks et transport HTTP Solana stable. + +`0.2.7-pre.001` ouvre techniquement : + +```text +workspace.package.version = 0.2.7-pre.1 +livraison = 0.2.7-pre.001 +commit = v0.2.7-pre.001 +``` + +Aucune dependency WebSocket n'est ajoutée dans ce gate. + +## 2. Sources internes relues et hiérarchie appliquée + +L'audit a relu, dans l'ordre demandé par le prompt d'ouverture : + +```text +RULES.md +docs/000-README.md +docs/rules/RULES_GENERAL.md +docs/rules/RULES_KSP.md +docs/rules/RULES_RUST.md +docs/rules/RULES_DEPENDENCIES.md +docs/rules/PROMPT_STRUCTURE.md +docs/rules/VERSION_WORKFLOW.md +docs/rules/FILE_CONTRACTS.md + +docs/architecture/002-LAYERS_AND_DEPENDENCIES.md +docs/architecture/003-COMPONENT_CONTRACTS.md +docs/architecture/004-COMPONENT_INVENTORY.md +docs/architecture/005-DEPENDENCY_GRAPH.md +docs/architecture/009-ACQUISITION_WORKERS_AND_JOBS.md +docs/architecture/010-APPS_SERVICES_SCENARIOS_AND_CONTROL.md + +docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md +docs/plans/007-V0_2_0_SERIES_PLANNING.md +docs/plans/008-V0_2_1_ONCHAIN_HTTP_PLAN.md +docs/validation/003-V0_2_1_ONCHAIN_HTTP.md +docs/validation/005-V0_2_3_KSP_TRANSPORT_007_RETRO_AUDIT.md +docs/validation/007-V0_2_4_HTTP_FINAL_COMPLIANCE.md + +crates/ksp-onchain-transport-lib/** +crates/ksp-config-lib/src/transport.rs +config/std.transport.json +config/schemas/std.transport.schema.json +contrats consommés de ksp-core-lib et ksp-logging-lib + +deltas/0.2.6/rel.001.md +docs/plans/013-V0_2_6_WALLET_DESK_PLAN.md +docs/validation/009-V0_2_6_WALLET_DESK_COMPLIANCE.md +``` + +`docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md` reste l'autorité de numérotation courante. `007-V0_2_0_SERIES_PLANNING.md` est historique. + +## 3. Baseline de démarrage + +Le sandbox d'analyse ne fournit pas le binaire `cargo`. + +Les commandes imposées ont été tentées avant modification : + +```text +cargo fmt --all IMPOSSIBLE : cargo absent, code 127 +python3 scripts/audit_rust_workspace_rules.py OK +cargo check --workspace IMPOSSIBLE : cargo absent, code 127 +cargo clippy --workspace --all-targets IMPOSSIBLE : cargo absent, code 127 +``` + +L'audit Python retourne : + +```text +General Rust rule audit: clean +Rust export completeness audit: 0 candidate(s) +KSP workspace Rust rule audit: clean +``` + +Aucun gate Cargo n'est déclaré réussi. Une validation opérateur est obligatoire avant commit de `pre.001`. + +## 4. État réel de Transport et Config hérité + +### 4.1 Surface HTTP stable à préserver + +`ksp-onchain-transport-lib` contient actuellement : + +- settings HTTP publics (`HttpEndpointUrl`, provider/cluster/role, retry, limits, endpoint, transport) ; +- `HttpEndpointClient` et `HttpTransportPool` ; +- 52 wrappers HTTP courants typés et 14 méthodes historiques Deprecated tracées ; +- JSON-RPC, retry/no-resend, snapshots et observabilité ; +- DTOs wire communs réutilisables, notamment `SolanaCommitment`, `SolanaRpcContext`, `SolanaRpcResponse`, encodings/account DTOs, filters et `SolanaWireField` ; +- émissions runtime via `ksp-logging-lib`, sans `tracing` direct. + +La surface WebSocket doit être additive : aucune renormalisation invasive des types `Http*` n'est requise dans `0.2.7`. + +### 4.2 Config actuel est explicitement HTTP V1 + +`config/std.transport.json`, son schema et `ksp-config-lib/src/transport.rs` décrivent un document V1 HTTP : + +```text +format_version = 1 +schema title = standard HTTP Transport +additionalProperties = false +serde(deny_unknown_fields) +format_version != 1 rejeté +``` + +Décision : **ne pas ajouter silencieusement des clés WebSocket au V1**. + +`0.2.7` fera évoluer le même composant standard `std.transport` vers un **format V2 explicitement HTTP + WebSocket**, tout en conservant la lecture du V1 HTTP-only pour compatibilité. Config reste propriétaire du parsing/résolution et adapte ensuite vers les settings publics Transport. + +Shape cible minimale : + +```text +format_version = 2 +retry # HTTP existant, conservé +ws_defaults # defaults de session WS KSP, non provider-specific +default_profile +profiles[] + profile_id + endpoints[] # HTTP existant, conservé + ws_endpoints[] # endpoints WebSocket explicites +``` + +`ws_endpoints[]` porte seulement les données nécessaires à Transport : nom logique, enabled, provider, cluster, URL et overrides de session éventuellement nécessaires. Les credentials restent résolus par Config comme aujourd'hui ; Transport reçoit un endpoint déjà résolu et doit redacter l'URL dans `Debug`/logs/errors. + +Le V1 chargé sous `0.2.7` produit une configuration HTTP valide et une collection WebSocket vide. Le V2 devient le format livré par les fixtures Config dès la tranche qui matérialise le WebSocket. + +## 5. Audit historique bot3 + +Archive auditée uniquement comme référence : + +```text +khadhroony-bot3_v0.5.3-pre.005-fix010.zip +ks-onchain-transport/src/standard_ws.rs +ks-onchain-transport/src/ws_client.rs +ks-onchain-transport/src/ws_pool.rs +ks-onchain-transport/src/ws_session.rs +consumers/demo et rapports de validation WebSocket +``` + +Invariants utiles repris : + +- plusieurs subscriptions sur une session physique ; +- plusieurs sessions possibles ; +- identifiant local stable distinct de l'identifiant serveur ; +- remapping des IDs serveur après reconnect ; +- reconnect borné et resubscribe ; +- canaris locaux de lifecycle. + +Choix bot3 explicitement **non repris** : + +- `Transport -> Config` ; +- `tracing` direct ; +- rôles HTTP et `max_subscriptions` mélangés au modèle endpoint générique ; +- pool/scheduler de sessions sans besoin démontré ; +- API d'unsubscribe publique fondée sur l'ID serveur éphémère ; +- broadcast unique comme canal principal de notifications data ; +- états lifecycle trop grossiers pour les races reconnect/unsubscribe. + +Le retour historique bot3 sur les flux à fort débit renforce le besoin d'une policy de backpressure explicite et observable ; aucune hypothèse de livraison lossless ne doit reposer sur un canal broadcast qui peut lagger silencieusement. + +## 6. Inventaire WebSocket Solana officiel au 2026-08-22 + +Source d'index : + +```text +https://solana.com/docs/rpc/websocket +``` + +L'index courant contient exactement **18 méthodes WebSocket : 9 subscribe + 9 unsubscribe**. + +```text +accountSubscribe / accountUnsubscribe +blockSubscribe / blockUnsubscribe +logsSubscribe / logsUnsubscribe +programSubscribe / programUnsubscribe +rootSubscribe / rootUnsubscribe +signatureSubscribe / signatureUnsubscribe +slotSubscribe / slotUnsubscribe +slotsUpdatesSubscribe / slotsUpdatesUnsubscribe +voteSubscribe / voteUnsubscribe +``` + +Statut observé : + +```text +6 paires sans marque unstable/deprecated dans la documentation actuelle +3 paires dont le subscribe est explicitement Unstable : + blockSubscribe + slotsUpdatesSubscribe + voteSubscribe +0 méthode WebSocket de cet index marquée Deprecated +``` + +Pour KSP, le label interne `Stable` signifie ici « documentée et non marquée unstable/deprecated par la source officielle auditée », pas une garantie de standardisation extérieure supplémentaire. + +Règles générales documentées : JSON-RPC 2.0 sur connexion persistante ; résultat subscribe numérique ; notifications avec `params.subscription` numérique ; lorsqu'un contexte est présent `context.slot` est fourni et `context.apiVersion` est omis ; les subscriptions qui acceptent `commitment` utilisent `finalized` par défaut. + +La matrice détaillée initiale est conservée dans `docs/validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md`. + +## 7. Points wire sensibles sous `KSP-TRANSPORT-007` + +### `accountSubscribe` + +Conserver : pubkey ; `commitment` ; encodings `base58|base64|base64+zstd|binary|jsonParsed` ; `dataSlice` ; notification `accountNotification` contextualisée. + +Le type partagé Agave `RpcAccountInfoConfig` contient aussi `min_context_slot`, mais le handler PubSub `v3.1.8` l'ignore explicitement. KSP ne doit donc pas promettre `minContextSlot` comme option WebSocket effective tant que l'upstream ne lui donne pas une sémantique réelle ; cette non-promise doit rester visible dans la compliance pour éviter qu'une option ignorée soit comptée comme supportée. + +### `programSubscribe` + +Conserver : program pubkey ; `commitment` ; `filters` ; mêmes encodings account ; `dataSlice` ; **`withContext` default false**. + +La documentation actuelle expose `withContext`, mais le code Agave `v3.1.8` lié par la page stocke bien `with_context` alors que le chemin générique de notification audité sérialise un `RpcResponse` contexté. KSP ne doit ni ignorer le paramètre, ni convertir cette incohérence amont en hypothèse stricte. Décision : exposer le flag et rendre le décodeur capable d'accepter la forme documentée non-contextée comme la forme contextée observée. + +### `logsSubscribe` + +Conserver les trois filtres : `all`, `allWithVotes`, `{mentions:[pubkey]}`. La forme `mentions` autorise exactement une adresse dans la documentation actuelle. Conserver `commitment`; notification `signature + err + logs` contextualisée. + +### `signatureSubscribe` + +Conserver `commitment` et `enableReceivedNotification`. Le résultat notification est une union wire : + +```text +"receivedSignature" +OU +{ "err": null | transaction error } +``` + +La subscription se termine après la notification terminale ; elle ne doit donc pas être resubscribed après que le runtime KSP a observé cette terminaison. + +### `slotSubscribe` / `rootSubscribe` + +Aucun paramètre. `slotNotification` conserve `slot`, `parent`, `root`; `rootNotification` conserve le root `u64`. + +### `blockSubscribe` — Unstable + +Conserver : filtre `all` ou `{mentionsAccountOrProgram:}` ; commitment limité à `confirmed|finalized` ; encoding `binary|base58|base64|json|jsonParsed` ; `transactionDetails` `full|accounts|signatures|none` ; `maxSupportedTransactionVersion` ; `showRewards`. La méthode nécessite côté validator les flags documentés ; KSP ne les simule pas. + +La notification conserve `context`, `slot`, `block` nullable et `err` nullable, ainsi que les variantes de block liées à la config. + +### `slotsUpdatesSubscribe` — Unstable + +Conserver les variantes taggées actuelles et leurs champs : + +```text +firstShredReceived +completed +createdBank +frozen +dead +optimisticConfirmation +root +``` + +Le runtime doit prévoir une forme `Unknown`/raw bornée pour qu'une variante ajoutée par une version upstream ne ferme pas la session entière avant réaudit KSP. + +### `voteSubscribe` — Unstable + +Aucun paramètre. La méthode nécessite le flag validator documenté et observe des votes gossip pre-consensus. Conserver `votePubkey`, `slots`, `hash`, `timestamp`, `signature`. Agave `v3.1.8` définit `timestamp` comme `Option` : le décodeur KSP doit donc préserver cette optionalité et accepter de manière tolérante les formes wire `omitted/null/value`, sans inventer une valeur. + +### `*Unsubscribe` + +Chaque unsubscribe reçoit l'ID serveur et retourne `true` quand la subscription est supprimée ; le handler Agave audité retourne `InvalidParams` pour un ID inconnu. Cette réalité wire ne devient pas l'API publique KSP : le caller utilise l'ID local/handle stable et la session traduit vers l'ID serveur courant. + +Le cross-check Agave `v3.1.8` confirme également que `logsSubscribe` impose exactement une adresse pour `mentions`, que `signatureSubscribe` ne consomme que `commitment` et `enableReceivedNotification`, et que `blockSubscribe` applique bien le jeu d'options documenté avec un commitment au moins `confirmed`. Ces constats renforcent la matrice normative sans ajouter d'options non documentées à KSP. + +## 8. Dépendances WebSocket candidates + +Audit au 2026-08-22 : + +| Candidate | Version auditée | Verdict | Motif | +|---------------------|----------------:|---------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| +| `tokio-tungstenite` | `0.30.0` | **retenue** | mature, Tokio-native, TLS rustls, continuité avec bot3 mais réauditée, contrôle de `WebSocketConfig`, client + serveur local de test | +| `futures-util` | `0.3.34` | **retenue comme utilitaire** | `StreamExt`/`SinkExt`; features minimales `std,sink` | +| `tokio-websockets` | `0.13.3` | alternative viable, non retenue | strict/minimal et performant, mais exige davantage d'assemblage/features et n'apporte pas de besoin fonctionnel supérieur démontré pour cette foundation | +| `fastwebsockets` | `0.10.0` | non retenue | plus bas niveau ; peut déléguer davantage de compliance au caller, inutile pour la première foundation KSP | + +Landing prévu, **pas dans `pre.001`** : + +```toml +# root [workspace.dependencies] +tokio-tungstenite = { version = "^0.30", default-features = false } +futures-util = { version = "^0.3", default-features = false } + +# ksp-onchain-transport-lib +tokio-tungstenite = { workspace = true, features = ["connect", "rustls-tls-webpki-roots"] } +futures-util = { workspace = true, features = ["std", "sink"] } +tokio = { workspace = true, features = ["macros", "rt", "sync", "time"] } +``` + +Le serveur de test local pourra activer `tokio/net` en dev si KSP utilise directement `TcpListener`. + +`url` n'est pas retenu a priori : l'endpoint KSP peut être validé et passé comme chaîne/request sans ajouter la feature uniquement par habitude bot3. `handshake`/`stream` sont déjà requis transitivement par `connect`/TLS et ne doivent pas être listés sans nécessité directe. + +Les defaults de `tungstenite::WebSocketConfig` ne sont **pas** des limites Solana normatives. KSP configurera explicitement des plafonds finis pour messages, frames et write buffer. Les valeurs numériques KSP seront figées avec les tests adversariaux de la tranche settings/session, et documentées comme policy locale, jamais comme limite protocolaire Solana. + +Sources : + +```text +https://docs.rs/crate/tokio-tungstenite/0.30.0 +https://docs.rs/crate/tokio-tungstenite/0.30.0/features +https://docs.rs/crate/futures-util/0.3.34 +https://docs.rs/crate/tokio-websockets/0.13.3 +https://docs.rs/crate/fastwebsockets/0.10.0 +``` + +## 9. Modèle public Transport retenu + +### 9.1 Settings + +Noms cibles : + +```text +WsEndpointUrl +WsProviderName +WsClusterName +WsReconnectSettings +WsResubscribePolicy +WsSessionSettings +WsEndpointSettings +WsTransportSettings +``` + +Les wrappers `WsProviderName`/`WsClusterName` sont parallèles aux `Http*` pour éviter une migration publique HTTP dans cette release. Une généralisation future n'est justifiée que si un troisième backend démontre un type commun réellement utile. + +`WsEndpointUrl` : + +- accepte seulement `ws://`/`wss://` après validation ; +- fournit un accès explicite à la valeur au code de connexion ; +- `Debug` et tout diagnostic par défaut sont redacted ; +- aucun snapshot public ne contient l'URL complète. + +`WsSessionSettings` porte les bornes runtime nécessaires : command timeout, close timeout, reconnect, resubscribe, capacités de queues, maximum de subscriptions actives, maximum de requests JSON-RPC en vol, maximum de message/frame/write buffer. Pas de rôle HTTP ni de scheduler. + +### 9.2 Cardinalité + +Contrat : + +```text +1 WsEndpointSettings + -> 0..N WsSession physiques créées explicitement par caller + -> 0..N WsSubscription actives +``` + +Deux appels de création sur le même endpoint ouvrent deux connexions physiques. Transport ne distribue pas automatiquement les subscriptions entre elles. + +### 9.3 Session actor + +Une tâche actor possède exclusivement : + +- le stream WebSocket physique ; +- le compteur d'IDs JSON-RPC requests ; +- la map des requests en vol ; +- le registre local des subscriptions ; +- le mapping `remote_subscription_id -> local_subscription_id` ; +- le lifecycle reconnect/resubscribe ; +- les compteurs/snapshot sûrs. + +Le handle public `WsSession` communique avec cet actor par canal bounded. Aucun caller ne split/manipule directement le socket. + +### 9.4 Identités + +```text +WsSessionId # KSP local, stable pour la vie du handle +WsSubscriptionId # KSP local, stable malgré reconnect +remote id u64 # strictement runtime/interne, remappable +``` + +Une `WsSubscription` typée contient l'identité locale, la projection de lifecycle et un receiver bounded de notifications typées. L'ID remote ne devient pas un identifiant métier/public de contrôle. + +## 10. State machines retenues + +### 10.1 Session + +```text +Disconnected + -> Connecting + -> Active + -> Failed + +Active + -> Reconnecting + -> Active + -> Failed + -> Closing + -> Closed + +Reconnecting + -> Closing + -> Closed +``` + +État public cible : + +```text +Disconnected | Connecting | Active | Reconnecting { attempt } | Closing | Closed | Failed +``` + +### 10.2 Subscription + +```text +Requested + -> Active + -> Failed + +Active + -> Resubscribing + -> Active + -> Failed + -> Cancelling + -> Closed + -> Closed # terminaison serveur connue, ex. signature terminale + +Requested/Resubscribing + -> Cancelling + -> Closed +``` + +État public cible : + +```text +Requested | Active | Resubscribing | Cancelling | Closed | Failed +``` + +Le motif terminal est séparé (`Unsubscribed`, `CompletedByServer`, `SessionClosed`, `BackpressureOverflow`, `ProtocolFailure`, etc.) afin de ne pas gonfler l'enum d'états. + +## 11. Reconnect et resubscribe + +### 11.1 Reconnect + +Reconnect uniquement pour perte du canal physique : EOF, close inattendue, erreur I/O/TLS/WebSocket read/write. Une erreur RPC applicative de subscribe/unsubscribe n'entraîne pas une reconnexion automatique de la session. + +Policy : + +- nombre d'essais fini et configurable ; +- backoff exponentiel borné par `initial_backoff`/`max_backoff` ; +- **pas de jitter dans `0.2.7`** afin d'éviter une dépendance/randomisation supplémentaire et garder les tests déterministes ; +- le budget se réinitialise après retour complet à `Active` ; +- exhaustion => session `Failed`, subscriptions terminales avec cause explicite ; +- shutdown/close annule immédiatement backoff et interdit toute nouvelle tentative. + +Les valeurs numériques par défaut sont une policy KSP et seront figées avec `WsSessionSettings` après tests ; elles ne seront pas attribuées à Solana. + +### 11.2 Resubscribe + +Enum public : + +```text +WsResubscribePolicy::Never +WsResubscribePolicy::ActiveSubscriptions +``` + +Default KSP proposé : `ActiveSubscriptions`. + +Après perte de connexion : + +1. tous les remote IDs deviennent invalides et sont retirés du mapping ; +2. la session émet/incrémente un signal de **continuity gap** ; +3. après reconnect, seules les subscriptions encore désirées actives sont restaurées ; +4. ordre déterministe : `WsSubscriptionId` croissant / ordre de création ; +5. chaque ack remappe un nouvel ID remote ; +6. une erreur de resubscribe marque cette subscription `Failed` mais ne ferme pas les autres si le socket reste sain. + +KSP **ne garantit pas la continuité lossless** entre la déconnexion et la restauration. Aucun backfill HTTP automatique n'est ajouté à Transport. Les consumers workers/jobs pourront réconcilier plus tard avec leur propre checkpoint/persistence. + +### 11.3 Race unsubscribe pendant reconnect + +La cancellation locale gagne toujours : + +- marquer la subscription non restaurable avant de traiter les acks tardifs ; +- retirer sa spec de la sélection resubscribe ; +- si un subscribe/resubscribe distant tardif réussit malgré tout, envoyer un best-effort unsubscribe de son nouvel ID ; +- ne jamais repasser la subscription locale à `Active` après cancellation. + +## 12. Backpressure et bornes de ressources + +### 12.1 Queues + +- canal commands session : bounded ; +- notifications : **queue bounded par subscription** ; +- lifecycle/snapshot : `watch`/état partagé compact, pas une file data infinie ; +- requests JSON-RPC en vol : map bornée + timeout ; +- subscriptions actives : plafond configurable par session. + +### 12.2 Overflow notifications + +Aucun drop silencieux. + +Si la queue d'une subscription est pleine : + +1. compteur overflow incrémenté ; +2. subscription passe `Failed(BackpressureOverflow)` ; +3. best-effort unsubscribe distant si possible ; +4. son receiver se termine avec la cause observable via état final ; +5. les autres subscriptions de la session restent actives. + +Cette policy isole un consumer lent sans sacrifier toute la connexion et sans prétendre être lossless. + +### 12.3 Frames/messages/JSON + +Configurer des plafonds finis pour : + +```text +max_message_size +max_frame_size +max_write_buffer_size +max_pending_requests +max_active_subscriptions +command_queue_capacity +notification_queue_capacity +``` + +La parse JSON ne reçoit donc jamais un message WebSocket arbitrairement grand. Les payloads raw conservés pour losslessness restent eux aussi sous cette borne. + +## 13. Cancellation, control frames et shutdown + +### 13.1 Control frames + +Le runtime gère proprement Ping/Pong/Close selon la crate WebSocket retenue. Aucun heartbeat applicatif périodique n'est activé par défaut dans `0.2.7` : la documentation Solana actuelle n'impose pas un ping KSP. Un keepalive configurable sera ajouté seulement si un besoin interop réel est démontré. + +### 13.2 Shutdown explicite + +`WsSession::close().await` doit : + +1. passer `Closing` et refuser de nouvelles subscriptions ; +2. annuler reconnect/backoff/resubscribe et requests en vol ; +3. si connecté, best-effort unsubscribe des actives dans un ordre déterministe sous un budget global ; +4. envoyer Close WebSocket ; +5. fermer toutes les subscriptions localement avec raison terminale ; +6. attendre la fin de l'actor sous `close_timeout` ; +7. finir `Closed` même si le peer ne répond pas à temps. + +Si la session est déjà reconnecting, elle ne se reconnecte jamais seulement pour envoyer des unsubscribe. + +`Drop` peut déclencher un signal best-effort, mais ne remplace pas l'API async explicite pour les garanties de lifecycle. + +## 14. Erreurs et anomalies de protocole + +Réutiliser lorsque possible les codes HTTP/JSON-RPC génériques existants : + +```text +invalid_settings +invalid_rpc_parameters +json_encode_failed +json_decode_failed +json_rpc_protocol_invalid +rpc_application_error +timeout +``` + +Codes WS cibles minimaux : + +```text +ws_connection_failed +ws_protocol_error +ws_session_closed +ws_subscription_failed +ws_backpressure_overflow +``` + +Policy d'anomalies : + +- JSON/message WebSocket structurellement invalide : erreur protocole session, fermeture/reconnect selon budget ; +- notification JSON valide mais payload typed invalide pour une subscription connue : fail de cette subscription, pas de teardown automatique des autres ; +- notification valide pour ID remote inconnu/stale : compteur + log safe, drop ; +- method notification incompatible avec la subscription connue : fail de cette subscription ; +- réponse JSON-RPC d'erreur à subscribe/unsubscribe : `rpc_application_error`, pas reconnect implicite. + +## 15. Observabilité et secrets + +Tous les événements KSP passent par `ksp-logging-lib`. + +Autorisés dans logs/snapshots : + +```text +endpoint logical name +provider label +cluster label +WsSessionId +WsSubscriptionId +subscription kind +state transitions +active/pending counts +reconnect attempt / exhaustion +continuity_gap_count +overflow_count +error code qualifié +``` + +Interdits : + +```text +URL WebSocket complète +query token/credential +Authorization/header sensible +raw notification arbitraire +payload massif +remote request body complet +``` + +Snapshot public cible : état session, endpoint name/provider/cluster, counts, reconnect state, continuity gaps, subscription IDs locaux + kinds + states. L'ID serveur peut rester interne ; un booléen `remote_bound` suffit pour diagnostiquer le binding sans faire croire à sa stabilité. + +Tests dédiés : URL `wss://user:pass@host/path?api-key=secret` ne doit apparaître ni dans `Debug`, ni erreurs, ni logs capturés, ni snapshots. + +## 16. DTOs et losslessness + +Réutiliser les DTOs HTTP lorsque leur sémantique wire est réellement identique : + +```text +SolanaCommitment +SolanaRpcContext +SolanaRpcResponse +SolanaAccountEncoding +SolanaDataSliceConfig +SolanaProgramAccountFilter +SolanaAccount +SolanaKeyedAccount +block/transaction DTOs compatibles +SolanaWireField +``` + +Définir des DTOs WS dédiés pour : enveloppes notification, lifecycle, subscription kind/config, logs, signature union, slot/root/slotsUpdates/vote, block update et toute forme où le wire diffère. + +`SolanaRpcContext.api_version` est déjà optionnel ; il peut donc représenter les contexts PubSub où la documentation indique que `apiVersion` est omis. + +Pour unstable/évolutif, préférer des enums avec fallback `Unknown { type_name, raw }` borné plutôt qu'un rejet session-wide d'un variant upstream nouveau. + +## 17. API générique provider-specific + +Le moteur interne doit encoder une spec générique `subscribe_method + unsubscribe_method + params + decoder`, afin que `0.2.8` puisse réutiliser la session. + +**Aucune API publique raw provider-extension n'est promise dans `0.2.7`.** Elle sera décidée après audit Helius de `0.2.8`. Cela évite de figer trop tôt une escape hatch qui deviendrait le contrat principal et contournerait les wrappers typés standards. + +## 18. Tests déterministes attendus + +Le test runtime utilise un serveur WebSocket local déterministe, sans provider externe : + +```text +handshake + close +subscribe -> notification -> unsubscribe +plusieurs subscriptions sur une session +deux sessions physiques sur la même URL +IDs locaux stables / IDs distants remappés +erreur RPC subscribe/unsubscribe +JSON/message malformé +notification method/payload incompatible +unknown/stale remote subscription id +message/frame oversized +connexion interrompue +reconnect borné + exhaustion + reset budget +resubscribe déterministe +unsubscribe pendant reconnect + ack stale +signature terminale => closed, jamais resubscribe +shutdown active/pending/reconnecting +backpressure : overflow d'une sub n'impacte pas les autres +limits active subscriptions/pending requests +program notification contexted + non-contexted +slotsUpdates unknown variant +vote timestamp omitted/null/value +URL/credentials absents de Debug/error/log/snapshot +warning centralisé pour unstable à la création, pas à chaque notification +``` + +Le serveur local est un fixture de Transport ; aucune application Tauri n'est modifiée pour tester WebSocket. + +## 19. Smoke live opt-in + +Après stabilisation : un test `#[ignore]` Transport pur sur Devnet, préférentiellement une subscription stable simple (`slotSubscribe`) : + +```text +connect +slotSubscribe +attendre une notification sous timeout +unsubscribe +close +``` + +Un endpoint override pourra être fourni par l'environnement Config seulement dans le smoke de composition séparé si celui-ci est réellement utile. Les unstable ne deviennent pas gates live car les validators publics peuvent les désactiver. + +Un rate-limit, refus provider ou indisponibilité externe ne constitue pas automatiquement une régression locale. + +## 20. Threat model synthétique + +| Risque | Défense retenue | Preuves attendues | +|--------------------------------------------------|----------------------------------------------------------------------|------------------------------| +| URL/token fuit dans diagnostics | type URL redacted + snapshots sans URL + messages d'erreur qualifiés | tests Debug/error/log | +| message/frame géant | limites WebSocket explicites avant JSON | tests oversized | +| JSON/allocation non bornée | taille message bornée + DTOs ciblés/raw borné | adversarial fixtures | +| consumer lent | queue par sub bounded, fail local explicite | overflow isolé | +| reconnect infini | budget fini + backoff borné | exhaustion test | +| notifications manquées pendant reconnect | continuity gap observable, aucune promesse lossless | lifecycle test/docs | +| resubscribe stale après unsubscribe | ID local desired-state, cancellation gagne | race test | +| remote ID réutilisé/remappé | mapping éphémère interne | reconnect/remap tests | +| subscription orpheline | registry actor + close explicite + cleanup terminal | shutdown tests | +| pending request leak | map bornée + timeout + purge reconnect/close | timeout tests | +| task leak | actor unique joinable + close timeout | repeated connect/close tests | +| shutdown bloqué | budget global + close timeout | hostile peer fixture | +| unstable upstream variant casse session | typed enum + Unknown/raw borné | unknown variant fixture | +| logique worker/persistence glisse dans Transport | aucun backfill/persistence/checkpoint | dependency/public API audit | + +## 21. Hors périmètre confirmé + +```text +Helius LaserStream / provider params -> 0.2.8 +Yellowstone gRPC -> 0.2.9 +provider Yellowstone commercial -> plus tard +shred/deshred/pre-execution -> plus tard +off-chain prices -> 0.2.10 +Wallet / 2FA / .kspwallet -> hors 0.2.7 +Store/persistence -> 0.3.x +Program decode/materialization -> plus tard +frontend réseau direct -> interdit +pool/scheduler automatique de sessions -> différé +backfill HTTP automatique sur gap WS -> workers/jobs futurs +public raw provider-extension API -> gate 0.2.8 +application heartbeat/ping périodique -> différé sans besoin démontré +``` + +## 22. Forecast recalibré + +L'inventaire officiel n'impose que 9 familles de subscriptions, mais le lifecycle concurrent est plus coûteux que le forecast initial. Le gate reste **positif sans split de release**, à condition de granulariser les tranches au lieu de compresser le moteur et les wrappers. + +```text +pre.001 audit interne/externe + matrice 18 méthodes + bot3 + dependencies + threat model + plan/sizing +pre.002 settings WS Transport + URL redaction + IDs/states/snapshots + tests de settings +pre.003 std.transport V2 HTTP+WS + backward V1 + schema/fixtures + Config -> WsTransportSettings +pre.004 deps tokio-tungstenite/futures-util + actor physique + handshake/read/write + pending JSON-RPC + serveur local +pre.005 limites frame/message/request + control frames + cancellation/close/shutdown + adversarial socket tests +pre.006 registry subscriptions + IDs locaux + generic subscribe/unsubscribe engine + channels typed bounded +pre.007 reconnect borné + resubscribe déterministe + continuity gap + races unsubscribe/reconnect +pre.008 backpressure per-sub + overflow/limits + leak/lifecycle adversarial tests +pre.009 wrappers stable lot A : account + program + logs, DTOs/options/KSP-TRANSPORT-007 +pre.010 wrappers stable lot B : signature + slot + root, terminaison signature/KSP-TRANSPORT-007 +pre.011 unstable : block + slotsUpdates + vote, warnings + fallbacks wire/KSP-TRANSPORT-007 +pre.012 compliance 18/18 + canaries public API + composition Config + régressions HTTP +pre.013 smoke live opt-in + README/USAGE + cargo tree/duplicates + dependency audit final +pre.014 validation workspace finale + docs/compliance + prompt 0.2.8 +rel.001 publication stable stricte +``` + +Chaque tranche reste scindable si son implémentation réelle dépasse le budget nominal de 15–20 minutes. Le nombre `pre.014` n'est pas une deadline normative. + +## 23. Gates de clôture `0.2.7` + +La release ne peut passer stable que si : + +- l'inventaire officiel est réaudité et reste rapproché par noms exacts ; +- 18/18 opérations standard de l'index ciblé ont un statut explicite ; +- 9/9 subscribe ont leur wrapper KSP promis et 9/9 unsubscribe sont couverts via handles/registry ; +- options et variantes wire sont conservées sous `KSP-TRANSPORT-007` ; +- la cardinalité endpoint -> N sessions -> N subscriptions est démontrée ; +- reconnect/resubscribe/backpressure/cancellation/shutdown sont bornés et testés ; +- continuity gaps sont observables sans promesse lossless ; +- secrets/URLs ne fuitent pas ; +- Config -> Transport reste l'unique direction d'adaptation ; +- aucune dépendance Store/Program/Wallet/Config/tracing direct n'apparaît dans Transport ; +- HTTP 52+14 ne régresse pas ; +- tests déterministes et workspace sont verts ; +- smoke live retenu reste opt-in ; +- README/USAGE, matrice finale, graphes Cargo et prompt `0.2.8` sont synchronisés. + +## 24. Validation opérateur requise pour `pre.001` + +Après application de ce gate documentaire/versionné : + +```bash +cargo fmt --all +python3 scripts/audit_rust_workspace_rules.py +cargo check --workspace +cargo clippy --workspace --all-targets +``` + +`cargo test --workspace` n'est pas imposé par cette tranche documentaire d'ouverture tant qu'aucun Rust runtime n'est ajouté, mais reste autorisé comme checkpoint opérateur. diff --git a/docs/validation/000-README.md b/docs/validation/000-README.md index d4a3e66..ca2359b 100644 --- a/docs/validation/000-README.md +++ b/docs/validation/000-README.md @@ -1,5 +1,5 @@ - + # Validations KSP @@ -18,3 +18,4 @@ Documents : - [`007-V0_2_4_HTTP_FINAL_COMPLIANCE.md`](007-V0_2_4_HTTP_FINAL_COMPLIANCE.md) — matrice finale validée de `0.2.4`, inventaire exact 52 current + 14 Deprecated, preuve typed 52/52, audit SIMD final, `KSP-TRANSPORT-007`, workspace complet et deux smokes Devnet passés avant publication stable. - [`008-V0_2_5_WALLET_SECURITY_COMPLIANCE.md`](008-V0_2_5_WALLET_SECURITY_COMPLIANCE.md) — matrice finale validée de la release stable `0.2.5`, threat model V1, canaris adversariaux, reproduction externe des vecteurs, audit de frontières, normalisation Rust/audit structurel, graphes Cargo et checkpoint final `pre.010-fix.003` vert. - [`009-V0_2_6_WALLET_DESK_COMPLIANCE.md`](009-V0_2_6_WALLET_DESK_COMPLIANCE.md) — matrice finale validée de la release stable `0.2.6`, couvrant Wallet Desk, les wires V1/V2, la migration explicite, le runtime Tauri packagé, les frontières sécurité/ownership et le gate opérateur `pre.018-fix.001` avec build final Linux vert. +- [`010-V0_2_7_ONCHAIN_WEBSOCKET.md`](010-V0_2_7_ONCHAIN_WEBSOCKET.md) — matrice active de `0.2.7`, ouverte par `pre.001` avec l'inventaire normatif 9 subscribe + 9 unsubscribe, les statuts unstable, le lifecycle, les risques et les preuves à fermer. diff --git a/docs/validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md b/docs/validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md new file mode 100644 index 0000000..6f685ce --- /dev/null +++ b/docs/validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md @@ -0,0 +1,260 @@ + + + +# Validation `0.2.7` — WebSocket Solana standard + +> **Statut : matrice initiale ouverte par `0.2.7-pre.001`.** Les colonnes de preuve seront consolidées au fil des prereleases puis fermées avant `0.2.7-rel.001`. + +## 1. Baseline normative + +Audit officiel effectué le **22 août 2026** contre : + +```text +https://solana.com/docs/rpc/websocket +``` + +Compte exact de l'index courant : + +```text +9 subscribe +9 unsubscribe +18 méthodes WebSocket totales +``` + +Répartition statut KSP : + +```text +12 méthodes appartenant à 6 paires documentées non marquées unstable/deprecated +6 méthodes appartenant à 3 paires unstable : block, slotsUpdates, vote +0 méthode de l'index courant marquée Deprecated +``` + +Pour une paire unstable, l'unsubscribe associé est classé `Unstable pair` dans KSP même si sa propre page n'affiche pas nécessairement le bandeau, car il n'existe que pour annuler la subscription unstable correspondante. + +## 2. Matrice exhaustive des 18 opérations + +| # | Méthode | Type | Statut `pre.001` | Paramètres / résultat essentiels | Notification / paire | Stratégie de test | Source officielle | Compliance | +|---:|---------------------------|-------------|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------|---------------------------------------------------------------|-----------------------------------------------------------------|-------------------| +| 1 | `accountSubscribe` | subscribe | Stable/documented | pubkey ; config `commitment`, `encoding`, `dataSlice` ; result numeric id ; `minContextSlot` upstream actuellement ignoré, donc non promis | `accountNotification` | fixture encodings/config + subscribe/notify | `https://solana.com/docs/rpc/websocket/accountsubscribe` | Planned `pre.009` | +| 2 | `accountUnsubscribe` | unsubscribe | Stable/documented | remote id ; `true` or RPC error unknown id | account pair | handle local -> remote id fixture | `https://solana.com/docs/rpc/websocket/accountunsubscribe` | Planned `pre.009` | +| 3 | `blockSubscribe` | subscribe | **Unstable** | `all`/mentions filter ; confirmed/finalized ; encoding ; tx details ; max tx version ; showRewards | `blockNotification` | all options + null block/error + validator capability fixture | `https://solana.com/docs/rpc/websocket/blocksubscribe` | Planned `pre.011` | +| 4 | `blockUnsubscribe` | unsubscribe | **Unstable pair** | remote id ; boolean/error | block pair | generic registry unsubscribe | `https://solana.com/docs/rpc/websocket/blockunsubscribe` | Planned `pre.011` | +| 5 | `logsSubscribe` | subscribe | Stable/documented | `all`, `allWithVotes`, exactly one `mentions`; commitment | `logsNotification` | 3 filters + invalid multi-mention + notification | `https://solana.com/docs/rpc/websocket/logssubscribe` | Planned `pre.009` | +| 6 | `logsUnsubscribe` | unsubscribe | Stable/documented | remote id ; boolean/error | logs pair | generic registry unsubscribe | `https://solana.com/docs/rpc/websocket/logsunsubscribe` | Planned `pre.009` | +| 7 | `programSubscribe` | subscribe | Stable/documented | program pubkey ; commitment ; filters ; encoding ; dataSlice ; `withContext` | `programNotification` | contexted/non-contexted fixtures + filters | `https://solana.com/docs/rpc/websocket/programsubscribe` | Planned `pre.009` | +| 8 | `programUnsubscribe` | unsubscribe | Stable/documented | remote id ; boolean/error | program pair | generic registry unsubscribe | `https://solana.com/docs/rpc/websocket/programunsubscribe` | Planned `pre.009` | +| 9 | `rootSubscribe` | subscribe | Stable/documented | no params ; numeric id | `rootNotification` => `u64` | exact root fixture | `https://solana.com/docs/rpc/websocket/rootsubscribe` | Planned `pre.010` | +| 10 | `rootUnsubscribe` | unsubscribe | Stable/documented | remote id ; boolean/error | root pair | generic registry unsubscribe | `https://solana.com/docs/rpc/websocket/rootunsubscribe` | Planned `pre.010` | +| 11 | `signatureSubscribe` | subscribe | Stable/documented | first transaction signature ; commitment ; `enableReceivedNotification` | `signatureNotification` early string or terminal error object | early + terminal + auto-close/no-resubscribe | `https://solana.com/docs/rpc/websocket/signaturesubscribe` | Planned `pre.010` | +| 12 | `signatureUnsubscribe` | unsubscribe | Stable/documented | remote id before terminal fire ; boolean/error | signature pair | cancel before terminal + stale after terminal | `https://solana.com/docs/rpc/websocket/signatureunsubscribe` | Planned `pre.010` | +| 13 | `slotSubscribe` | subscribe | Stable/documented | no params ; numeric id | `slotNotification` `{slot,parent,root}` | exact fixture + live smoke candidate | `https://solana.com/docs/rpc/websocket/slotsubscribe` | Planned `pre.010` | +| 14 | `slotUnsubscribe` | unsubscribe | Stable/documented | remote id ; boolean/error | slot pair | generic registry unsubscribe | `https://solana.com/docs/rpc/websocket/slotunsubscribe` | Planned `pre.010` | +| 15 | `slotsUpdatesSubscribe` | subscribe | **Unstable** | no params ; numeric id | tagged `slotsUpdatesNotification` | each known variant + unknown fallback | `https://solana.com/docs/rpc/websocket/slotsupdatessubscribe` | Planned `pre.011` | +| 16 | `slotsUpdatesUnsubscribe` | unsubscribe | **Unstable pair** | remote id ; boolean/error | slotsUpdates pair | generic registry unsubscribe | `https://solana.com/docs/rpc/websocket/slotsupdatesunsubscribe` | Planned `pre.011` | +| 17 | `voteSubscribe` | subscribe | **Unstable** | no params ; validator flag required | `voteNotification` | fields + timestamp omitted/null/value + warning | `https://solana.com/docs/rpc/websocket/votesubscribe` | Planned `pre.011` | +| 18 | `voteUnsubscribe` | unsubscribe | **Unstable pair** | remote id ; boolean/error | vote pair | generic registry unsubscribe | `https://solana.com/docs/rpc/websocket/voteunsubscribe` | Planned `pre.011` | + +## 3. Notification matrix + +| Subscribe | Notification | Shape à préserver | Point lossless / lifecycle | +|-------------------------|----------------------------|------------------------------------------------|--------------------------------------------------------------------------------------------------| +| `accountSubscribe` | `accountNotification` | contextual account payload | reuse account DTOs/encodings/dataSlice | +| `programSubscribe` | `programNotification` | keyed account, documenté avec contexte | accepter contexted/non-contexted à cause de l'écart docs/source audité ; préserver `withContext` | +| `logsSubscribe` | `logsNotification` | context + `{signature, err, logs}` | err nullable ; filtre mentions exactement une adresse | +| `signatureSubscribe` | `signatureNotification` | context + `"receivedSignature"` **ou** `{err}` | terminal object clôt la subscription ; early string ne la clôt pas | +| `slotSubscribe` | `slotNotification` | `{slot,parent,root}` | non-contextual | +| `rootSubscribe` | `rootNotification` | `u64` | non-contextual | +| `blockSubscribe` | `blockNotification` | context + `{slot, block, err}` | unstable ; `block`/`err` nullable ; variants block selon config | +| `slotsUpdatesSubscribe` | `slotsUpdatesNotification` | tagged union slot lifecycle | unstable ; fallback unknown/raw borné | +| `voteSubscribe` | `voteNotification` | `{votePubkey,slots,hash,timestamp,signature}` | unstable/pre-consensus ; timestamp tolerant wire | + +Les contexts WebSocket documentés omettent `apiVersion`. `SolanaRpcContext.api_version: Option` est compatible avec cette omission. + +## 4. Source Agave ciblée pour ambiguïtés + +Les pages officielles WebSocket du 2026-08-22 lient actuellement Agave `v3.1.8` pour les handlers PubSub : + +```text +https://github.com/anza-xyz/agave/blob/v3.1.8/rpc/src/rpc_pubsub.rs +https://github.com/anza-xyz/agave/blob/v3.1.8/rpc/src/rpc_subscriptions.rs +``` + +Constats ciblés : + +- `accountSubscribe` : le type partagé `RpcAccountInfoConfig` expose `min_context_slot`, mais le handler PubSub `v3.1.8` le destructure en `_ // ignored`. KSP ne le compte donc pas comme option WebSocket effective ; +- `programSubscribe` : `RpcProgramAccountsConfig.with_context` est lu dans `ProgramSubscriptionParams`, le trait exposé dans `rpc_pubsub.rs` utilise `Subscriber>`, le helper `check_commitment_and_notify` construit un `RpcResponse` avec contexte, et aucune consommation `with_context` n'a été retrouvée dans `rpc_subscriptions.rs` lors de l'audit ; +- `logsSubscribe` : le handler accepte `all`, `allWithVotes` ou `mentions` et rejette un filtre `mentions` contenant autre chose qu'exactement une adresse ; +- `signatureSubscribe` : le handler utilise la signature, `commitment` et `enable_received_notification`, sans option WebSocket supplémentaire ; +- `blockSubscribe` : le handler consomme le jeu d'options documenté et impose un commitment au moins `confirmed` ; +- `*Unsubscribe` : un ID serveur inconnu produit `InvalidParams` dans le handler audité ; +- `voteSubscribe` : `RpcVote.timestamp` est `Option`, ce qui justifie de préserver l'optionalité wire sans valeur inventée. + +Décisions compliance : transmettre `programSubscribe.withContext` malgré l'écart observé et accepter les formes contextée/non-contextée ; ne pas promettre `accountSubscribe.minContextSlot` tant qu'il est ignoré upstream ; conserver `vote.timestamp` comme champ optionnel/tolérant. + +## 5. Méthodes unstable + +### `blockSubscribe` + +Conditions officielles : + +```text +--rpc-pubsub-enable-block-subscription +--enable-rpc-transaction-history +``` + +KSP : warning centralisé à la création, test fixture toujours disponible, smoke live non requis. + +### `slotsUpdatesSubscribe` + +Le format est explicitement annoncé comme susceptible de changer. Variants actuels : + +```text +firstShredReceived slot,timestamp +completed slot,timestamp +createdBank slot,parent,timestamp +frozen slot,timestamp,stats +dead slot,timestamp,err +optimisticConfirmation slot,timestamp +root slot,timestamp +``` + +`stats` actuel : `numTransactionEntries`, `numSuccessfulTransactions`, `numFailedTransactions`, `maxTransactionsPerEntry`. + +### `voteSubscribe` + +Condition officielle : + +```text +--rpc-pubsub-enable-vote-subscription +``` + +Les votes observés sont gossip/pre-consensus ; aucune garantie d'entrée dans le ledger. Transport les livre comme wire, sans interprétation métier. + +## 6. Lifecycle compliance initiale + +| Contrat | Décision `pre.001` | Gate cible | +|---|---|---| +| plusieurs sessions / même URL | création physique explicite ; aucun singleton/pool automatique | `pre.004`, `pre.012` | +| plusieurs subs / session | registry actor par session | `pre.006` | +| ID public subscription | local KSP stable | `pre.006` | +| ID serveur | éphémère interne et remappé | `pre.006`/`pre.007` | +| session states | Disconnected/Connecting/Active/Reconnecting/Closing/Closed/Failed | `pre.002` | +| subscription states | Requested/Active/Resubscribing/Cancelling/Closed/Failed | `pre.002` | +| reconnect | physique uniquement, budget/backoff finis | `pre.007` | +| resubscribe | policy `Never|ActiveSubscriptions`, ordre local déterministe | `pre.007` | +| continuity | gap observable, aucune promesse lossless | `pre.007` | +| backpressure | queue par sub bounded ; overflow => fail local explicite | `pre.008` | +| shutdown | explicite, bounded, annule reconnect et subscriptions | `pre.005` | +| keepalive | pas de ping applicatif périodique sans besoin démontré | `pre.005` | + +## 7. Threat/security compliance initiale + +| Invariant | Preuve attendue | Statut | +|---------------------------------------------------|-----------------------------------|---------| +| URL/credentials absents de `Debug` | unit tests URL wrapper | Planned | +| URL/credentials absents des erreurs | adversarial connection errors | Planned | +| URL/credentials absents des logs | writer/capture KSP logging | Planned | +| snapshots sans URL/raw payload | public API canary | Planned | +| frame/message finis | oversized server fixture | Planned | +| JSON borné indirectement par message | oversized + malformed fixture | Planned | +| queues notifications bornées | slow consumer fixture | Planned | +| pending RPC borné + timeout | no-response fixture | Planned | +| reconnect loop bornée | repeated disconnect fixture | Planned | +| unsubscribe pendant reconnect ne resubscribe pas | race fixture | Planned | +| signature terminale ne resubscribe pas | terminal fixture | Planned | +| shutdown ne bloque pas | peer hostile/no close ack fixture | Planned | +| no Store/Program/Wallet/Config dep dans Transport | cargo tree + source canary | Planned | +| no direct `tracing` dans Transport | workspace audit/canary | Planned | + +## 8. Dependency compliance initiale + +Candidate retenue au gate : + +```text +tokio-tungstenite 0.30.0 +futures-util 0.3.34 +``` + +Features prévues : + +```text +tokio-tungstenite: default-features=false + connect + rustls-tls-webpki-roots +futures-util: default-features=false + std + sink +``` + +Sources : + +```text +https://docs.rs/crate/tokio-tungstenite/0.30.0 +https://docs.rs/crate/tokio-tungstenite/0.30.0/features +https://docs.rs/crate/futures-util/0.3.34 +``` + +Alternatives auditées mais non retenues : `tokio-websockets 0.13.3`, `fastwebsockets 0.10.0`. + +Aucune de ces dependencies n'est ajoutée par `pre.001`; le graphe Cargo stable ne change pas dans ce gate hors signal de version workspace. + +## 9. Config compliance initiale + +V1 actuel : HTTP-only strict. Décision : + +```text +V1 -> support de lecture conservé, WS vide +V2 -> HTTP existant + ws_defaults + profiles[].ws_endpoints +Config -> WsTransportSettings +Transport -X-> Config +``` + +La schema V1 n'est pas assouplie. Une schema V2 explicite remplace la fixture standard au moment où l'adapter est matérialisé. + +## 10. Validation du gate `pre.001` + +Exécuté dans le sandbox : + +```text +archive stable v0.2.6 vérifiée OK +lecture règles/architecture/plans/validation OK +inventaire Transport/Config réel OK +archive bot3 WebSocket auditée OK +audit docs officielles WebSocket OK +inventaire exact 18 = 9+9 OK +audit dependencies candidates OK +state machines / reconnect / resubscribe DECIDED +backpressure / cancellation / shutdown / secrets DECIDED +shape Config V2 minimal DECIDED +plan/sizing OK +python3 scripts/audit_rust_workspace_rules.py baseline OK +``` + +Tenté mais non exécutable dans le sandbox : + +```text +cargo fmt --all cargo absent +cargo check --workspace cargo absent +cargo clippy --workspace --all-targets cargo absent +``` + +La matrice ne considère donc pas `pre.001` techniquement validé par Cargo tant que l'opérateur n'a pas exécuté ces gates sur son checkout. + +## 11. Critères finaux à transformer en preuves + +Avant `rel.001`, cette matrice doit obtenir : + +```text +18/18 méthodes official-index accounted +9/9 subscribe wrappers public typed +9/9 unsubscribe couverts par handles/registry +0 option officielle perdue +0 fuite URL/credential +N sessions same URL prouvé +N subscriptions same session prouvé +reconnect/resubscribe/backpressure/shutdown gates verts +unstable warnings centralisés +HTTP 52+14 non régressé +Config V1 backward + V2 WS validés +smoke live opt-in documenté +cargo tree inspecté +cargo test --workspace vert +README/USAGE synchronisés +prompt 0.2.8 préparé +```