# `0.2.1-pre.001` — plan `ksp-onchain-transport-lib` HTTP Solana foundation ## 1. Statut et conclusion exécutive Ce document ouvre `0.2.1` à partir de la base stable fournie `0.2.0` et exécute le gate de sizing imposé par le prompt `006` avant toute grosse implémentation. Sa version initiale appartient à `0.2.1-pre.001`; le split de releases présenté dans cette version du plan est le recalibrage documentaire de `0.2.1-pre.001-fix.001`. > **État `pre.007` : candidate de clôture.** La foundation HTTP réduite est fonctionnellement matérialisée ; `pre.007` ajoute les canaries finales, le smoke Devnet opt-in, README/USAGE, la matrice de validation durable et le prompt `0.2.2`. La publication stable reste séparée dans `rel.001` après validations opérateur. Conclusion du gate : ```text Périmètre monolithique 0.2.1 décrit par le prompt 006 : NON clôturable raisonnablement dans cette session. 0.2.1 réduite après split : OUI, clôturable raisonnablement dans cette session. ``` Le périmètre initial combinait simultanément : 52 méthodes HTTP courantes typées, 14 méthodes historiques deprecated à classifier, les formes de paramètres legacy encore documentées, la crate foundation, le protocole JSON-RPC, l'ownership des modèles wire, les pools, rôles, priorités, limites, concurrence, retry/backoff, erreurs, logging, Config standard, adapter Config -> Transport, tests, fixtures, smoke tests et documentation. Cette combinaison dépasse la discipline KSP de tranches d'environ 15–20 minutes et ne permet pas de garantir la clôture complète de la release dans la même session. Le split conserve **l'inventaire exhaustif** : aucune méthode n'est masquée. Les 52 méthodes courantes et les 14 pages deprecated restent verrouillées dans la matrice ci-dessous et sont affectées explicitement à une release de la série HTTP. ## 2. Sources relues et base stable auditée ### 2.1 Sources internes KSP Relues pour cette tranche : - `ROADMAP.md` ; - `docs/000-README.md` ; - `docs/IDEAS.md` ; - `docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md` ; - `docs/plans/007-V0_2_0_SERIES_PLANNING.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/008-DATA_MATERIALIZATION_AND_STORE.md` ; - `docs/architecture/009-ACQUISITION_WORKERS_AND_JOBS.md` ; - `docs/rules/RULES_KSP.md` ; - `docs/rules/RULES_DEPENDENCIES.md` ; - `docs/rules/RULES_RUST.md` ; - `docs/rules/PROMPT_STRUCTURE.md` ; - `docs/rules/VERSION_WORKFLOW.md` ; - `docs/rules/FILE_CONTRACTS.md` ; - contrats publics actuels de `ksp-core-lib`, `ksp-logging-lib` et `ksp-config-lib`. ### 2.2 Base fournie L'archive `khadhroony-solana-project-v0.2.0.zip` contient : ```text workspace.package.version = "0.2.0" ``` et exactement les quatre crates déjà stabilisées : ```text ksp-core-lib ksp-logging-lib ksp-config-lib ksp-app-config-desk ``` Aucune crate Transport n'existe encore. L'archive ne contient pas `.git`, donc le tag `v0.2.0` ne peut pas être revérifié localement depuis ce zip. Le contenu technique fourni correspond néanmoins à la version stable attendue. ### 2.3 Contrats KSP qui conditionnent Transport - `ksp-core-lib` possède l'unique `Error`, `ErrorCode` et `Result` KSP. Transport déclarera ses codes dans son domaine mais ne créera pas une hiérarchie d'erreurs concurrente. - `ksp-logging-lib` possède la façade de tracing et les macros KSP ; Transport ne dépendra pas directement de `tracing`. - `ksp-config-lib` possède déjà registry, documents, profils, interpolation d'environnement, sensibilité/provenance et adapters de runtime. Le nouvel adapter sera donc **Config -> settings Transport**, jamais l'inverse. - Les règles d'architecture interdisent Transport -> Store et Transport -> Program. Les réponses restent wire/transport et préservent l'information nécessaire aux futures couches RAW/CORE. ## 3. Audit détaillé du transport bot3 Archive de référence : ```text khadhroony-bot3_v0.5.3-pre.005-fix010.zip workspace.package.version = "0.5.3-pre.5" ``` ### 3.1 Dépendances historiques `ks-onchain-transport/Cargo.toml` dépend directement de : ```text futures-util base64 bs58 ks-config ks-core ks-lib reqwest serde serde_json tokio tokio-tungstenite tracing ``` Décision KSP : | Élément bot3 | Décision | Motif | |------------------------|-------------------------------------------|---------------------------------------------------------------------------------------| | `reqwest` async HTTP | adapter | besoin HTTP réel, version/features à réauditer | | `serde` / `serde_json` | reprendre | contrat JSON-RPC/wire naturel et déjà workspace | | `tokio` | reprendre | runtime async déjà workspace ; `sync` devra être ajouté si Semaphore est retenu | | `base64` / `bs58` | différer | ne les ajouter qu'à la première méthode qui doit réellement encoder/valider ces wires | | `futures-util` | abandonner pour HTTP foundation | usage surtout live/stream ; pas nécessaire par anticipation | | `tokio-tungstenite` | abandonner pour `0.2.1` | WebSocket hors périmètre | | `ks-config` | **abandonner comme dépendance Transport** | ownership inversé dans KSP | | `ks-lib` | **abandonner** | couplage aux modèles canonical/Program contraire à RAW/CORE | | `tracing` direct | **abandonner** | `ksp-logging-lib` est propriétaire | ### 3.2 Inventaire des méthodes bot3 Le registre `STANDARD_HTTP_METHODS` contient **52 noms uniques**, tous avec le contrat `TypedAdapter`. La comparaison nom par nom avec l'index HTTP officiel audité le 2026-08-17 donne : ```text current Solana HTTP index : 52 bot3 STANDARD_HTTP_METHODS: 52 missing in bot3 : 0 extra in bot3 : 0 ``` Bot3 constitue donc une bonne preuve historique pour les **noms** de la surface courante, mais pas une source normative des paramètres, statuts, modèles ni frontières KSP. ### 3.3 Settings/roles bot3 utiles Bot3 possède : - endpoint : `name`, `enabled`, `provider`, `cluster`, `url`, `connect_timeout_ms`, `request_timeout_ms`, `max_idle_connections_per_host`, `roles` ; - rôle : `role`, `enabled`, `request_kinds`, `priority`, `requests_per_second`, `burst_capacity`, `max_concurrent_requests`, `max_subscriptions`, `pause_after_rate_limit_ms` ; - wildcard `*` pour `request_kinds` ; - limiteur token-bucket approximé + `Semaphore` ; - cooldown après rate-limit ; - snapshots sans URL afin de limiter la fuite de secrets. À reprendre : identité/provider/cluster, rôles ouverts, priorités, RPS/burst/concurrence, cooldown et snapshots redacted. À abandonner pour HTTP : `max_subscriptions` appartient au futur WebSocket et ne sera pas prérempli dans le contrat HTTP. ### 3.4 Pool bot3 : force et faiblesse Le pool historique fait un round-robin entre clients pouvant gérer le rôle/la méthode. Le client choisit localement le rôle correspondant de plus faible priorité. Cette architecture ne produit toutefois pas une sélection globale nette « meilleure priorité sur tous les endpoints, puis round-robin dans le tier ». KSP refond donc la sélection au niveau du pool logique : priorité globale, fairness dans un même tier, fallback sur endpoint indisponible/cooldown/saturé, et deadline commune. ### 3.5 Retry/rate-limit bot3 Bot3 sait reconnaître HTTP `429`, lire `Retry-After`, appliquer un backoff exponentiel borné et imposer un cooldown. Ces invariants sont utiles mais la politique n'est pas suffisamment liée à la nature de la méthode. En particulier, KSP doit empêcher tout resend ambigu de `sendTransaction`/`requestAirdrop` au nom d'un retry « transport » générique. ### 3.6 Couplages de modèles à supprimer Bot3 lie certaines méthodes techniques à `ks-lib`, notamment autour des signatures/transactions et de modèles canoniques. KSP interdit le pattern : ```text getTransaction -> modèle CORE/DECODE canonique ``` `getTransaction`/`getBlock` devront préserver le wire et les variations d'encoding sans normaliser vers Store/Program. ## 4. Audit officiel Solana HTTP — 2026-08-17 ### 4.1 Sources normatives Sources externes utilisées : - index HTTP Solana : ; - structures JSON RPC Solana : ; - pages individuelles `/docs/rpc/http/` ; - index/pages Deprecated : `/docs/rpc/deprecated/` ; - JSON-RPC 2.0 : ; - changelog Agave : pour vérifier la suppression runtime des endpoints v1 historiques. La documentation Solana indique JSON-RPC 2.0 sur HTTP `POST`, `Content-Type: application/json`, `jsonrpc = "2.0"`, `id`, `method` et paramètres ordonnés. ### 4.2 Surface courante L'index officiel contient **52 méthodes HTTP courantes** : ```text Accounts 6 Tokens 5 Transactions 11 Blocks 10 Cluster 15 Economics 5 Total 52 ``` Aucune page HTTP courante marquée `Unstable Method` ou `Experimental` n'a été trouvée pendant l'audit. Les résultats officiels portant explicitement le marqueur `Unstable Method` concernent actuellement des méthodes WebSocket/PubSub et restent hors `0.2.1`. ### 4.3 Surface Deprecated séparée La navigation officielle Deprecated contient **14 méthodes**. Les pages indiquent qu'elles étaient attendues supprimées en `solana-core v2.0`. Le changelog Agave confirme ensuite le retrait des endpoints RPC v1 obsolete/deprecated dans la ligne v2. Conséquence KSP : - elles restent dans le registre/matrice de conformité historique ; - leur statut documentaire est `Deprecated` ; - leur statut runtime KSP initial est `Removed` ; - aucune méthode callable ne simulera un succès ; - aucune tentative de warning « à l'utilisation » n'est nécessaire tant qu'elles sont réellement non exécutables ; - leur disponibilité devra être revérifiée depuis les sources officielles si une release future réouvre ce point. ### 4.4 Deprecated au niveau d'une forme de requête Deux méthodes **courantes et stables** documentent encore une forme de paramètres legacy explicitement deprecated : - `getTransaction` : second paramètre `encoding` sous forme de string nue ; - `getBlock` : second paramètre `encoding` sous forme de string nue. KSP doit donc distinguer : ```text method documentation status request-form documentation status runtime support status ``` Un simple enum au niveau méthode ne suffit pas à représenter fidèlement la documentation actuelle. `getBlocks` possède également un overload backward-compatible où le deuxième paramètre peut être l'objet config, mais la page actuelle ne le marque pas deprecated. Cette forme doit être sérialisée/testée sans warning. ## 5. Matrice exhaustive — 52 méthodes HTTP courantes Colonnes : nom, catégorie, paramètres/config à préserver, forme de résultat, statut documentaire, disponibilité runtime, niveau bot3, release KSP assignée, preuve minimale et source normative. | Méthode | Catégorie | Params / config | Résultat | Statut | Runtime | Bot3 | Release | Tests minimaux | Source | |-------------------------------------|--------------|----------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------|---------------------------------|----------|---------------|---------|--------------------------------------------------------------|------------------------------------------------------------------------------| | `getAccountInfo` | Accounts | pubkey ; config? {commitment, encoding, dataSlice, minContextSlot} | RpcResponse | Stable | Supporté | typed_adapter | `0.2.2` | params/encoding/dataSlice ; account/null ; RPC error | [Solana](https://solana.com/docs/rpc/http/getaccountinfo) | | `getBalance` | Accounts | pubkey ; config? {commitment, minContextSlot} | RpcResponse | Stable | Supporté | typed_adapter | `0.2.1` | params/config ; value u64 ; context/error | [Solana](https://solana.com/docs/rpc/http/getbalance) | | `getLargestAccounts` | Accounts | config? {commitment, filter, sortResults} | RpcResponse<[LargestAccount]> | Stable | Supporté | typed_adapter | `0.2.2` | config/filter/sort ; array | [Solana](https://solana.com/docs/rpc/http/getlargestaccounts) | | `getMinimumBalanceForRentExemption` | Accounts | data_len ; config? {commitment} | u64 | Stable | Supporté | typed_adapter | `0.2.2` | data length/config ; u64 | [Solana](https://solana.com/docs/rpc/http/getminimumbalanceforrentexemption) | | `getMultipleAccounts` | Accounts | pubkeys <= 100 ; config? {commitment, minContextSlot, dataSlice, encoding} | RpcResponse<[Account\|null]> | Stable | Supporté | typed_adapter | `0.2.2` | max/params ; mixed account/null ; encoding | [Solana](https://solana.com/docs/rpc/http/getmultipleaccounts) | | `getProgramAccounts` | Accounts | program pubkey ; config? {commitment, minContextSlot, withContext, encoding, dataSlice, filters, sortResults} | [KeyedAccount] ou RpcResponse<[KeyedAccount]> | Stable | Supporté | typed_adapter | `0.2.2` | filters/dataSlice ; withContext false/true ; errors | [Solana](https://solana.com/docs/rpc/http/getprogramaccounts) | | `getTokenAccountBalance` | Tokens | token account ; config? {commitment} | RpcResponse | Stable | Supporté | typed_adapter | `0.2.2` | config ; token amount | [Solana](https://solana.com/docs/rpc/http/gettokenaccountbalance) | | `getTokenAccountsByDelegate` | Tokens | delegate ; filter {mint\|programId} ; config? {commitment,minContextSlot,dataSlice,encoding} | RpcResponse<[KeyedAccount]> | Stable | Supporté | typed_adapter | `0.2.2` | mint/programId exclusifs ; config ; array | [Solana](https://solana.com/docs/rpc/http/gettokenaccountsbydelegate) | | `getTokenAccountsByOwner` | Tokens | owner ; filter {mint\|programId} ; config? {commitment,minContextSlot,dataSlice,encoding} | RpcResponse<[KeyedAccount]> | Stable | Supporté | typed_adapter | `0.2.2` | mint/programId exclusifs ; config ; array | [Solana](https://solana.com/docs/rpc/http/gettokenaccountsbyowner) | | `getTokenLargestAccounts` | Tokens | mint ; config? {commitment} | RpcResponse<[TokenLargestAccount]> | Stable | Supporté | typed_adapter | `0.2.2` | config ; array | [Solana](https://solana.com/docs/rpc/http/gettokenlargestaccounts) | | `getTokenSupply` | Tokens | mint ; config? {commitment} | RpcResponse | Stable | Supporté | typed_adapter | `0.2.2` | config ; TokenAmount | [Solana](https://solana.com/docs/rpc/http/gettokensupply) | | `getFeeForMessage` | Transactions | message base64 ; config? {commitment,minContextSlot} | RpcResponse | Stable | Supporté | typed_adapter | `0.2.3` | message/config ; fee/null ; error | [Solana](https://solana.com/docs/rpc/http/getfeeformessage) | | `getLatestBlockhash` | Transactions | config? {commitment,minContextSlot} | RpcResponse<{blockhash,lastValidBlockHeight}> | Stable | Supporté | typed_adapter | `0.2.3` | config ; object | [Solana](https://solana.com/docs/rpc/http/getlatestblockhash) | | `getRecentPrioritizationFees` | Transactions | pubkeys? <= 128 | [{slot,prioritizationFee}] | Stable | Supporté | typed_adapter | `0.2.3` | no params / addresses ; max ; array | [Solana](https://solana.com/docs/rpc/http/getrecentprioritizationfees) | | `getSignaturesForAddress` | Transactions | address ; config? {commitment,minContextSlot,limit,before,until} | [SignatureInfo] | Stable | Supporté | typed_adapter | `0.2.3` | pagination/config ; nullable memo/blockTime/status | [Solana](https://solana.com/docs/rpc/http/getsignaturesforaddress) | | `getSignatureStatuses` | Transactions | signatures <= 256 ; config? {searchTransactionHistory} | RpcResponse<[SignatureStatus\|null]> | Stable | Supporté | typed_adapter | `0.2.3` | max ; cache/history ; object/null | [Solana](https://solana.com/docs/rpc/http/getsignaturestatuses) | | `getTransaction` | Transactions | signature ; config? {commitment,maxSupportedTransactionVersion,encoding} ; legacy bare encoding deprecated | TransactionResponse\|null | Stable; forme legacy deprecated | Supporté | typed_adapter | `0.2.3` | config + legacy warning ; all encodings ; null ; error | [Solana](https://solana.com/docs/rpc/http/gettransaction) | | `getTransactionCount` | Transactions | config? {commitment,minContextSlot} | u64 | Stable | Supporté | typed_adapter | `0.2.3` | config ; u64 | [Solana](https://solana.com/docs/rpc/http/gettransactioncount) | | `isBlockhashValid` | Transactions | blockhash ; config? {commitment,minContextSlot} | RpcResponse | Stable | Supporté | typed_adapter | `0.2.3` | hash/config ; bool | [Solana](https://solana.com/docs/rpc/http/isblockhashvalid) | | `requestAirdrop` | Transactions | pubkey ; lamports ; config? {commitment,recentBlockhash} | signature | Stable | Supporté | typed_adapter | `0.2.3` | serialization ; signature/error ; no-resend policy | [Solana](https://solana.com/docs/rpc/http/requestairdrop) | | `sendTransaction` | Transactions | signed transaction ; config? {encoding,skipPreflight,preflightCommitment,maxRetries,minContextSlot} | signature | Stable | Supporté | typed_adapter | `0.2.3` | all configs ; RPC error ; timeout/no-resend | [Solana](https://solana.com/docs/rpc/http/sendtransaction) | | `simulateTransaction` | Transactions | transaction ; config? {commitment,encoding,replaceRecentBlockhash,sigVerify,minContextSlot,innerInstructions,accounts} | RpcResponse | Stable | Supporté | typed_adapter | `0.2.3` | configs compatibles/incompatibles ; logs/accounts/null/error | [Solana](https://solana.com/docs/rpc/http/simulatetransaction) | | `getBlock` | Blocks | slot ; config? {commitment,encoding,transactionDetails,maxSupportedTransactionVersion,rewards} ; legacy bare encoding deprecated | BlockResponse\|null | Stable; forme legacy deprecated | Supporté | typed_adapter | `0.2.4` | config + legacy warning ; transactionDetails variants ; null | [Solana](https://solana.com/docs/rpc/http/getblock) | | `getBlockCommitment` | Blocks | slot | {commitment:[u64]\|null,totalStake:u64} | Stable | Supporté | typed_adapter | `0.2.4` | slot ; commitment null/array | [Solana](https://solana.com/docs/rpc/http/getblockcommitment) | | `getBlockHeight` | Blocks | config? {commitment,minContextSlot} | u64 | Stable | Supporté | typed_adapter | `0.2.4` | config ; u64 | [Solana](https://solana.com/docs/rpc/http/getblockheight) | | `getBlockProduction` | Blocks | config? {commitment,identity,range} | RpcResponse | Stable | Supporté | typed_adapter | `0.2.4` | identity/range/config ; map | [Solana](https://solana.com/docs/rpc/http/getblockproduction) | | `getBlocks` | Blocks | startSlot ; endSlot? ou config? ; config? {commitment,minContextSlot} | [u64] | Stable | Supporté | typed_adapter | `0.2.4` | overload 1/2/3 params ; max range 500k ; empty | [Solana](https://solana.com/docs/rpc/http/getblocks) | | `getBlocksWithLimit` | Blocks | startSlot ; limit <= 500000 ; config? {commitment,minContextSlot} | [u64] | Stable | Supporté | typed_adapter | `0.2.4` | limit boundaries ; config ; array | [Solana](https://solana.com/docs/rpc/http/getblockswithlimit) | | `getBlockTime` | Blocks | slot | i64 | Stable | Supporté | typed_adapter | `0.2.4` | success ; unavailable/error variant | [Solana](https://solana.com/docs/rpc/http/getblocktime) | | `getFirstAvailableBlock` | Blocks | aucun | u64 | Stable | Supporté | typed_adapter | `0.2.4` | empty params ; u64 | [Solana](https://solana.com/docs/rpc/http/getfirstavailableblock) | | `getRecentPerformanceSamples` | Blocks | limit? <= 720 | [PerformanceSample] | Stable | Supporté | typed_adapter | `0.2.4` | no params/limit ; nullable numNonVoteTransactions | [Solana](https://solana.com/docs/rpc/http/getrecentperformancesamples) | | `minimumLedgerSlot` | Blocks | aucun | u64 | Stable | Supporté | typed_adapter | `0.2.4` | empty params ; u64 | [Solana](https://solana.com/docs/rpc/http/minimumledgerslot) | | `getClusterNodes` | Cluster | aucun | [ClusterNode] | Stable | Supporté | typed_adapter | `0.2.2` | nullable endpoints/features ; array | [Solana](https://solana.com/docs/rpc/http/getclusternodes) | | `getEpochInfo` | Cluster | config? {commitment,minContextSlot} | EpochInfo | Stable | Supporté | typed_adapter | `0.2.2` | config ; transactionCount null | [Solana](https://solana.com/docs/rpc/http/getepochinfo) | | `getEpochSchedule` | Cluster | aucun | EpochSchedule | Stable | Supporté | typed_adapter | `0.2.2` | empty params ; object | [Solana](https://solana.com/docs/rpc/http/getepochschedule) | | `getGenesisHash` | Cluster | aucun | string base58 | Stable | Supporté | typed_adapter | `0.2.1` | empty params ; hash string ; error | [Solana](https://solana.com/docs/rpc/http/getgenesishash) | | `getHealth` | Cluster | aucun | "ok" ou RPC unhealthy error | Stable | Supporté | typed_adapter | `0.2.1` | healthy ; unhealthy RPC error | [Solana](https://solana.com/docs/rpc/http/gethealth) | | `getHighestSnapshotSlot` | Cluster | aucun | {full:u64,incremental:u64\|null} | Stable | Supporté | typed_adapter | `0.2.2` | incremental null ; no snapshot/error | [Solana](https://solana.com/docs/rpc/http/gethighestsnapshotslot) | | `getIdentity` | Cluster | aucun | {identity:string} | Stable | Supporté | typed_adapter | `0.2.2` | empty params ; identity | [Solana](https://solana.com/docs/rpc/http/getidentity) | | `getLeaderSchedule` | Cluster | slot? \| config? \| null ; config? {commitment,identity} | map identity->[slot_index] \| null | Stable | Supporté | typed_adapter | `0.2.2` | all overloads ; filter ; null | [Solana](https://solana.com/docs/rpc/http/getleaderschedule) | | `getMaxRetransmitSlot` | Cluster | aucun | u64 | Stable | Supporté | typed_adapter | `0.2.2` | empty params ; u64 | [Solana](https://solana.com/docs/rpc/http/getmaxretransmitslot) | | `getMaxShredInsertSlot` | Cluster | aucun | u64 | Stable | Supporté | typed_adapter | `0.2.2` | empty params ; u64 | [Solana](https://solana.com/docs/rpc/http/getmaxshredinsertslot) | | `getSlot` | Cluster | config? {commitment,minContextSlot} | u64 | Stable | Supporté | typed_adapter | `0.2.2` | config ; u64 | [Solana](https://solana.com/docs/rpc/http/getslot) | | `getSlotLeader` | Cluster | config? {commitment,minContextSlot} | string pubkey | Stable | Supporté | typed_adapter | `0.2.2` | config ; pubkey | [Solana](https://solana.com/docs/rpc/http/getslotleader) | | `getSlotLeaders` | Cluster | startSlot ; limit 1..5000 | [string pubkey] | Stable | Supporté | typed_adapter | `0.2.2` | limit boundaries ; array | [Solana](https://solana.com/docs/rpc/http/getslotleaders) | | `getVersion` | Cluster | aucun | {solana-core:string,feature-set:u32\|null} | Stable | Supporté | typed_adapter | `0.2.1` | empty params ; feature-set present/null | [Solana](https://solana.com/docs/rpc/http/getversion) | | `getVoteAccounts` | Cluster | config? {commitment,votePubkey,keepUnstakedDelinquents,delinquentSlotDistance} | {current:[VoteAccount],delinquent:[VoteAccount]} | Stable | Supporté | typed_adapter | `0.2.2` | filters/config ; both sets | [Solana](https://solana.com/docs/rpc/http/getvoteaccounts) | | `getInflationGovernor` | Economics | config? {commitment} | InflationGovernor | Stable | Supporté | typed_adapter | `0.2.4` | config ; f64 fields | [Solana](https://solana.com/docs/rpc/http/getinflationgovernor) | | `getInflationRate` | Economics | aucun | {total,validator,foundation:f64,epoch:u64} | Stable | Supporté | typed_adapter | `0.2.4` | empty params ; object | [Solana](https://solana.com/docs/rpc/http/getinflationrate) | | `getInflationReward` | Economics | addresses ; config? {commitment,epoch,minContextSlot} | [InflationReward\|null] | Stable | Supporté | typed_adapter | `0.2.4` | addresses/config ; reward/null ; commission null | [Solana](https://solana.com/docs/rpc/http/getinflationreward) | | `getStakeMinimumDelegation` | Economics | config? {commitment,minContextSlot} | RpcResponse | Stable | Supporté | typed_adapter | `0.2.4` | config ; value | [Solana](https://solana.com/docs/rpc/http/getstakeminimumdelegation) | | `getSupply` | Economics | config? {commitment,excludeNonCirculatingAccountsList} | RpcResponse | Stable | Supporté | typed_adapter | `0.2.4` | config true/false ; list semantics | [Solana](https://solana.com/docs/rpc/http/getsupply) | ## 6. Matrice exhaustive — 14 méthodes Deprecated historiques | Méthode | Statut doc | Runtime actuel | Remplacement / direction | Bot3 HTTP | Contrat KSP | Source | |-------------------------------------|------------|-------------------------|---------------------------------------|------------------------------|-------------------------------------------------------------|------------------------------------------------------------------------------------| | `confirmTransaction` | Deprecated | **Removed** (Agave v2+) | `getSignatureStatuses` | absent du registre HTTP bot3 | descripteur historique en `0.2.1`, aucune exécution simulée | [Solana](https://solana.com/docs/rpc/deprecated/confirmtransaction) | | `getConfirmedBlock` | Deprecated | **Removed** (Agave v2+) | `getBlock` | absent du registre HTTP bot3 | descripteur historique en `0.2.1`, aucune exécution simulée | [Solana](https://solana.com/docs/rpc/deprecated/getconfirmedblock) | | `getConfirmedBlocks` | Deprecated | **Removed** (Agave v2+) | `getBlocks` | absent du registre HTTP bot3 | descripteur historique en `0.2.1`, aucune exécution simulée | [Solana](https://solana.com/docs/rpc/deprecated/getconfirmedblocks) | | `getConfirmedBlocksWithLimit` | Deprecated | **Removed** (Agave v2+) | `getBlocksWithLimit` | absent du registre HTTP bot3 | descripteur historique en `0.2.1`, aucune exécution simulée | [Solana](https://solana.com/docs/rpc/deprecated/getconfirmedblockswithlimit) | | `getConfirmedSignaturesForAddress2` | Deprecated | **Removed** (Agave v2+) | `getSignaturesForAddress` | absent du registre HTTP bot3 | descripteur historique en `0.2.1`, aucune exécution simulée | [Solana](https://solana.com/docs/rpc/deprecated/getconfirmedsignaturesforaddress2) | | `getConfirmedTransaction` | Deprecated | **Removed** (Agave v2+) | `getTransaction` | absent du registre HTTP bot3 | descripteur historique en `0.2.1`, aucune exécution simulée | [Solana](https://solana.com/docs/rpc/deprecated/getconfirmedtransaction) | | `getFeeCalculatorForBlockhash` | Deprecated | **Removed** (Agave v2+) | `isBlockhashValid / getFeeForMessage` | absent du registre HTTP bot3 | descripteur historique en `0.2.1`, aucune exécution simulée | [Solana](https://solana.com/docs/rpc/deprecated/getfeecalculatorforblockhash) | | `getFeeRateGovernor` | Deprecated | **Removed** (Agave v2+) | `getFeeForMessage` | absent du registre HTTP bot3 | descripteur historique en `0.2.1`, aucune exécution simulée | [Solana](https://solana.com/docs/rpc/deprecated/getfeerategovernor) | | `getFees` | Deprecated | **Removed** (Agave v2+) | `getFeeForMessage` | absent du registre HTTP bot3 | descripteur historique en `0.2.1`, aucune exécution simulée | [Solana](https://solana.com/docs/rpc/deprecated/getfees) | | `getRecentBlockhash` | Deprecated | **Removed** (Agave v2+) | `getLatestBlockhash` | absent du registre HTTP bot3 | descripteur historique en `0.2.1`, aucune exécution simulée | [Solana](https://solana.com/docs/rpc/deprecated/getrecentblockhash) | | `getSignatureConfirmation` | Deprecated | **Removed** (Agave v2+) | `getSignatureStatuses` | absent du registre HTTP bot3 | descripteur historique en `0.2.1`, aucune exécution simulée | [Solana](https://solana.com/docs/rpc/deprecated/getsignatureconfirmation) | | `getSignatureStatus` | Deprecated | **Removed** (Agave v2+) | `getSignatureStatuses` | absent du registre HTTP bot3 | descripteur historique en `0.2.1`, aucune exécution simulée | [Solana](https://solana.com/docs/rpc/deprecated/getsignaturestatus) | | `getSnapshotSlot` | Deprecated | **Removed** (Agave v2+) | `getHighestSnapshotSlot` | absent du registre HTTP bot3 | descripteur historique en `0.2.1`, aucune exécution simulée | [Solana](https://solana.com/docs/rpc/deprecated/getsnapshotslot) | | `getStakeActivation` | Deprecated | **Removed** (Agave v2+) | `approche alternative documentée` | absent du registre HTTP bot3 | descripteur historique en `0.2.1`, aucune exécution simulée | [Solana](https://solana.com/docs/rpc/deprecated/getstakeactivation) | ### 6.1 Écart bot3 / documentation actuelle La décision `runtime = Removed` repose ici sur le changelog officiel Agave v2+, qui documente la suppression des endpoints RPC v1 obsolètes/dépréciés. Aucun POST JSON-RPC live vers un provider public ou privé n’a été exécuté pendant `pre.001`; une divergence propre à un provider devra donc être documentée comme extension/comportement provider, sans réintroduire ces méthodes dans le contrat standard KSP. Résumé nom par nom : - les **52 courantes** sont toutes présentes dans bot3 et toutes marquées `TypedAdapter` ; - les **14 Deprecated** de la navigation officielle sont absentes du registre HTTP standard bot3 ; - elles ne doivent pas être recopiées comme wrappers callables aujourd'hui puisque le runtime Agave v2 les a retirées ; - le nouvel inventaire KSP sera donc plus complet que bot3 sur la **metadata documentaire**, tout en exposant moins de faux support runtime. ## 7. Split obligatoire de la série HTTP Le split retenu est : | Release | Mission | Surface courante assignée | |---------|---------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------| | `0.2.1` | **HTTP transport foundation** | 4 canaris : `getBalance`, `getGenesisHash`, `getHealth`, `getVersion` + registre 52/14 + infrastructure complète foundation | | `0.2.2` | HTTP Accounts + Tokens + Cluster | 5 Accounts restants + 5 Tokens + 12 Cluster restants = 22 | | `0.2.3` | HTTP Transactions | 11 | | `0.2.4` | HTTP Blocks + Economics + compliance finale | 10 Blocks + 5 Economics = 15 + gate exhaustif 52 courantes / 14 historiques | La présence d'un appel JSON-RPC raw/générique dans `0.2.1` **ne comptera pas** comme couverture typée des méthodes affectées à `0.2.2`–`0.2.4`. Ces trois releases HTTP complémentaires représentent **trois sessions nominales au maximum**, pas trois chats obligatoires. Si une release est entièrement clôturée plus vite que prévu, la même session peut ouvrir puis clôturer la suivante après un nouveau gate de sizing positif. Les frontières restent strictement séparées : numéro de release, prereleases, version, delta, validations et clôture stable. Wallet est décalé à `0.2.5` et Wallet Desk à `0.2.6`. Le premier `getBalance` reste volontairement dans `0.2.1`, donc ce split ne retire pas la capacité réseau minimale dont Wallet Desk aura besoin. ## 8. Périmètre exact de la `0.2.1` réduite `0.2.1` doit clôturer : 1. création de `ksp-onchain-transport-lib` ; 2. settings publics Transport et validation ; 3. JSON-RPC 2.0 request/result/error envelopes ; 4. registre exhaustif des 52 courantes + 14 historiques deprecated ; 5. metadata méthode + forme de requête + runtime + retry class ; 6. endpoint client HTTP async ; 7. pool logique, rôles ouverts, capabilities/request kinds, priorité et fallback ; 8. RPS/burst/concurrence/cooldown ; 9. timeout et retry transport borné ; 10. mapping vers `ksp_core_lib::Error` ; 11. observabilité via `ksp-logging-lib` ; 12. 4 méthodes typées canari ; 13. `std.transport` + schema + exemple + adapter Config -> settings Transport ; 14. tests/fixtures/canaries/smoke opt-in ; 15. `README.md` / `USAGE.md` ; 16. preuve que les familles restantes sont explicitement reportées, sans perdre l'inventaire. ## 9. Contrats publics Transport décidés Les noms pourront être ajustés seulement si une contrainte Rust concrète apparaît, mais l'ownership et la sémantique sont fixés. ### 9.1 Settings principaux ```rust pub struct HttpTransportSettings { pub endpoints: Vec, pub retry: HttpRetrySettings, } pub struct HttpEndpointSettings { pub name: String, pub enabled: bool, pub provider: String, pub cluster: String, pub url: HttpEndpointUrl, pub connect_timeout: Duration, pub request_timeout: Duration, pub max_idle_connections_per_host: Option, pub roles: Vec, } pub struct HttpEndpointRoleSettings { pub role: HttpRoleName, pub enabled: bool, pub request_kinds: Vec, pub priority: u32, pub limits: HttpRoleLimits, } pub struct HttpRoleLimits { pub requests_per_second: Option, pub burst_capacity: Option, pub max_concurrent_requests: Option, pub pause_after_rate_limit: Option, } pub struct HttpRetrySettings { pub max_retries: u32, pub initial_backoff: Duration, pub max_backoff: Duration, } ``` Décisions : - Transport utilise `Duration`; Config traduit ses scalaires `*_ms` vers ce contrat runtime ; - `provider`, `cluster`, `role` et `request_kind` restent des descriptors ouverts/newtypes validés, pas des enums fermées ; - `*` reste un wildcard valide pour `request_kinds` ; - URL est encapsulée dans un type KSP avec `Debug` redacted ; `reqwest::Url` n'est pas un type public du contrat ; - aucune lecture directe de l'environnement dans Transport ; - aucun champ WS/gRPC anticipé. ### 9.2 Validation minimale Refuser au minimum : - nom endpoint vide/dupliqué ; - URL non HTTP(S) ou invalide ; - aucun endpoint activé ; - rôle vide/dupliqué sur un endpoint ; - request kind vide ; - limites incohérentes (`burst` sans RPS, zéro lorsque la valeur est présente, backoff max < initial, etc.) ; - timeout nul lorsque la sémantique exige une deadline positive. Un endpoint `enabled = false` reste valide dans le document/snapshot mais n'est jamais candidat à la sélection. ## 10. Descriptor central des méthodes Contrat conceptuel : ```rust pub struct HttpRpcMethodDescriptor { pub method: &'static str, pub category: HttpRpcCategory, pub request_kind: &'static str, pub documentation_status: RpcDocumentationStatus, pub runtime_status: RpcRuntimeStatus, pub operation_kind: RpcOperationKind, pub transport_retry_class: TransportRetryClass, pub replacement: Option<&'static str>, } pub enum RpcDocumentationStatus { Stable, Deprecated, Unstable, } pub enum RpcRuntimeStatus { Supported, Removed, } pub enum RpcRequestFormStatus { Stable, Deprecated, } pub enum RpcOperationKind { Read, Simulation, WriteSubmission, } ``` Le descripteur est la source unique pour : - nom JSON-RPC ; - routing/request kind ; - statut documentaire ; - statut runtime ; - warning deprecated/unstable ; - remplacement historique ; - politique de retry transport ; - canary de complétude. Les warnings ne sont pas dispersés dans 52 méthodes. ## 11. JSON-RPC et API raw/générique `0.2.1` fournit les envelopes JSON-RPC 2.0 KSP : ```text request : jsonrpc, id, method, params success : jsonrpc, id, result failure : jsonrpc, id, error { code, message, data? } ``` Invariants : - `jsonrpc` doit être exactement `2.0` ; - l'`id` de réponse doit correspondre à la requête ; - `result` et `error` sont mutuellement exclusifs ; - une réponse invalide devient `invalid_response`/`json_rpc_protocol`, jamais un faux résultat ; - le `data` d'une erreur RPC peut être préservé comme `serde_json::Value`, mais n'est pas loggué massivement par défaut. Une API raw/générique peut exister pour l'extensibilité/provider interop. Elle ne remplace pas la surface typée normative et n'est pas une preuve de couverture des releases `0.2.2`–`0.2.4`. Pour la surface standard connue, l'exécuteur consulte le registry. Une méthode `runtime = Removed` est refusée comme méthode standard au lieu d'être envoyée en prétendant qu'elle est supportée. ## 12. Pool, rôles, capabilities et sélection ### 12.1 Unité de pool Le pool sélectionne un **endpoint/client HTTP logique**. Le pooling de sockets/keep-alive reste la responsabilité du client `reqwest` sous-jacent. ### 12.1.1 Politique client concrétisée par `pre.003` Chaque endpoint construit un `reqwest::Client` partageable qui conserve le pooling de sockets sous-jacent. Le builder KSP applique les `connect_timeout`, `request_timeout` et `max_idle_connections_per_host` déjà possédés par les settings. Décisions de sécurité/déterminisme : - backend HTTPS : feature `reqwest/rustls` explicite ; - redirects automatiques désactivés : une URL provider éventuellement porteuse de credential n'est jamais redirigée implicitement vers une autre destination ; - proxies système/environnement implicites désactivés via le builder `reqwest`; une future prise en charge de proxy devra être un contrat Config/Transport explicite ; - `User-Agent` KSP stable : `ksp-onchain-transport-lib/` ; - URL conservée uniquement dans les settings privés du client logique et absente des snapshots/`Debug` publics. ### 12.2 Algorithme de sélection Pour une requête : 1. déterminer le `request_kind` depuis le descriptor standard ou le caller raw ; 2. filtrer endpoints disabled ; 3. filtrer rôles disabled ; 4. filtrer rôle exact + capability (`request_kind` ou `*`) ; 5. exclure temporairement les candidats en cooldown/rate-limit ; 6. ordonner par `priority` croissante ; 7. round-robin équitable entre candidats du meilleur tier ; 8. si capacité/concurrence indisponible, essayer les pairs puis le tier de priorité suivant ; 9. si tous les candidats sont temporairement bloqués, attendre jusqu'au premier candidat admissible sans dépasser la deadline de requête ; 10. si aucun endpoint ne peut satisfaire le rôle/méthode, retourner une erreur `endpoint_selection` structurée. ### 12.3 Concurrence et rate limit Chaque couple endpoint/rôle possède son état runtime : - token bucket RPS/burst si configuré ; - semaphore de concurrence si configuré ; - cooldown après `429` ; - deadline commune de requête. Aucune boucle active de health-check n'est ajoutée en `0.2.1`. Le health est passif : succès/échecs récents, cooldown et état `Enabled/Disabled/Available/Degraded/RateLimited` dans des snapshots sûrs. ### 12.4 Thread-safety Les clients/pools sont partageables entre tâches : `Arc` sur état commun, primitives Tokio pour concurrence asynchrone, atomics lorsque suffisants. Aucune mutex synchrone ne doit couvrir un await réseau. ## 13. Retry, backoff et timeout ### 13.1 Retry transport autorisé Retry uniquement quand l'opération est classée retry-safe et qu'aucun résultat exploitable n'a été obtenu : - erreur de connexion clairement retryable ; - certaines erreurs HTTP provider temporaires ; - `429` avec cooldown/`Retry-After` ; - timeout pour les opérations read/simulation retry-safe. Backoff exponentiel borné : ```text initial_backoff <= computed_backoff <= max_backoff attempts <= max_retries ``` `Retry-After` peut allonger la pause dans la borne décidée par le transport ; aucune attente non bornée. ### 13.2 Write/submission `sendTransaction` et `requestAirdrop` sont classés `WriteSubmission / NeverAfterDispatch`. Le transport KSP ne renvoie pas automatiquement une transaction sur timeout ou erreur ambiguë après dispatch. Le paramètre Solana `sendTransaction.maxRetries` appartient au comportement du nœud RPC et ne devient pas la politique de retry HTTP KSP. `simulateTransaction` reste une simulation et peut être retry-safe au niveau transport. ### 13.3 Retry métier interdit Aucune décision du type « RPC application error => refaire l'opération métier » n'est introduite dans Transport. ## 14. Stratégie d'erreurs Les erreurs utilisent `ksp_core_lib::Error` et des constantes `ErrorCode::new("onchain_transport", ...)` dans la crate Transport. Codes candidats à stabiliser dans `pre.002` : ```text invalid_settings endpoint_selection_failed http_connection_failed http_request_failed timeout rate_limited json_encode_failed json_decode_failed json_rpc_protocol_invalid rpc_application_error method_removed invalid_response ``` Chaque erreur ajoute uniquement du contexte sûr : endpoint name/provider/cluster, méthode, rôle, attempt, code HTTP/RPC. L'URL complète et tout secret sont exclus des contextes/logs. ## 15. Ownership des modèles de réponse ### 15.1 Principe Transport représente le wire nécessaire à l'appel et à sa future persistence RAW. Il ne produit pas CORE, DECODE ou SPECIALIZED. ### 15.2 Primitives communes Introduire des types transport seulement lorsqu'ils ajoutent un invariant wire utile. Les champs dynamiques/évolutifs peuvent rester `serde_json::Value` aux frontières appropriées plutôt que de forcer une dépendance SDK massive. Décisions : - `RpcResponse` pour les réponses contextuelles ; - `RpcContext` avec slot/API version lorsque documenté ; - `TokenAmount` conservant `amount: String`, `decimals`, `uiAmount: Option`, `uiAmountString` ; - `Account` transport préservant encoding/data sans décodage Program ; - signatures/hash/identités comme wire string/newtype KSP, sauf bénéfice clair à utiliser une primitive `ksp-core-lib` déjà autorisée ; - `getTransaction`/`getBlock` : top-level typé + payloads encoding-dependent lossless/flexibles ; - distinction stricte JSON `null` vs champ absent lorsque le contrat le nécessite. ### 15.3 Dépendances SDK Ne pas introduire `solana-client` ni un SDK client haut niveau pour obtenir des DTOs. Une dépendance Solana additionnelle devra répondre à un besoin wire précis et respecter le firewall KSP. ## 16. Config standard Transport et adapter ### 16.1 Identifiants retenus Selon les conventions de `ksp-config-lib` : ```text FILE_ID_STD_TRANSPORT = "cfg.std.transport" FILE_ID_SCHEMA_STD_TRANSPORT = "schema.std.transport" DEFAULT_STD_TRANSPORT_FILENAME = "std.transport.json" DEFAULT_STD_TRANSPORT_SCHEMA_FILENAME = "std.transport.schema.json" ``` Fichiers : ```text config/std.transport.json config/schemas/std.transport.schema.json config/examples/std.transport.example.json ``` ### 16.2 Forme candidate du document ```json { "format_version": 1, "default_profile": "devnet_public", "retry": { "max_retries": 2, "initial_backoff_ms": 100, "max_backoff_ms": 2000 }, "profiles": [ { "profile_id": "devnet_public", "http_endpoints": [ { "name": "solana_devnet_public", "enabled": true, "provider": "solana-public", "cluster": "devnet", "url": "https://api.devnet.solana.com", "connect_timeout_ms": 5000, "request_timeout_ms": 15000, "max_idle_connections_per_host": 8, "roles": [ { "role": "default", "enabled": true, "request_kinds": ["*"], "priority": 100, "limits": { "requests_per_second": null, "burst_capacity": null, "max_concurrent_requests": 8, "pause_after_rate_limit_ms": 1000 } } ] } ] } ] } ``` Le moteur Config existant résout profil + environnement. L'adapter transforme ensuite ce document effectif vers `HttpTransportSettings` et convertit les millisecondes en `Duration`. ### 16.3 Secrets/env Transport ne lit jamais l'environnement. Le schema autorise une URL résolue par le mécanisme de placeholders Config existant. La sensibilité `KSP_SECRET_*` doit être propagée/redacted dans l'adapter et les snapshots. Aucune nouvelle variable `.env.example` n'est nécessaire en `pre.001`. Le profil standard peut utiliser l'URL publique Devnet sans secret. Une variable provider réellement consommée sera ajoutée dans la même tranche que son usage réel, pas par anticipation. ### 16.4 Config Desk Aucune UI Transport dédiée. Le document reste éditable via le moteur générique Config Desk existant. ## 17. Dépendances externes candidates Audit du 2026-08-17 : - `reqwest` courant observé : `0.13.4` ; - `tokio` courant observé : `1.53.1`, tandis que le workspace utilise déjà `^1.53` ; - `serde` / `serde_json` sont déjà des dépendances workspace `^1.0` ; - bot3 utilisait également `base64` et `bs58`, à différer jusqu'au premier besoin d'encoding explicite. Décision concrétisée par `pre.002` : ```text reqwest = ^0.13, default-features = false au workspace serde.workspace = true serde_json.workspace = true ksp-core-lib path/workspace ksp-logging-lib path/workspace ``` `pre.002` utilisait `reqwest` uniquement pour le parsing/validation robuste de `HttpEndpointUrl`. `pre.003` crée réellement les clients HTTP logiques et active donc localement la feature `rustls` de `reqwest 0.13` dans `ksp-onchain-transport-lib`, tandis que le workspace conserve `default-features = false` sans feature d’usage globale. Aucune feature `json` n'est nécessaire : KSP possède déjà son encodage JSON-RPC via `serde_json`. Aucune dépendance Tokio directe n'est ajoutée tant que `pre.004` n'utilise pas effectivement ses primitives de concurrence/attente. Ne pas ajouter : ```text solana-client tokio-tungstenite gRPC stack futures-util uniquement pour anticipation live base64/bs58 avant besoin concret tracing direct ``` Les commandes `cargo tree` sont obligatoires dans la tranche qui ajoute effectivement `reqwest`. ## 18. Logging et observabilité Target KSP : ```text ksp-onchain-transport-lib ``` Ce target est possédé explicitement par `src/constants.rs` via `pub(crate) const TRACING_TARGET: &str = "ksp-onchain-transport-lib"`; les appels de logging utilisent `crate::TRACING_TARGET` conformément à `DEP-LOG-010/011`. `env!("CARGO_PKG_NAME")` reste réservé aux usages où l’identité Cargo est réellement la donnée recherchée, par exemple le User-Agent HTTP. Événements : - création client/pool ; - sélection endpoint/rôle ; - début/fin requête en debug/trace ; - retry/attempt ; - timeout ; - 429/cooldown/backoff ; - endpoint degraded/unavailable ; - RPC application error ; - forme deprecated/unstable utilisée ; - snapshots de santé importants. Interdits par défaut : - URL complète si elle peut porter un token ; - API key/provider token ; - body massif ; - transaction complète ; - réponse complète. Les erreurs `reqwest` utiles sont remappées/réémises via `ksp-logging-lib`; aucun target externe n'est activé globalement par Transport. Pendant le développement actif jusqu'à `pre.006`, `std.logging.json` autorisait `debug` uniquement pour `ksp-onchain-transport-lib` et routait ces événements vers un fichier dédié. `pre.007` applique `KSP-APP-031` : le target et le sink dédié reviennent à `info`, avec le fichier `transport/onchain/ksp-onchain-transport.log`. La console et `file.all.info` restent également à `info`. Un développement/correctif futur peut relever temporairement uniquement ce target/sink via Config Desk conformément à `DEP-LOG-012`. ## 19. Architecture de tests ### 19.1 Unit tests Sous `unit_tests/` miroir de `src/` : - settings validation ; - URL redaction ; - endpoint/role/request-kind matching ; - priority/fairness/fallback ; - limiter RPS/burst ; - max concurrent ; - cooldown ; - retry/backoff borné ; - timeout ; - JSON-RPC request/success/error/id mismatch ; - RPC error mapping ; - method/request-form status ; - warning path deprecated/unstable ; - aucune fuite de secret dans `Debug`/snapshot. ### 19.2 Tests par méthode Chaque méthode de la matrice obtient dans sa release assignée au minimum : - sérialisation de request ; - désérialisation du succès ; - optional/null significatifs ; - erreur significative ; - configs/overloads pertinents. Fixtures JSON déterministes, pas d'Internet pour les tests par défaut. ### 19.3 Smoke réseau opt-in `0.2.1` peut valider sur Devnet, de manière ignorée/opt-in : ```text getHealth getVersion getGenesisHash getBalance ``` Le test obtient son endpoint depuis les mécanismes Config/fixtures de test ; Transport ne lit pas directement l'env. ### 19.4 Canaries architecturales Automatiser autant que possible : ```text Transport -X-> Config Transport -X-> Store Transport -X-> Program Transport -X-> tracing direct registre current == 52 noms audités registre deprecated historique == 14 noms audités 4 méthodes typed de 0.2.1 présentes aucune méthode reportée déclarée typed-complete avant sa release ``` La canary de méthode utilise un inventaire statique versionné/audité ; elle ne scrape pas Internet pendant `cargo test`. ## 20. Hors périmètre confirmé de la `0.2.1` réduite Explicitement reportés : - les 48 méthodes courantes non canari selon la matrice `0.2.2`–`0.2.4` ; - WebSocket Solana ; - LaserStream ; - Yellowstone gRPC ; - Wallet et Wallet Desk ; - Store/RAW persistence ; - Program/decoders/materializers ; - execution policy et orchestration métier ; - workers/jobs ; - Tauri Transport dédiée ; - off-chain/trading/ML. Le report des 48 méthodes n'est **pas une suppression de l'obligation exhaustive** : elles restent listées nom par nom dans ce plan et réservées à des releases déjà numérotées. ## 21. Critères de clôture de la `0.2.1` réduite La release peut devenir stable seulement si : - crate `ksp-onchain-transport-lib` propre ; - aucune dépendance vers Config/Store/Program/tracing direct ; - settings publics et validation documentés ; - JSON-RPC 2.0 et error mapping testés ; - registry 52 current + 14 historical exact ; - statuts centralisés et request-form deprecated supporté pour la foundation du mécanisme ; - pool/priority/fallback/limites/concurrence/timeout/retry testés ; - write retry policy inscrite dans les descriptors même si les méthodes write arrivent en `0.2.3` ; - snapshots/Debug ne révèlent pas les secrets ; - `getBalance`, `getGenesisHash`, `getHealth`, `getVersion` sont typés et testés ; - Config standard + schema + example + adapter fonctionnent dans le bon sens ; - README/USAGE expliquent API raw vs typed et limites de couverture de `0.2.1` ; - tests ciblés/workspace et cargo tree passent sur le poste de développement ; - prompt `0.2.2 — HTTP Accounts + Tokens + Cluster` prêt dans la dernière prerelease ; - aucune méthode de la matrice n'est perdue du planning HTTP global. ## 22. Prévision souple des prereleases de la `0.2.1` réduite | Tranche | Objectif | |-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `pre.001` | audit KSP + bot3 + docs officielles, matrice 52+14, architecture, split et sizing | | `pre.002` | **réalisé** : crate/workspace, codes erreur, settings/validation, JSON-RPC, descriptors/status, base Logging | | `pre.003` | **réalisé** : endpoint client + pool logique + rôles/capabilities + priorité/fairness/fallback + snapshots sûrs | | `pre.004` | **réalisé** : RPS/burst/concurrence/cooldown + deadline commune + retry/backoff + classification retry/no-resend | | `pre.005` | **réalisé** : exécution HTTP JSON-RPC + `getHealth`, `getVersion`, `getGenesisHash`, `getBalance` + fixtures déterministes + centralisation des canaries workspace dans Core | | `pre.006` | **réalisé** : `std.transport` schema/document/example + registry Config + adapter Config -> Transport + sensibilité/provenance/env tests | | `pre.007` | **réalisé côté livraison candidate** : completeness/canaries, smoke opt-in, README/USAGE, docs finales, prompt `0.2.2`, préparation `rel.001`; `cargo tree`/Cargo restent à valider opérateur | Ce découpage est révisable si une tranche dépasse le budget ; la release réduite, contrairement au scope initial, reste raisonnablement clôturable dans la session. ### 22.1 État après `0.2.1-pre.002` `pre.002` matérialise la fondation sans ouvrir le client/pool de `pre.003` : - nouvelle crate `ksp-onchain-transport-lib`, membre du workspace, `workspace.package.version = 0.2.1-pre.2` ; - dépendances directes : `ksp-core-lib`, `ksp-logging-lib`, `serde`, `serde_json`, `reqwest` sans features par défaut ; aucune dépendance Config/Store/Program/`tracing` directe ; - settings runtime publics : URL redacted, provider/cluster/role/request-kind ouverts, endpoints/rôles/limites/retry et validation structurelle ; - douze codes d'erreur `onchain_transport` réservés/stabilisés selon la stratégie du §14 ; - JSON-RPC HTTP KSP : request numérique KSP, sérialisation, parse success/error, validation `jsonrpc = 2.0`, id exact et exclusivité `result`/`error`, avec `null` résultat préservé ; - `Debug` des requests/réponses n'expose pas les params, résultats ou payloads d'erreur distants par défaut ; - registre central exact de 52 méthodes current + 14 historiques, avec catégorie, request kind, statut documentaire/runtime, forme legacy, operation kind, retry class, remplacement et release de couverture ; - `getTransaction` et `getBlock` portent explicitement `StableWithDeprecatedLegacy`; `sendTransaction` et `requestAirdrop` portent `WriteSubmission / NeverAfterDispatch`; - contrôle central `ensure_runtime_supported()` : warning KSP pour surfaces non stables supportées, warning + `method_removed` pour les historiques supprimées ; - 40 tests Rust ajoutés (unitaires + intégration), dont redaction, invariants JSON-RPC, matrice 4/22/11/15 et canary de firewall du manifest. Les éléments annoncés pour `pre.003` dans cet état historique sont désormais concrétisés au §22.2 ; rate limiting, concurrence et retry effectif restent reportés. ### 22.2 État après `0.2.1-pre.003` `pre.003` matérialise la première couche de routing HTTP sans encore exécuter de JSON-RPC : - `workspace.package.version = 0.2.1-pre.3` ; - `reqwest` reste `default-features = false` et active uniquement `rustls` pour rendre les endpoints HTTPS réellement constructibles ; - `HttpEndpointClient` encapsule un `reqwest::Client`, applique connect/request timeout et limite idle-per-host, désactive redirect et proxy système implicites, et n'expose jamais l'URL dans `Debug`/snapshot ; - `HttpEndpointSnapshot`, `HttpEndpointRoleSnapshot` et `HttpTransportPoolSnapshot` exposent uniquement identité/provider/cluster/routing/status sûrs ; - `HttpTransportPool` valide les settings puis construit un client logique par endpoint ; - sélection exacte rôle + capability, wildcard `*`, priorité globale croissante et round-robin par rôle/request-kind dans le meilleur tier ; - endpoint ou rôle disabled exclus ; un tier inférieur devient donc fallback lorsqu'aucun candidat du tier supérieur n'est sélectionnable ; - `select_for_method` dérive la capability depuis le registre RPC central et refuse une méthode historique `Removed` avant routing ; - aucune mutex synchrone ne couvre un `await` réseau : le seul verrou actuel protège brièvement les curseurs de fairness, avant toute I/O ; - le contrat d'availability réserve `Disabled`, `Available`, `Degraded`, `RateLimited`; `pre.003` ne produit que les deux premiers, les transitions runtime appartenant à `pre.004`. - la suite Transport compte désormais 53 tests déclarés, dont les nouvelles preuves de redaction client/pool, priorité, fairness, fallback, capability et contrat public du pool. Restent volontairement à `pre.004` : token bucket RPS/burst, semaphore de concurrence, cooldown/429, deadline effective, retry/backoff et mutations passives `Degraded/RateLimited`. ### 22.3 État après `0.2.1-pre.004` `pre.004` matérialise la résilience runtime du pool sans encore exécuter les quatre méthodes JSON-RPC canari : - `workspace.package.version = 0.2.1-pre.4` ; - limiteur token-bucket par couple endpoint/rôle avec RPS et burst ; lorsque RPS est configuré sans burst explicite, la capacité initiale dérive d'une seconde de RPS ; - semaphore Tokio par rôle pour `max_concurrent_requests`, avec permit détenu pendant la durée de l'admission/exécution et notification des waiters à la libération ; - cooldown par rôle après rate-limit provider, utilisant la valeur configurée ou un fallback runtime borné ; un `Retry-After` déjà résolu en durée peut prolonger ce cooldown dans une borne défensive ; - sélection runtime par priorité puis round-robin, avec tentative des pairs et tiers inférieurs lorsqu'un candidat est temporairement bloqué par RPS, cooldown ou concurrence ; - si tous les candidats sont temporairement bloqués, attente du premier signal/candidat admissible sans dépasser une deadline commune ; par défaut, cette deadline utilise le plus petit `request_timeout` des endpoints structurellement compatibles ; - snapshots sûrs enrichis avec availability de rôle, limites, requêtes en vol, cooldown restant et compteurs success/failure/rate-limit, sans URL ni secret ; - transitions passives `Available -> Degraded/RateLimited` sur observations runtime et retour `Degraded -> Available` après succès ; - policy centralisée `evaluate_transport_retry()` : backoff exponentiel borné, budget `max_retries`, causes transport explicitement retryables, RPC application errors hors retry, et respect strict de `TransportRetryClass::NeverAfterDispatch` pour empêcher un resend automatique après dispatch ambigu ; - après `pre.004-fix.001/.002`, les features d’usage sont activées localement par crate : Transport porte `reqwest/rustls`, `serde/derive`, `tokio/macros+sync+time` et `tokio/rt` seulement en dev ; le root conserve version/default-features sans activation de features d’usage ; - la suite Transport compte désormais **72 tests déclarés**, avec preuves supplémentaires sur limiter, concurrence, cooldown, deadline, fallback runtime, santé passive, policy de retry et consommation publique de l'admission async. La tranche ne parse pas encore elle-même le header HTTP `Retry-After` et n'exécute pas de POST JSON-RPC : ces signaux seront raccordés à l'exécuteur HTTP avec les méthodes canari de `pre.005`. Restent à `pre.005` : exécution HTTP JSON-RPC réelle et wrappers typés `getHealth`, `getVersion`, `getGenesisHash`, `getBalance`, avec fixtures déterministes et raccordement des erreurs HTTP/429/timeout à la résilience maintenant disponible. ### 22.4 État après `0.2.1-pre.005` `pre.005` matérialise la première exécution réseau HTTP complète et les quatre canaris typés de la release : - `workspace.package.version = 0.2.1-pre.5` ; - exécuteur générique `execute_standard_rpc` : allocation d'id JSON-RPC KSP, admission via pool, POST `application/json`, deadline commune, mapping connexion/timeout/status HTTP, parsing JSON-RPC et mise à jour de santé passive ; - HTTP `429` raccordé au cooldown de rôle et à la policy de retry avec parsing défensif de `Retry-After` sous forme delta-seconds ; statuts temporaires `408/500/502/503/504` raccordés au retry borné ; - les retries réacquièrent capacité RPS/concurrence et restent bornés par la même deadline ; la classification `NeverAfterDispatch` reste centralisée pour empêcher les futurs write/submission de se resoumettre après ambiguïté ; - wrappers typés `getHealth`, `getGenesisHash`, `getVersion`, `getBalance`, avec `ksp_core_lib::Pubkey`, commitment/minContextSlot optionnels, DTO de contexte/balance/version et validation de forme ; - fixtures JSON déterministes et serveur HTTP local de tests : les canaris prouvent le POST réel et les formes request/response ; tests supplémentaires sur récupération après 429 et timeout reqwest ; - les deux canaries de dépendances générales quittent `ksp-onchain-transport-lib/tests/dependency_boundary.rs` et sont centralisées sous `ksp-core-lib/tests/workspace_dependencies.rs`, conformément à `DEP-CARGO-007` ; - Transport compte désormais **78 tests déclarés** ; Core porte 2 canaries workspace supplémentaires dans sa surface d'intégration. `pre.005-fix.001` normalise ensuite le contrat de target KSP : Transport et Config possèdent désormais leur `TRACING_TARGET` principal dans `src/constants.rs`, les targets comportementaux ne dépendent plus de `env!("CARGO_PKG_NAME")` ni de littéraux dispersés, et une canarie workspace Core protège cette convention. L’usage Cargo de `CARGO_PKG_NAME` pour le User-Agent Transport reste volontaire et autorisé. Restent à `pre.006` : document/schema/exemple `std.transport`, enregistrement Config, adapter Config -> Transport et tests de sensibilité/provenance/env. ### 22.5 État après `0.2.1-pre.006` `pre.006` matérialise la frontière de configuration standard du Transport sans créer de dépendance inverse : - `workspace.package.version = 0.2.1-pre.6` ; - `config/std.transport.json` et `config/schemas/std.transport.schema.json` deviennent des ressources Config gérées, avec `cfg.std.transport` / `schema.std.transport` dans le registry ; - le document standard possède `retry` en global et des `endpoints` par profil ; `devnet_public` est le profil autonome par défaut et `mainnet_public` reste sélectionnable explicitement ; - `config/examples/std.transport.example.json` illustre un pool mixte public/privé, y compris une URL complète provenant d’un `KSP_SECRET_*` ; - `.env.example` inventorie les deux overrides publics standard et l’override secret d’exemple sans committer de credential réel ; - `ksp-config-lib` dépend désormais de `ksp-onchain-transport-lib` dans la direction autorisée Config -> Transport, tandis que le firewall Transport -> Config reste inchangé ; - `ConfigDocumentEngine::load_resolved_transport_config` sélectionne le profil, résout l’environnement, convertit les scalaires `*_ms` en `Duration`, construit les rôles/limites/endpoints puis délègue la validation finale à `HttpTransportSettings::validate()` ; - `ResolvedTransportConfig` conserve le profil, la provenance détaillée et la projection safe, et expose les settings runtime réels ; son `Debug` n’expose pas les URLs secrètes ; - les tests couvrent le document committé, les origines global/profile, la precedence process > `.env`, la provenance JSON Pointer, la redaction `KSP_SECRET_*` et l’échec d’une URL secrète invalide sans fuite de canary ; - `DEP-TRANSPORT-005` explicite maintenant que Config peut dépendre des crates Transport pour posséder les adapters, jamais l’inverse. `pre.006-fix.002`, déclenché par l’audit complet du workspace avant clôture, corrige une fuite diagnostique potentielle : les `reqwest::Error` attachées à `ksp_core_lib::Error` sont désormais neutralisées par `without_url()` avant `with_source`, avec une canarie timeout portant un secret dans la query de l’endpoint. Le même audit remet aussi en conformité les fins de ligne des fichiers courants concernés par `GEN-FILE-005` et corrige le rustdoc Transport devenu obsolète après l’introduction de l’adapter Config -> Transport. Les écarts purement documentaires sans impact code/config restent volontairement réservés à `pre.007`. ### 22.6 État candidat après `0.2.1-pre.007` `pre.007` ferme le périmètre de développement de la foundation sans ajouter de méthode HTTP typed au-delà des quatre canaris : - `workspace.package.version = 0.2.1-pre.7` ; - revérification officielle du 2026-08-17 : l’index Solana HTTP courant expose toujours 52 méthodes et la navigation Deprecated conserve les 14 noms historiques audités ; - deux canaries d’intégration publiques figent la partition `52 current / 14 historical / 4-22-11-15` et le set exact des quatre canaris `0.2.1` ; - la candidate déclare **80 tests Transport** et **114 tests Config**, dont un unique smoke Devnet Config `ignored` ; - un smoke Devnet `ignored` sous `ksp-config-lib` valide de manière opt-in la chaîne Config -> `devnet_public` -> Transport -> `getHealth/getGenesisHash/getVersion/getBalance`, sans lecture d’environnement dans Transport ; - `ksp-onchain-transport-lib/README.md` et `USAGE.md` documentent frontières, API raw vs typed, résilience, sécurité, Config et utilisation ; - `docs/validation/003-V0_2_1_ONCHAIN_HTTP.md` devient la matrice durable de clôture ; - le niveau de référence du sink Transport dédié revient de `debug` à `info` avant stable ; - les écarts documentaires résiduels issus de l’audit complet sont normalisés : convention `bindings/gen`, README Config et inventaire composant HTTP ; - `prompts/007-V0_2_2_START_PROMPT.md` prépare `0.2.2 — HTTP Accounts + Tokens + Cluster` avec un nouvel audit officiel et gate de sizing ; - `CHANGELOG.md` n’est pas encore modifié : l’entrée stable `0.2.1` appartient à `rel.001`, conformément au workflow des releases précédentes. Les validations Cargo et `cargo tree` de cette candidate doivent être exécutées sur le dépôt canonique. Si elles sont propres, `rel.001` reste strictement publicationnel : version stable, statuts ROADMAP/plan/validation, entrée CHANGELOG, delta de release, commit et tag `v0.2.1`. ## 23. Séquence `0.2.x` recalibrée ```text 0.2.1 HTTP transport foundation + 4 canaris 0.2.2 HTTP Accounts + Tokens + Cluster 0.2.3 HTTP Transactions 0.2.4 HTTP Blocks + Economics + compliance HTTP finale 0.2.5 wallet foundation (.kspwallet) 0.2.6 Wallet Desk 0.2.7 standard Solana WebSocket 0.2.8 Helius LaserStream WebSocket 0.2.9 Yellowstone gRPC standard foundation 0.2.10 off-chain price transport 0.2.11 price visualization desk 0.2.12 interface/wire foundation 0.2.13 program-api foundation ``` Le découpage `0.2.2`–`0.2.4` est un **maximum nominal de trois sessions**. Une session peut clôturer plusieurs releases successives si, après la clôture complète de chacune, le sizing de la suivante reste positif. Cette optimisation de session ne fusionne jamais les releases elles-mêmes. Chaque release HTTP `0.2.2`–`0.2.4` commence par une **revérification officielle temporelle** de sa famille et de l'index global. Si Solana ajoute/supprime/change une méthode entre-temps, la matrice est mise à jour explicitement plutôt que de conserver aveuglément l'état du 2026-08-17. ## 24. Gate de sizing — réponse obligatoire Question : ```text La release 0.2.1 peut-elle raisonnablement être entièrement clôturée dans cette session de chat avec des prereleases de ~15–20 minutes ? ``` Réponse pour le périmètre du prompt `006` : ```text NON. ``` Motif : 52 méthodes typed + 14 deprecated historiques + toute la foundation/résilience/Config/tests/docs constituent plusieurs releases cohérentes et non une seule release de session. Réponse après split, pour la `0.2.1` réduite définie par ce plan : ```text OUI, raisonnablement. ``` La grosse implémentation ne doit commencer qu'à `pre.002`, sur ce périmètre réduit. ## 25. Validations de `pre.001` Exécutées dans le sandbox : - lecture des sources internes obligatoires ; - inspection des manifests et contrats publics KSP ; - audit ciblé de l'archive bot3 et de `ks-onchain-transport`/Config historique ; - comparaison locale automatisée des 52 noms bot3 avec les 52 noms de l'index officiel audité : aucun manque/extra ; - consultation des sources officielles Solana/JSON-RPC/Agave ; - audit des versions candidates `reqwest`/`tokio` depuis leurs registres officiels ; - vérification de l'absence de `.git` dans l'archive KSP. Non exécutées : ```text cargo fmt --all cargo check --workspace cargo clippy --workspace --all-targets cargo test --workspace cargo tree ... ``` Le binaire `cargo` n'est pas disponible dans ce sandbox (`cargo: command not found`). Aucune réussite Cargo n'est donc déclarée. Aucune dépendance ni source Rust n'étant ajoutée dans `pre.001`, les `cargo tree` seront particulièrement utiles à `pre.002`, après ajout réel de `reqwest`.