# Validation `0.2.7` — WebSocket Solana standard > **Statut : matrice active, `0.2.7-pre.004`.** Settings/lifecycle `pre.002`, Config V2 `pre.003` et première session physique actor-owned `pre.004` sont matérialisés. Registry subscriptions, shutdown complet, reconnect et wrappers typed restent ouverts. ## 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. Baselines Git Agave et SIMD ### 4.1 Hiérarchie de contrôle La compliance applique le même modèle que la compliance HTTP finale : ```text documentation Solana actuelle -> surface publique annoncée Git Agave tag courant audité v4.2.1 -> implémentation réelle et types wire SIMD main -> évolutions Activated, Review, Draft ou Idea à surveiller ``` Les pages Solana peuvent lier une révision Agave plus ancienne. KSP ne confond donc pas le lien source embarqué dans une page documentaire avec la baseline Git courante. Au 2026-08-22, **Agave `v4.2.1`** est disponible sur Git et devient la baseline d'implémentation de `0.2.7`. Sources Agave ciblées : ```text https://github.com/anza-xyz/agave/blob/v4.2.1/rpc/src/rpc_pubsub.rs https://github.com/anza-xyz/agave/blob/v4.2.1/rpc/src/rpc_subscriptions.rs https://github.com/anza-xyz/agave/blob/v4.2.1/rpc-client-types/src/response.rs ``` ### 4.2 Constats Agave `v4.2.1` Le cross-check du tag courant confirme les **9 subscribe + 9 unsubscribe** déjà inventoriés par la documentation publique et ne révèle aucune opération PubSub standard supplémentaire. Constats ciblés : - `accountSubscribe` : le type partagé `RpcAccountInfoConfig` expose `min_context_slot`, mais le handler PubSub `v4.2.1` le destructure en `_ // ignored`. KSP ne le compte donc pas comme option WebSocket effective ; - `programSubscribe` : `RpcProgramAccountsConfig.with_context` est lu et doit rester dans le contrat KSP ; la tolérance contexted/non-contexted reste requise pour ne pas convertir les divergences docs/source/provider en perte wire ; - `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`, donc optionnel au niveau wire ; - `slotsUpdatesSubscribe` : `SlotUpdate` possède toujours les sept variantes courantes `FirstShredReceived`, `Completed`, `CreatedBank`, `Frozen`, `Dead`, `OptimisticConfirmation` et `Root`. Décisions compliance : transmettre `programSubscribe.withContext`, accepter les formes contextée/non-contextée, ne pas promettre `accountSubscribe.minContextSlot` tant qu'il est ignoré upstream, conserver `vote.timestamp` optionnel et garder un fallback borné pour toute future variante `SlotUpdate` inconnue. ### 4.3 Audit SIMD ciblé Aucun SIMD audité n'ajoute actuellement une dixième famille de subscription standard à la surface Agave `v4.2.1`. Ils modifient en revanche des hypothèses que KSP doit éviter de figer. | SIMD | Statut au 2026-08-22 | Conséquence WebSocket KSP | |-----------------------------------------------|----------------------|----------------------------------------------------------------------------------------------------------------------------------------------| | `0118` Partitioned Epoch Rewards Distribution | Activated | conserver les DTOs bloc/rewards HTTP déjà lossless, dont `numRewardPartitions` omitted, `null` ou valeur | | `0291` Commission Rate in Basis Points | Review | conserver les représentations de commission upstream indépendantes lorsqu'elles sont présentes | | `0296` Larger Transaction Size | Review | proposition jusqu'à 4096 octets : ne pas déduire les limites WS de l'ancienne taille transaction 1232 ; surveiller `blockSubscribe` | | `0298` Bank Hash in Block Footer | Idea | aucun `bankHash` spéculatif dans le wire courant ; surveiller le DTO bloc partagé | | `0301` parent bank hash | PR fermé, non mergé | aucun `parentBankHash` spéculatif ; même conclusion que la compliance HTTP | | `0307` Add Block Footer | Review | aucun `footer` spéculatif ; réauditer `blockSubscribe` lorsque l'upstream l'expose réellement | | `0326` Alpenglow | Review | ne pas figer les sémantiques TowerBFT de `voteSubscribe` ou `optimisticConfirmation` dans un contrat durable | | `0337` Alpenglow Fast Leader Handover Markers | Review | nouveaux block markers futurs : surveiller shape et taille, sans ajout WS anticipé | | `0384` Alpenglow migration | Review | des notifications RPC commitment/optimistic confirmation peuvent être suspendues pendant migration ; aucune hypothèse de séquence exhaustive | | `0385` Transaction V1 | Review | `maxSupportedTransactionVersion` et les versions transaction restent génériques ; ne pas caper KSP à v0 | Sources SIMD : ```text https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0118-partitioned-epoch-reward-distribution.md https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0291-commission-rate-in-basis-points.md https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0296-larger-transactions.md https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0298-bank-hash-in-block-footer.md https://github.com/solana-foundation/solana-improvement-documents/pull/301 https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0307-add-block-footer.md https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0326-alpenglow.md https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0337-parent-ready-update-marker.md https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0384-alpenglow-migration.md https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0385-transaction-v1.md ``` Les limites WebSocket sont donc des bornes KSP configurables et finies, jamais une transposition en dur de l'ancienne limite transaction de 1232 octets. ## 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 | **Done `pre.004`**, canary final `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 | **Done `pre.002`** | | subscription states | Requested/Active/Resubscribing/Cancelling/Closed/Failed | **Done `pre.002`** | | reconnect | physique uniquement, budget/backoff finis | `pre.007` | | resubscribe | policy `Never` ou `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 | **Done `pre.002`** | | URL/credentials absents des erreurs | validation URL + connection errors safe | **Done through `pre.004`** | | URL/credentials absents des logs | actor logs only safe endpoint metadata | Partial `pre.004`, capture finale future | | snapshots sans URL/raw payload | unit shape + public contract | **Done `pre.002`** | | frame/message finis | settings bornés + `WebSocketConfig` raccordé | Partial `pre.004`, adversarial `pre.005` | | JSON borné indirectement par message | oversized + malformed fixture | Planned | | queues notifications bornées | capacité settings, channel futur | Partial `pre.002` | | pending RPC borné + timeout | map actor bornée + timeout request | **Done `pre.004`** | | 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 + source audit | **Done `pre.002`** | ## 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`. `pre.004` matérialise `tokio-tungstenite` et `futures-util` dans `[workspace.dependencies]` sans features consumer au root. Transport active seulement `connect`, `rustls-tls-webpki-roots`, `std` et `sink`; `tokio/net` est ajouté côté dev fixture local. ## 9. Config compliance initiale V1 hérité : HTTP-only strict. Contrat de compatibilité : ```text V1 -> support de lecture conservé, WS vide V2 -> HTTP existant + ws_defaults + profiles[].ws_endpoints profiles[].ws_endpoints[].kind -> discriminateur de famille/protocole WS 0.2.7 -> seule valeur supportée : solana_standard Config -> WsTransportSettings Transport -X-> Config ``` Le type Transport correspondant au discriminateur est prévu `#[non_exhaustive]`. L'objectif est de pouvoir ajouter ultérieurement une famille telle que Helius Enhanced WebSocket sans créer un nouveau conteneur Config ni injecter des options provider-specific dans les settings Solana standard. Toute valeur inconnue reste explicitement rejetée en `0.2.7`; aucun paramètre Helius n'est implémenté par anticipation. `pre.003` remplace le schema enregistré par `urn:ksp:schema:std.transport:v2`, avec deux branches strictes : V1 HTTP-only et V2 HTTP + WebSocket. La compatibilité V1 ne relâche donc ni `additionalProperties`, ni la shape historique. Preuves `pre.003` : ```text config/std.transport.json format_version = 2 config/examples/std.transport.example.json format_version = 2 V1 fixture dédiée load + HTTP mapping OK attendu V1 ws_settings = None V2 ws_settings = Some(validated) ws_defaults globals profiles[].ws_endpoints profile-local ws_endpoints[].kind enum schema solana_standard ws_endpoints[].session overrides génériques optionnels Config -> Transport seule direction de dépendance ``` `ResolvedTransportConfig::settings()` reste l'accesseur HTTP historique. `http_settings()` l'explicite et `ws_settings()` expose `Option<&WsTransportSettings>` afin que le V1 backward ne force jamais un faux `WsTransportSettings` vide. `into_transport_settings()` permet de consommer les deux contrats ensemble. ## 9.1 Checkpoint settings/lifecycle `pre.002` Surface publique matérialisée : ```text WsEndpointUrl WsProviderName WsClusterName WsProtocolKind::SolanaStandard WsReconnectSettings WsResubscribePolicy::{Never, ActiveSubscriptions} WsSessionSettings WsEndpointSettings WsTransportSettings WsSessionId WsSubscriptionId WsSessionState WsSubscriptionState WsSubscriptionKind WsSessionSnapshot WsSubscriptionSnapshot ``` Gates déterministes ajoutés : ```text ws:// et wss:// acceptés HTTP rejeté par WsEndpointUrl Debug URL redacted erreur de scheme sans URL/credential settings session default bornés zero runtime bound rejeté reconnect backoff inversé rejeté endpoint names uniques au moins un endpoint enabled Debug WsTransportSettings sans URL/credential 9 familles standard WsSubscriptionKind snapshots sans URL ni remote subscription id public API canary crate-root ``` Logging : `TRACING_TARGET` reste défini dans `constants.rs`; les nouveaux événements utilisent exclusivement `ksp_logging_lib::trace!`, `debug!` et `warn!` avec des fields sûrs. Aucun `tracing` direct n'est ajouté. Les defaults de taille/queue sont des **policies KSP locales**, pas des limites Solana. Leur enforcement réel et leurs tests oversized/slow-consumer restent attendus dans les tranches socket/backpressure. ## 9.2 Checkpoint runtime physique `pre.004` Surface matérialisée : ```text WsSession::connect(endpoint) public, une connexion physique par appel actor socket propriétaire exclusif du WebSocket command queue tokio mpsc bounded pending JSON-RPC BTreeMap bounded par max_pending_requests request timeout command_timeout response dispatch par id numérique KSP state snapshot watch + WsSessionSnapshot raw JSON-RPC public non, primitive pub(crate) reconnect/resubscribe non, gates futurs ``` Fixtures déterministes `pre.004` : ```text handshake local + JSON-RPC round-trip deux sessions physiques distinctes sur la même URL deux requests concurrentes + réponses inversées erreur RPC applicative sans teardown de session erreur de connexion sans URL/credential dans Error Debug ``` Les limites `max_message_size`, `max_frame_size` et `max_write_buffer_size` sont raccordées à `tungstenite::WebSocketConfig`. Les fixtures oversized et le shutdown/control-frame hostile restent explicitement `pre.005`. ## 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 cross-check Git Agave v4.2.1 OK audit SIMD WebSocket ciblé OK inventaire exact 18 = 9+9 OK audit dependencies candidates OK state machines / reconnect / resubscribe DECIDED backpressure / cancellation / shutdown / secrets DECIDED shape Config V2 + discriminateur famille WS 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 (**Done `pre.003`**, compilation opérateur requise) smoke live opt-in documenté cargo tree inspecté cargo test --workspace vert README/USAGE synchronisés prompt 0.2.8 préparé ```