# Validation `0.2.9` — Yellowstone gRPC standard + PublicNode **Statut courant : la validation technique `pre.013-fix.004` est fermée et verte : `clippy --all-targets`, workspace, graphes Cargo, puis smoke PublicNode authentifié Mainnet + Testnet `2/2 PASS`. `pre.014` réconcilie et ferme cette matrice documentaire ; seule la tranche `pre.015` de préparation de publication restera avant `rel.001`.** ## 1. Autorités et baseline courante Autorités : ```text RULES.md + docs/rules/* plan 016 courant code réel Transport/Config prompts/014-V0_2_9_START_PROMPT.md pour les contraintes non supersédées deltas/0.2.9/* pour l'historique immuable et les décisions postérieures au prompt upstream Yellowstone primaire ``` Baseline opérateur immédiatement avant `pre.012` (`0.2.9-pre.011`) : | Gate | Résultat | |------------------------------------------|--------------------------| | `cargo fmt --all` | PASS | | audit Rust workspace | PASS, 0 candidate export | | `cargo check --workspace` | PASS | | `cargo clippy --workspace --all-targets` | PASS sans warning | | Config unit | 113/113 PASS | | Config public API | 15/15 PASS | | Config ownership | 5/5 PASS | | Transport unit | 383/383 PASS | | Transport public API | 49/49 PASS | | Transport release completeness | 43/43 PASS | | Transport doctests | 4/4 PASS | | workspace dependency canary | 3/3 PASS | | `cargo test --workspace` | PASS | Cette baseline est le seuil de non-régression de `pre.012`. ## 2. Gate dépendances et licence | Exigence | Décision / preuve | État | |--------------------------------|------------------------------------------|----------| | proto officiel sans vendoring | `yellowstone-grpc-proto ^12.6` | **PASS** | | pas de client upstream runtime | `yellowstone-grpc-client` absent | **PASS** | | runtime Tonic KSP-owned | `tonic ^0.14` + `tonic-prost ^0.14` | **PASS** | | proto subtree compatible | Apache-2.0 selon `LICENSING.md` upstream | **PASS** | | source AGPL copiée | aucune | **PASS** | | raw client Tonic public | aucun | **PASS** | | graph final | graphes `pre.013` inspectés | **PASS** | Le graphe de dépendances n'est pas modifié par `pre.012`; aucune nouvelle dépendance Cargo n'est introduite dans cette tranche. ## 3. Matrice service `Geyser` | RPC | Classification | Cible | Preuve | Verdict | |-----------------------|--------------------------------|-------|---------------------------------|----------| | `Subscribe` | standard | oui | stream bidi local + lifecycle | **PASS** | | `SubscribeDeshred` | extension/pré-exécution Triton | non | exclusion documentée | **OUT** | | `SubscribeReplayInfo` | standard unary | oui | fixture unary + reconnect tests | **PASS** | | `Ping` | standard unary | oui | fixture exact echo | **PASS** | | `GetLatestBlockhash` | standard unary | oui | fixture typed | **PASS** | | `GetBlockHeight` | standard unary | oui | fixture typed | **PASS** | | `GetSlot` | standard unary | oui | fixture typed | **PASS** | | `IsBlockhashValid` | standard unary | oui | fixture typed + invalid input | **PASS** | | `GetVersion` | standard unary | oui | fixture typed | **PASS** | ## 4. `SubscribeRequest` — coverage normative ### 4.1 Top-level | Champ | Verdict | Preuve minimale | |-----------------------|----------|----------------------------------| | `accounts` | **PASS** | maps typed + wire exact | | `slots` | **PASS** | maps typed + wire exact | | `transactions` | **PASS** | maps typed + wire exact | | `transactions_status` | **PASS** | same filter family, separate map | | `blocks` | **PASS** | maps typed + wire exact | | `blocks_meta` | **PASS** | named empty marker preserved | | `entry` | **PASS** | named empty marker preserved | | `commitment` | **PASS** | optional wire semantics | | `accounts_data_slice` | **PASS** | order + bounds | | `ping` | **PASS** | optional id | | `from_slot` | **PASS** | optional + replay mutation | ### 4.2 Accounts ```text account[] owner[] filters[] nonempty_txn_signature? cuckoo_accounts_filter? memcmp bytes/base58/base64 datasize token_account_state lamports eq/ne/lt/gt ``` Verdict : **PASS** — wire complet retenu, bounds déterministes, payload Debug redacted, malformed fixed-width rejeté. ### 4.3 Slots ```text filter_by_commitment? interslot_updates? processed confirmed finalized first_shred_received completed created_bank dead + dead_error? ``` Verdict : **PASS**. ### 4.4 Transactions et transaction_status ```text vote? failed? signature? account_include[] account_exclude[] account_required[] cuckoo_account_include? token_accounts? = ALL | BALANCE_CHANGED ``` Verdict : **PASS** — request wire, transaction storage/meta, status error et malformed signature couverts. ### 4.5 Blocks, block_meta, entry ```text account_include[] include_transactions? include_accounts? include_entries? cuckoo_account_include? blocks_meta marker entry marker ``` Verdict : **PASS** — block complet, metadata, entries et champs optional/legacy couverts. ## 5. `SubscribeUpdate` — coverage normative | Variante | Verdict | |--------------------------------------|----------| | account | **PASS** | | slot | **PASS** | | transaction | **PASS** | | transaction_status | **PASS** | | block | **PASS** | | ping | **PASS** | | pong | **PASS** | | block_meta | **PASS** | | entry | **PASS** | | top-level `filters[]` / `created_at` | **PASS** | Les raw protobufs ne sortent pas de la façade publique. Les décodeurs rejettent les formes structurellement invalides sans copier des payloads arbitraires dans les erreurs. ## 6. Settings, bounds et redaction | Contrôle | État | |-----------------------------------------------|--------------------| | URL `http/https` seulement et longueur bornée | **PASS** | | URL Debug redacted | **PASS** | | metadata key/value/count bornés | **PASS** | | metadata secret/public distincte | **PASS** Transport | | timeouts non nuls et bornés | **PASS** | | reconnect bounds cohérents | **PASS** | | inbound/outbound message sizes | **PASS** | | request/update queue capacities | **PASS** | | filter group/name bounds | **PASS** | | account/owner/memcmp/data slice bounds | **PASS** | | transaction/block selectors bounds | **PASS** | | remote `Status` message/details non recopiés | **PASS** | | Transport -> env/config | absent ou **PASS** | ## 7. Lifecycle, backpressure et replay | Exigence | Verdict | Preuve | |------------------------------|-----------------|------------------------------------------------| | stream bidi unique | **PASS** | round-trip fixture | | mutation request | **PASS** | same request channel | | server Ping / client reply | **PASS** | actor test | | Pong decode | **PASS** | update decode | | server half-close | **PASS** | state terminal observable | | explicit client close | **PASS** | half-close avant deadline | | hostile server shutdown | **PASS** | close timeout borné | | slow receiver | **PASS** | overflow terminal observable | | oversized inbound | **PASS** | Tonic decoder bound | | oversized outbound | **PASS** | reject avant queue dispatch | | reconnect backoff | **PASS** | budget borné | | shutdown pendant backoff | **PASS** | interruption sans nouvelle connexion | | resubscribe déterministe | **PASS** | last accepted full request | | reprise `from_slot` | **PASS** | dernier slot observé + demande explicite | | ReplayInfo `first_available` | **PASS** | clamp/gap seulement si prouvé | | duplicate observability | **PASS** | compteur borné, pas de suppression silencieuse | | exactly-once/lossless | **NON GARANTI** | contrat explicite | Snapshot public requis et présent : ```text reconnect_count replay_attempt_count continuity_gap_count duplicate_update_count last_requested_from_slot last_observed_slot state/error code safe ``` ## 8. Gate Config V3 — `pre.011` ### 8.1 Schema et backward compatibility Le gate opérateur `pre.011` ferme les exigences suivantes : | Exigence | Preuve | Verdict | |------------------------------------------|-----------------------------------------|----------| | schema `$id` V3 | `urn:ksp:schema:std.transport:v3` | **PASS** | | branches V1/V2 conservées | fixtures V1, V2 et document committé | **PASS** | | V1 HTTP-only | `v1_transport_fixture...` | **PASS** | | V2 HTTP+WS | fixture V2 + compatibilité constructeur | **PASS** | | V3 HTTP+WS+gRPC | 113 tests Config | **PASS** | | `grpc_endpoints` optionnel par profil V3 | profil `devnet_public` sans gRPC | **PASS** | | propriétés inconnues refusées | branches schema strictes | **PASS** | ### 8.2 Mapping runtime V3 doit mapper : ```text grpc_defaults -> YellowstoneGrpcSessionSettings grpc_endpoints[].url -> YellowstoneGrpcEndpointUrl provider -> YellowstoneGrpcProviderName cluster -> YellowstoneGrpcClusterName protocol = solana_yellowstone -> gate Config explicite metadata -> YellowstoneGrpcMetadataEntry::public secret_metadata -> YellowstoneGrpcMetadataEntry::secret session overrides -> merge avec grpc_defaults ``` API : | Surface | Exigence | Statut source | |---------------------------------|-------------------------------------|---------------| | `http_settings()` | inchangée | **OK** | | `ws_settings()` | V1 None, V2/V3 selon profil | **OK** | | `grpc_settings()` | V1/V2 None, V3 optionnel | **PASS** | | `into_transport_settings()` | tuple historique HTTP + WS inchangé | **PASS** | | `into_all_transport_settings()` | nouveau tuple HTTP + WS + gRPC | **PASS** | ### 8.3 Provenance et secrets Règles validées : ```text metadata publique + provenance KSP_SECRET_* -> reject secret_metadata sans provenance secret -> reject secret_metadata + variable KSP_PUBLIC_/KSP_* -> reject secret_metadata + KSP_SECRET_/KSPB_SECRET_ -> accept segments littéraux autour du secret -> accept safe_value -> secret segment ******** Transport Debug -> URL/metadata secret absents ``` Les tests V3 démontrent le cas valide et les deux croisements invalides sans exposer les canaris. ### 8.4 PublicNode Mainnet Profil committé et validé : ```text profile_id = publicnode_mainnet provider = publicnode cluster = mainnet-beta protocol = solana_yellowstone url = https://solana-yellowstone-grpc.publicnode.com:443 secret_metadata = x-token <- ${KSP_SECRET_PUBLICNODE_MAINNET_GRPC_X_TOKEN} ``` Le mapping produit un endpoint TLS Yellowstone standard et une metadata secrète redacted. Le smoke final ouvre `Subscribe`, reçoit un update `Slot` non nul et ferme de manière bornée : **PASS**. ### 8.5 PublicNode Testnet Profil committé et validé : ```text profile_id = publicnode_testnet provider = publicnode cluster = testnet protocol = solana_yellowstone url = https://solana-testnet-yellowstone-grpc.publicnode.com:443 secret_metadata = x-token <- ${KSP_SECRET_PUBLICNODE_TESTNET_GRPC_X_TOKEN} ``` Le hostname exact a été fourni par l'opérateur puis validé par le smoke live. `Subscribe` reçoit un update `Slot` non nul : **PASS**. Le même personal token opérateur a été validé sur les deux réseaux. Les deux variables KSP restent distinctes afin de permettre des valeurs différentes si la policy provider change ; cette modélisation ne constitue pas une preuve que les tokens PublicNode sont network-scoped. ## 9. Provider-neutrality | Point | Verdict | |-----------------------------------------------|------------------| | type public `PublicNodeGrpc*` sans divergence | absent, **PASS** | | protocol standard encodé comme provider | non, **PASS** | | provider descriptif séparé | oui, **PASS** | | auth PublicNode hardcodée dans Transport | non, **PASS** | | Helius/OrbitFlare runtime gRPC dans `0.2.9` | non, **PASS** | | provider extensions dans N2 | aucune, **PASS** | ## 10. Non-régressions obligatoires Le gate final doit préserver : ```text HTTP current typed 52/52 HTTP historical 14/14 Deprecated/Removed KSP-TRANSPORT-007 vert Standard WebSocket 9 familles / 18 opérations Helius LaserStream WebSocket 7 familles standard + transaction + slotsUpdates, heartbeat provider-owned Transport -> Config interdit Transport -> std::env KSP_* interdit tracing direct Transport interdit ``` La baseline `pre.011` reste conservée. Les changements `pre.013` ont porté uniquement le gate live, la Config PublicNode/Testnet et le harness de smoke ; le gate final `pre.013-fix.004` confirme les non-régressions workspace. ## 11. Historique des gates fermé Les détails de commandes, warnings corrigés et fichiers exacts restent dans leurs deltas immuables. | Tranche | Gate consolidé | |-----------------------------------|---------------------------------------| | `pre.001` + `fix.001` + `fix.002` | audit/sizing/providers/licence fermé | | `pre.002` + `fix.001` + `fix.002` | moteur/settings/channel fermé | | `pre.003` + `fix.001` | TLS/metadata/unary fermé | | `pre.004` + `fix.001` | Subscribe common fermé | | `pre.005` + `fix.001` | Accounts/Slots fermé | | `pre.006` | namespace HTTP fermé | | `pre.007` | Transactions fermé | | `pre.008` + `fix.001` | Blocks fermé | | `pre.009` + `fix.001` | bidi/backpressure fermé | | `pre.010` + `fix.001` | reconnect/replay fermé sans warning | | `pre.011` | Config V3/PublicNode mapping fermé | | `pre.012` + `fix.001` | réaudit + règles de fermeture | | `pre.013` + `fix.001`–`fix.004` | live PublicNode + graphes fermé | | `pre.014` | réconciliation documentaire candidate | Cette table remplace les anciens appendices numérotés successivement `19.x`, `20`, `21`, etc. qui rendaient le document ambigu. ## 12. Verdict opérateur consolidé jusqu'à `pre.013-fix.004` Le gate technique final reçu le 2026-08-24 est : | Exigence | Résultat opérateur | |-----------------------------------------------|--------------------------------------| | `cargo fmt --all` | **PASS** | | audit Rust workspace | **PASS, 0 candidate export** | | audit Markdown | **PASS, 87 tableaux / 258 fichiers** | | `cargo check --workspace` | **PASS** | | `cargo clippy --workspace --all-targets` | **PASS sans warning** | | Transport unit | **383/383 PASS** | | Transport public API | **49/49 PASS** | | Transport release completeness | **43/43 PASS** | | Transport doctests | **4/4 PASS** | | Config unit dans workspace | **113/113 PASS** | | Config ownership dans workspace | **5/5 PASS** | | Config public API dans workspace | **15/15 PASS** | | workspace dependencies | **3/3 PASS** | | `cargo test --workspace` | **PASS** | | graphes Cargo finaux | **PASS / inspectés** | | smoke PublicNode Mainnet `Subscribe` + `Slot` | **PASS** | | smoke PublicNode Testnet `Subscribe` + `Slot` | **PASS** | | smoke live total | **2/2 PASS** | Les graphes résolus confirment la stack attendue : ```text yellowstone-grpc-proto 12.6.0 tonic 0.14.6 tonic-prost 0.14.6 prost 0.14.4 prost-types 0.14.4 yellowstone-grpc-client absent du runtime KSP ``` Les graphes ont été exécutés avant `pre.013-fix.004`; ils restent valides car `fix.004` n'a modifié ni dépendance ni feature Cargo. ## 13. Verdict PublicNode live final Les essais intermédiaires ont établi deux faits utiles : les appels sans credential peuvent atteindre le service mais recevoir `PERMISSION_DENIED`, et la validation représentative de la foundation doit porter sur `Subscribe` plutôt que sur une unary provider éventuellement restreinte. Le smoke final : ```text reçoit deux personal tokens par stdin, Mainnet puis Testnet accepte que les deux lignes contiennent la même valeur construit une metadata secrète x-token ouvre TLS puis Subscribe filtre slots attend un YellowstoneSubscribeUpdate::Slot sous timeout exige slot > 0 ferme de manière bornée n'expose aucun secret dans URL/Debug/arguments ``` Résultat opérateur final : **Mainnet PASS, Testnet PASS, 2/2**. Le timeout KSP de half-close est un résultat accepté par le harness uniquement après réception prouvée d'un slot ; le runtime conserve son contrat de timeout explicite. ## 14. Gate `pre.014` — réconciliation documentaire Cette tranche ne modifie ni runtime, ni Config, ni schema, ni smoke. Elle synchronise le plan, la présente validation, README/USAGE Transport et les références durables avec les faits exécutés en `pre.013`. Le verdict technique de `0.2.9` est donc **PASS**. Après le gate documentaire `pre.014`, seuls le prompt suivant, `CHANGELOG.md` et `ROADMAP.md` restent à finaliser en `pre.015`. ## 15. Gate `pre.015` — préparation de publication Payload fonctionnel autorisé : ```text prompts/015-V0_2_10_START_PROMPT.md CHANGELOG.md ROADMAP.md ``` Seuls les fichiers mécaniques de version et de traçabilité (`Cargo.toml`, delta `pre.015`) peuvent s'ajouter à ce payload. Aucun README, USAGE, plan, validation, règle, code, test, schema ou config ne doit être corrigé dans `pre.015`. Si un tel défaut est découvert, une nouvelle prerelease dédiée est ouverte puis la préparation de publication est rejouée sous un nouveau numéro. ## 16. Verdict stable attendu avant `rel.001` `0.2.9` peut passer à `rel.001` seulement après la séquence suivante : ```text pre.013 technique/live fermé pre.014 réconciliation documentaire fermée pre.015 prompt/CHANGELOG/ROADMAP fermé ``` Et si tous les invariants suivants sont vrais : ```text standard vs extension explicitement classifié SubscribeDeshred OUT documenté 7 unary verts Subscribe + 9 updates verts resource/backpressure/lifecycle verts reconnect/replay sans promesse lossless Config V3 backward V1/V2 provider/protocol distincts PublicNode Mainnet live Subscribe + Slot PASS PublicNode Testnet live Subscribe + Slot PASS x-token secret requis par les profils PublicNode, aucun secret versionné HTTP/WS/Helius non régressés dependency firewall vert cargo graphs inspectés README/USAGE réconciliés avant la dernière pre validation 012 fermée avant la dernière pre prompt/CHANGELOG/ROADMAP seuls dans la dernière pre workspace final vert ```