# `ksp-onchain-transport-lib` `ksp-onchain-transport-lib` est la bibliothèque KSP propriétaire du transport on-chain Solana. Sa première surface est le transport HTTP JSON-RPC ; les extensions WebSocket et gRPC sont introduites séparément lorsque leur release les cible. ## Responsabilités La crate possède : - les settings runtime HTTP publics ; - les endpoints nommés et leurs metadata provider/cluster ; - les rôles, capabilities/request kinds et priorités ; - la sélection/fairness/fallback du pool ; - les limites RPS/burst/concurrence et le cooldown ; - les deadlines et timeouts ; - le retry/backoff borné et la règle no-resend après dispatch ambigu ; - les enveloppes JSON-RPC 2.0 et leur validation ; - le registre audité des méthodes Solana HTTP ; - l'exécution générique des méthodes standard supportées ; - les wrappers typés explicitement livrés par KSP ; - les snapshots runtime sûrs ; - l'observabilité Transport via `ksp-logging-lib`. La crate ne possède ni documents Config, ni persistence Store, ni modèles Program/métier. ## Frontières de dépendances La direction autorisée est : ```text ksp-config-lib -> ksp-onchain-transport-lib -> ksp-core-lib -> ksp-logging-lib -> reqwest / tokio / serde -> tokio-tungstenite / futures-util ``` La direction inverse est interdite : ```text ksp-onchain-transport-lib -X-> ksp-config-lib ksp-onchain-transport-lib -X-> Store ksp-onchain-transport-lib -X-> Program ksp-onchain-transport-lib -X-> tracing direct ``` `ksp-config-lib` peut donc charger `std.transport.json` et construire `HttpTransportSettings`, tandis que Transport reste directement utilisable par un consumer qui fournit lui-même ses settings. ## Surface HTTP standard Le registre KSP conserve deux inventaires distincts : ```text 52 méthodes HTTP courantes 14 méthodes historiques Deprecated / runtime Removed ``` Le registre porte notamment : - catégorie ; - request kind ; - statut documentaire ; - statut runtime ; - forme de requête stable/legacy ; - type d'opération ; - classe de retry ; - remplacement historique éventuel ; - release de couverture typée KSP. La release stable `0.2.4` complète la surface typée des **52 méthodes courantes** : ```text 0.2.1 foundation : 4 0.2.2 Accounts/Tokens/Cluster : 22 0.2.3 Transactions : 11 0.2.4 Blocks/Economics : 15 total : 52 ``` `0.2.4` ajoute les dix wrappers Blocks et les cinq wrappers Economics. Les points sensibles restent notamment `getBlock` moderne + bare encoding legacy deprecated, les quatre `transactionDetails`, les versions transaction numériques génériques, `numRewardPartitions`, `commissionBps`, les overloads de `getBlocks`, les ranges de production, les valeurs d'inflation/minimum de délégation fournies par le runtime et les `null` positionnels de `getInflationReward`. `KSP-TRANSPORT-007` impose qu'un wrapper typé couvre toutes les possibilités RPC supportées retenues par l'audit : paramètres/options, overloads et formes legacy encore supportées, contraintes déterministes utiles et variantes de réponse pertinentes sans perte. Le réaudit final `0.2.4` agrège le réaudit 37/37 de `0.2.3` avec les 15 nouveaux wrappers et porte la preuve stable à **52/52**. Les 14 méthodes historiques restent découvrables pour la compliance mais sont `Removed` et ne sont pas simulées comme appelables. ## Foundation WebSocket `0.2.7-pre.004` La première session physique WebSocket est matérialisée sans introduire de pool/scheduler automatique ni de registry de subscriptions anticipé. `WsSession::connect(WsEndpointSettings)` : - ouvre exactement une connexion physique pour un appel ; - confie le socket à une tâche actor unique ; - sérialise les commandes internes par un canal `mpsc` borné ; - maintient une map bornée de requests JSON-RPC en attente ; - publie `WsSessionSnapshot` via un état compact `watch` ; - applique aux sockets les plafonds KSP de message, frame et write buffer ; - ne projette jamais l'URL dans `Debug`, snapshot, erreurs KSP ou logs ; - répond aux `Ping` reçus et tolère les `Pong`; le lifecycle complet `Close`/shutdown a été durci ensuite en `pre.005`. Le chemin JSON-RPC générique reste `pub(crate)`. Il sert de primitive au moteur typed de subscriptions et **ne constitue pas une API publique raw provider-extension**. Le registry subscriptions et le mapping remote/local sont matérialisés en `pre.006`; reconnect/resubscribe puis le durcissement backpressure restent dans les tranches suivantes. Les tests déterministes utilisent un serveur WebSocket local et prouvent le handshake, le round-trip JSON-RPC, le dispatch de réponses hors ordre, l'isolation des erreurs RPC applicatives, deux sessions physiques distinctes sur la même URL et la redaction des erreurs de connexion. ### Durcissement `0.2.7-pre.005` La session physique dispose désormais de `WsSession::close().await`. Le signal de shutdown est distinct de la command queue, passe l'état en `Closing`, annule les requests JSON-RPC en attente, envoie un Close WebSocket best-effort sous `close_timeout`, puis publie `Closed`. Un peer qui ne répond pas au Close ne peut donc pas bloquer indéfiniment le shutdown. Les limites `max_message_size`, `max_frame_size`, `max_write_buffer_size` et `max_pending_requests` sont couvertes par des fixtures adversariales locales. Les requests outbound qui dépassent les bornes message/frame sont rejetées avant écriture ; les frames/messages inbound surdimensionnés sont rejetés par Tungstenite avant parse JSON. Les timeouts pending libèrent leur capacité sans faire tomber une session encore saine. Ping/Pong/Close sont traités comme control frames : le Pong automatique Tungstenite est flushé, un Close distant propre mène à `Closed`, tandis qu'une erreur I/O ou une violation de protocole mène à `Failed`. Aucun heartbeat applicatif périodique n'est ajouté. ### Registry de subscriptions `0.2.7-pre.006` Le même actor possède maintenant le registre des subscriptions logiques, sans exposer les IDs numériques distants. Chaque subscription reçoit un `WsSubscriptionId` local stable, et le mapping `remote_subscription_id -> WsSubscriptionId` reste strictement runtime/interne. La création générique typed reste `pub(crate)` jusqu'aux wrappers standard des tranches `pre.009+`. Le handle public `WsSubscription` expose uniquement : - `id()` et `kind()` ; - `state()` ; - `recv()` sur un canal typed borné ; - `unsubscribe()` qui conserve le booléen retourné par l'unsubscribe Solana standard. L'ACK de subscribe est traité atomiquement dans l'actor : le remote ID est lié au local ID avant que la notification suivante puisse être dispatchée. Les notifications inconnues/stale sont ignorées avec un diagnostic sûr. Un mismatch de méthode de notification ou un échec de décodage typed termine uniquement la subscription concernée ; la session physique reste `Active`. Le reconnect/resubscribe et les races associées restent hors scope jusqu'à `pre.007`. Le durcissement adversarial final de backpressure/leaks reste `pre.008`. ## Résilience L'admission est calculée par couple endpoint/rôle. Le pool applique : 1. rôle et capability ; 2. priorité croissante ; 3. round-robin dans le meilleur tier ; 4. RPS/burst ; 5. concurrence ; 6. cooldown rate-limit ; 7. fallback vers les pairs puis les tiers inférieurs ; 8. deadline commune à l'opération et ses retries. Les retries ne sont autorisés que lorsque la metadata de méthode et l'état de dispatch les rendent sûrs. `WriteSubmission / NeverAfterDispatch` interdit tout resend automatique après un dispatch ambigu. Les erreurs JSON-RPC applicatives ne sont pas transformées en retries transport génériques. ## Sécurité et diagnostics Les URLs d'endpoint peuvent contenir des credentials. Elles ne sont donc pas exposées par les `Debug`, snapshots ou logs ordinaires. Les `reqwest::Error` attachées comme source sont neutralisées avec `without_url()` avant exposition dans le contrat d'erreur KSP. Le target de tracing est possédé explicitement par : ```text src/constants.rs TRACING_TARGET = "ksp-onchain-transport-lib" ``` La configuration Logging de référence conserve un fichier dédié Transport à niveau `info`. Un niveau `debug`/`trace` ciblé peut être réactivé temporairement via Config lors d'un développement ou diagnostic explicite. ## Tests Les tests par défaut sont déterministes et n'exigent pas Internet : fixtures JSON et serveur HTTP local couvrent requêtes, réponses, retry, 429, timeout, redaction et routing. Deux smokes Devnet opt-in sont séparés par responsabilité : ```text Transport pur : settings programmatiques -> HttpTransportPool -> Accounts/Tokens/Cluster représentatifs -> trois reads Transactions -> getBlockHeight -> getInflationRate/getStakeMinimumDelegation Composition historique : Config -> std.transport/devnet_public -> HttpTransportPool -> getHealth/getGenesisHash/getVersion/getBalance ``` Le smoke Transport utilise pour sa branche Token la forme Devnet documentée `getTokenAccountsByOwner(owner, { programId }, { commitment: finalized, encoding: jsonParsed })`. L'owner est une Pubkey ordinaire de l'exemple officiel ; aucune présence de token account n'est exigée, donc une liste vide reste valide. Les deux sont `ignored` par défaut. Le smoke Transport appartient durablement à cette crate ; le smoke cross-crates hébergé dans Config reste transitoire jusqu'à l'existence d'une surface KSP d'intégration/orchestration appropriée. ## Documentation - [`USAGE.md`](USAGE.md) — consommation directe, Config -> Transport, API typed/raw, smokes et inspection runtime ; - [`../../docs/plans/008-V0_2_1_ONCHAIN_HTTP_PLAN.md`](../../docs/plans/008-V0_2_1_ONCHAIN_HTTP_PLAN.md) — foundation HTTP stable ; - [`../../docs/plans/009-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER_PLAN.md`](../../docs/plans/009-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER_PLAN.md) — extension typed Accounts/Tokens/Cluster ; - [`../../docs/validation/004-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER.md`](../../docs/validation/004-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER.md) — matrice finale validée `0.2.2` ; - [`../../docs/plans/010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md`](../../docs/plans/010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md) — plan historique clôturé de `0.2.3` ; - [`../../docs/validation/006-V0_2_3_HTTP_TRANSACTIONS.md`](../../docs/validation/006-V0_2_3_HTTP_TRANSACTIONS.md) — matrice finale validée `0.2.3` ; - [`../../docs/plans/011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md`](../../docs/plans/011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md) — plan Blocks/Economics et compliance HTTP finale ; - [`../../docs/validation/007-V0_2_4_HTTP_FINAL_COMPLIANCE.md`](../../docs/validation/007-V0_2_4_HTTP_FINAL_COMPLIANCE.md) — matrice finale validée `52/52 + 14/14` et audit `KSP-TRANSPORT-007` global ; - [`../../config/std.transport.json`](../../config/std.transport.json) — configuration standard HTTP + WebSocket V2, avec lecture backward V1 HTTP-only.