Files
khadhroony-solana-project/docs/plans/016-V0_2_9_YELLOWSTONE_GRPC_PLAN.md
2026-08-25 05:44:41 +02:00

32 KiB
Raw Blame History

Plan 0.2.9 — moteur Yellowstone gRPC + standard Solana + PublicNode

Statut courant : le couloir technique 0.2.9-pre.013 est fermé par pre.013-fix.004 : gates déterministes et workspace verts, graphes Cargo finaux inspectés, et smoke Yellowstone PublicNode authentifié exécuté avec succès sur Mainnet et Testnet (2/2 PASS). pre.014 est la réconciliation documentaire finale ; seule pre.015 restera ensuite pour le prompt 0.2.10, CHANGELOG.md et ROADMAP.md, avant rel.001.

1. Objet et autorité de la release

Base stable d'ouverture :

v0.2.8

Release :

0.2.9 — Yellowstone gRPC standard/provider-neutral

Le gate pre.001, ses fixes, les deltas techniques suivants et le code réel ont affiné le prompt de démarrage. L'ordre d'autorité utilisé pendant la release est :

règles normatives KSP
code et fichiers réellement livrés
nouveaux deltas immuables de 0.2.9
décisions courantes consolidées dans ce plan et la validation 012
prompt de démarrage pour les contraintes qui n'ont pas été explicitement supersédées

Les deltas historiques ne sont jamais réécrits pour refléter une décision ultérieure.

2. Résultat final attendu

0.2.9 doit fermer une première fondation Yellowstone gRPC exploitable sans devenir un SDK fournisseur :

backend gRPC distinct de HTTP et WebSocket
moteur Tonic/Protobuf privé dans ksp-onchain-transport-lib
façade Yellowstone standard provider-neutral
7 unary standard retenus
Subscribe standard avec les familles/accounts/slots/transactions/blocks retenues
9 variantes SubscribeUpdate courantes
stream bidirectionnel borné
backpressure, half-close et shutdown déterministes
reconnect KSP-owned et replay/from_slot prudent
aucune promesse exactly-once/lossless non prouvée
Config Transport V3 backward-readable V1/V2
provider et protocol distincts dans Config
première intégration PublicNode strictement standard
smoke live opt-in avec credential secret non versionné
non-régressions HTTP, WebSocket standard et Helius WebSocket

3. Scope fermé par pre.001

3.1 Inclus

N1 — moteur Yellowstone gRPC
N2 — standard Solana Yellowstone
N3 — première intégration PublicNode quand elle réutilise le standard sans divergence wire
TLS et metadata provider-neutral
7 unary standards retenus
Subscribe standard retenu
reconnect/replay observables mais non lossless
Config V3 si le mapping reste Config -> Transport
smokes PublicNode architecture-safe

3.2 Hors scope 0.2.9

SubscribeDeshred / pré-exécution
extensions Triton spécifiques
adapter Helius LaserStream gRPC spécifique
adapter OrbitFlare spécifique
pool/scheduler automatique complexe de sessions gRPC
serveur Geyser/plugin validator
Store/persistence/backfill historique
workers/jobs d'acquisition
replay lossless garanti
refonte HTTP ou WebSocket

Les intégrations provider futures ne dupliquent jamais le moteur N1. Une façade provider n'existe que si elle porte une divergence réelle : auth, capabilities, restriction, extension wire ou policy lifecycle.

4. Audit upstream et dépendances retenues

4.1 Snapshot normatif et réaudit final

Le gate d'ouverture puis le réaudit final du 2026-08-24 confirment que les numéros du plugin, du client, du proto et des releases GitHub évoluent indépendamment. Il ne faut donc pas transformer un numéro de master en numéro de release publié.

État observé au réaudit final :

latest release GitHub indexée         v14.2.2+solana.4.1.0 — 2026-07-27
master yellowstone-grpc-geyser        15.1.2
master yellowstone-grpc-client        13.3.0
master yellowstone-grpc-proto         12.6.0
master Agave Geyser / storage         4.2.0
master prost / prost-types / tonic    0.14.x
KSP yellowstone-grpc-proto direct     ^12.6

Le service Geyser, la shape de SubscribeRequest et les neuf variantes SubscribeUpdate retenues par KSP n'ont pas changé matériellement par rapport au gate pre.001. SubscribeDeshred reste explicitement hors scope 0.2.9.

Les sources primaires restent :

https://github.com/rpcpool/yellowstone-grpc/releases
https://github.com/rpcpool/yellowstone-grpc/blob/master/CHANGELOG.md
https://github.com/rpcpool/yellowstone-grpc/blob/master/yellowstone-grpc-proto/proto/geyser.proto
https://github.com/rpcpool/yellowstone-grpc/blob/master/yellowstone-grpc-proto/proto/solana-storage.proto
https://github.com/rpcpool/yellowstone-grpc/blob/master/LICENSING.md
https://docs.rs/crate/yellowstone-grpc-proto/latest
https://docs.rs/crate/yellowstone-grpc-client/latest

Le réaudit final a été exécuté en pre.012; aucun changement upstream matériel na remis en cause la surface retenue.

4.2 Licence

Le repository upstream est globalement AGPL-3.0-only, mais LICENSING.md affecte explicitement Apache-2.0 à :

examples/
yellowstone-grpc-client/
yellowstone-grpc-client-nodejs/
yellowstone-grpc-proto/

Décision :

dépendance publiée yellowstone-grpc-proto = acceptée
.proto vendored dans KSP                      = non
source provenant des zones AGPL              = non copiée
future copie upstream                         = nouveau gate provenance/licence obligatoire

4.3 Stratégie client

Stratégie Décision Raison principale
yellowstone-grpc-client + yellowstone-grpc-proto non retenue importerait trop de lifecycle/reconnect upstream et de surface client
yellowstone-grpc-proto + client KSP autour de tonic retenue wire officiel, moteur/lifecycle/redaction KSP-owned
proto/génération KSP vendored fallback seulement dette licence/synchronisation/build plus forte

Matérialisation courante :

yellowstone-grpc-proto ^12.6   runtime sans feature tonic
yellowstone-grpc-proto         dev/test avec feature tonic pour GeyserServer fixture
tonic ^0.14                    channel + TLS runtime ; codegen/server dev/test
tonic-prost ^0.14              ProstCodec bas niveau
http ^1.5                      PathAndQuery interne
yellowstone-grpc-client        absent
prost/prost-types              aucune dépendance KSP directe
proto vendored                 absent

Les graphes Cargo inspectés pendant pre.002/pre.003 n'ont pas révélé de seconde génération incompatible à corriger. Le graph final est réinspecté en pre.012.

5. Matrice protocolaire fermée

5.1 Service Geyser

RPC Forme Classification Cible 0.2.9 État
Subscribe bidi stream standard Yellowstone oui DONE pre.009/pre.010
SubscribeDeshred bidi stream extension/pré-exécution Triton publiée dans le proto non OUT
SubscribeReplayInfo unary standard oui DONE
Ping unary standard oui DONE
GetLatestBlockhash unary standard oui DONE
GetBlockHeight unary standard oui DONE
GetSlot unary standard oui DONE
IsBlockhashValid unary standard oui DONE
GetVersion unary standard oui DONE

SubscribeDeshred reste explicitement exclu même s'il existe dans le proto publié : sa présence wire n'en fait pas une capacité provider-neutral de la fondation KSP.

5.2 SubscribeRequest

Champ Sémantique État
accounts map nom -> filtre accounts DONE
slots map nom -> filtre slots DONE
transactions map nom -> filtre transactions DONE
transactions_status même famille de filtre transaction DONE
blocks map nom -> filtre blocks DONE
blocks_meta map nom -> filtre marqueur vide DONE
entry map nom -> filtre marqueur vide DONE
commitment optional Processed/Confirmed/Finalized DONE
accounts_data_slice repeated offset/length DONE
ping optional request ping/id DONE
from_slot optional u64 DONE, sémantique prudente

Accounts :

account[]
owner[]
filters[]
nonempty_txn_signature?
cuckoo_accounts_filter?

memcmp { offset, oneof bytes | base58 | base64 }
datasize
token_account_state
lamports { oneof eq | ne | lt | gt }

Slots :

filter_by_commitment?
interslot_updates?
statuses = processed | confirmed | finalized | first_shred_received | completed | created_bank | dead

Transactions et transaction_status :

vote?
failed?
signature?
account_include[]
account_exclude[]
account_required[]
cuckoo_account_include?
token_accounts? = ALL | BALANCE_CHANGED

Blocks :

account_include[]
include_transactions?
include_accounts?
include_entries?
cuckoo_account_include?

blocks_meta et entry conservent la distinction absence / map vide / filtre nommé vide.

Bornes KSP communes matérialisées :

filter groups nommés total     <= 1024
filter name                    1..128 octets, trim exact, sans contrôle
filter names                   uniques globalement entre les sept maps
accounts_data_slice count      <= 128
accounts_data_slice length     <= 64 MiB
offset + length                sans overflow u64

Les bounds spécifiques Accounts/Transactions/Blocks sont ceux désormais testés dans leurs tranches respectives ; ils ne sont pas dupliqués comme knobs Config.

5.3 SubscribeUpdate

Variante Champs structurants conservés État
account account info + slot + is_startup DONE
slot slot + parent? + status + dead_error? DONE
transaction signature/is_vote/transaction/meta/index + slot DONE
transaction_status slot/signature/is_vote/index/error DONE
block slot/hash/rewards/time/height/parent/counts + transactions/accounts/entries DONE
ping marker server ping DONE
pong id DONE
block_meta block metadata/counts sans tableaux complets DONE
entry slot/index/num_hashes/hash/transaction counts/index DONE

Le top-level conserve également filters[] et created_at. Les types Prost/Yellowstone générés restent privés.

5.4 Unary standards

RPC Request Response KSP utile État
SubscribeReplayInfo vide first_available? DONE
Ping count count DONE
GetLatestBlockhash commitment? slot, blockhash, last_valid_block_height DONE
GetBlockHeight commitment? block_height DONE
GetSlot commitment? slot DONE
IsBlockhashValid blockhash + commitment? slot + valid DONE
GetVersion vide version bornée DONE

Ces capacités ne remplacent pas les wrappers Solana JSON-RPC HTTP.

6. Architecture runtime actuelle

6.1 Séparation des backends

HTTP                         HttpTransportSettings / pool HTTP
WebSocket engine             WsSession actor partagé
Solana standard WS           SolanaStandardWsSession
Helius LaserStream WS        HeliusLaserStreamWsSession

Yellowstone gRPC engine      YellowstoneGrpcChannel + moteur bidi KSP
Solana Yellowstone standard  contrats typed KSP
provider                     descripteur d'exécution/capability, pas nouveau protocole

Interdictions :

pas de WsProtocolKind pour gRPC
pas de WsEndpointSettings pour gRPC
pas de client Tonic brut réexporté
pas de second moteur physique par provider
pas de façade provider vide qui ne ferait que renommer le standard

6.2 Contrats publics principaux matérialisés

YellowstoneGrpcEndpointUrl
YellowstoneGrpcProviderName
YellowstoneGrpcClusterName
YellowstoneGrpcMetadataEntry
YellowstoneGrpcReconnectSettings
YellowstoneGrpcSessionSettings
YellowstoneGrpcEndpointSettings
YellowstoneGrpcTransportSettings
YellowstoneGrpcChannel

YellowstoneSubscribeRequest + filtres typed
YellowstoneSubscribeUpdate + variantes typed
SolanaYellowstoneGrpcSubscribeSession
YellowstoneGrpcSubscribeSnapshot
7 unary typed

Le wire Tonic/Prost reste privé et n'est pas une escape hatch publique.

6.3 Credentials et diagnostics

Transport reçoit des valeurs déjà résolues par son consumer. Il ne connaît :

aucun KSP_SECRET_*
aucun KSP_PUBLIC_*
aucun std::env
aucun header commercial hardcodé dans le standard

Les URLs, metadata sensibles, messages/details de tonic::Status et payloads arbitraires ne sont pas recopiés dans Debug, Display, snapshots ou contexts KSP.

7. Lifecycle, backpressure et continuité

7.1 Stream bidi

Acquis depuis pre.009 :

une request mpsc bornée consommée par Tonic
une update queue bornée côté KSP
mutation du SubscribeRequest sur le même stream
Ping serveur -> réponse automatique appropriée
Pong décodé
server half-close observable
client explicit close borné
Drop best-effort sans panic
oversized inbound/outbound borné
slow receiver overflow terminal et observable
shutdown déterministe

Aucune queue non bornée et aucun drop silencieux n'est présenté comme lossless.

7.2 Reconnect/replay

Acquis depuis pre.010 :

reconnect automatique           oui, borné et KSP-owned
resubscribe                     dernier SubscribeRequest complet accepté
from_slot de reprise            max(from_slot explicite, dernier slot observé) quand applicable
SubscribeReplayInfo             informatif
first_available                 clamp/prouve un gap seulement s'il dépasse le slot demandé
exactly-once                    non garanti
lossless                        non garanti
ordre global sans gap           non garanti
duplicate                       possible, compté, non supprimé silencieusement
gap                             compté seulement lorsqu'une preuve est disponible
shutdown during backoff         interrompt la reconnexion
budget reconnect épuisé         état terminal safe

Snapshot public safe :

reconnect_count
replay_attempt_count
continuity_gap_count
duplicate_update_count
last_requested_from_slot
last_observed_slot
terminal state/error code safe

La présence de from_slot ou SubscribeReplayInfo n'autorise aucune promesse de replay historique complet.

8. Config Transport V3 — tranche pre.011

8.1 Compatibilité documentaire

Décision fermée :

V1 = HTTP-only, backward-readable
V2 = HTTP + WebSocket, backward-readable
V3 = HTTP + WebSocket + Yellowstone gRPC optionnel par profil

Le schema V3 conserve des branches strictes V1/V2 au lieu de relâcher leurs additionalProperties.

Shape V3 :

format_version = 3
retry
ws_defaults
grpc_defaults
default_profile
profiles[] {
    profile_id
    endpoints[]
    ws_endpoints[]
    grpc_endpoints[]?   # optionnel par profil
}

L'absence de grpc_endpoints dans un profil V3 signifie None, pas un YellowstoneGrpcTransportSettings vide inventé.

8.2 grpc_defaults

Les defaults Config correspondent uniquement à de vrais settings runtime Transport :

connect_timeout_ms
unary_timeout_ms
close_timeout_ms
reconnect.max_retries
reconnect.initial_backoff_ms
reconnect.max_backoff_ms
request_channel_capacity
update_channel_capacity
max_inbound_message_size_bytes
max_outbound_message_size_bytes

Les bounds de filtres Subscribe restent un contrat Transport fixe et ne deviennent pas des options Config sans besoin démontré.

8.3 Endpoint gRPC

Chaque grpc_endpoints[] porte :

name
enabled
provider
cluster
protocol = solana_yellowstone
url
metadata[]?          # classe publique
secret_metadata[]?   # classe secrète
session?             # overrides bornés

Axes distincts :

protocol  = contrat wire standard, actuellement solana_yellowstone
provider  = environnement d'exécution descriptif, par exemple publicnode

provider = publicnode ne crée donc pas un PublicNodeGrpcProtocol ni une façade provider sans divergence réelle.

8.4 Provenance des metadata

Config est propriétaire de la résolution :

metadata          -> interdit toute provenance KSP_SECRET_*/KSPB_SECRET_*
secret_metadata   -> exige au moins une provenance secret et interdit une variable non-secret
littéraux autour d'un placeholder secret -> autorisés ; safe_value masque seulement le segment secret

Transport reçoit ensuite YellowstoneGrpcMetadataEntry public/secret et ne connaît jamais le nom de variable d'environnement.

8.5 API Config sans rupture V2

L'API existante reste :

http_settings()
ws_settings()
into_transport_settings() -> (HTTP, Option<WS>)

pre.011 ajoute :

grpc_settings()
into_all_transport_settings() -> (HTTP, Option<WS>, Option<Yellowstone gRPC>)

Le tuple historique n'est pas modifié silencieusement.

9. PublicNode dans 0.2.9

9.1 Mainnet

Endpoint retenu et validé en live :

https://solana-yellowstone-grpc.publicnode.com:443

Le profil committé est :

profile_id = publicnode_mainnet
provider   = publicnode
cluster    = mainnet-beta
protocol   = solana_yellowstone
metadata   = x-token secret via KSP_SECRET_PUBLICNODE_MAINNET_GRPC_X_TOKEN

Les tentatives sans metadata ont atteint le service mais ont reçu PERMISSION_DENIED. Le profil final fournit donc le personal token PublicNode comme metadata gRPC secrète x-token; Transport ne connaît ni le nom de variable KSP ni sa provenance.

9.2 Testnet

L'endpoint exact confirmé par l'opérateur et validé en live est :

https://solana-testnet-yellowstone-grpc.publicnode.com:443

Le profil committé est :

profile_id = publicnode_testnet
provider   = publicnode
cluster    = testnet
protocol   = solana_yellowstone
metadata   = x-token secret via KSP_SECRET_PUBLICNODE_TESTNET_GRPC_X_TOKEN

Le smoke final prouve qu'un même personal token opérateur peut fonctionner sur Mainnet et Testnet. KSP conserve néanmoins deux variables de configuration distinctes afin de ne pas imposer une hypothèse de portée provider à long terme ; elles peuvent recevoir la même valeur. Cette séparation est une liberté de configuration KSP, pas une assertion que PublicNode délivre des tokens network-scoped.

10. Threat model et bornes

Menaces couvertes :

credential dans URI/metadata
Status/message/details provider arbitraires
Debug dérivé de filtres/payloads
TLS/connect error qui réémet l'URI
message inbound/outbound hostile
stream flood / slow consumer
filter explosion / collision de noms
unknown enum/oneof
server/client half-close
reconnect loop
node divergent après reconnect
duplicate/gap après replay
mutation tardive du stream

Réponses :

wrappers redacted
allowlist de contexts KSP
validation/bounds avant I/O
queues bornées
états terminaux observables
reconnect budget borné
aucune promesse de continuité non prouvée
Config sensitivity gate avant construction de metadata secret

11. Smoke ownership

Ordre de preuve :

Transport programmatic -> PublicNode Mainnet Yellowstone + secret x-token fourni au harness
Transport programmatic -> PublicNode Testnet Yellowstone + secret x-token fourni au harness
Config V3 -> Transport -> PublicNode via secret_metadata, jamais un smoke réseau placé dans Config par facilité

Un smoke Transport pur peut vivre dans ksp-onchain-transport-lib/tests puisqu'il construit ses settings programmatiquement.

Un smoke cross-crates Config -> Transport ne doit pas devenir une responsabilité durable de ksp-config-lib. Sil nexiste pas encore de surface dintégration appropriée, la preuve live reste un smoke Transport programmatique avec secret fourni uniquement au harness opérateur.

Aucun secret provider n'est versionné.

12. État des tranches et historique compact

Les preuves détaillées restent dans les fichiers deltas/0.2.9/*.md. Le plan ne duplique plus leurs journaux complets.

Tranche Objet État consolidé
pre.001 + fixes audit upstream, licence, providers, architecture, sizing CLOSED
pre.002 + fixes dépendances, settings/errors, channel minimal CLOSED
pre.003 + fix TLS, metadata, fixture locale, 7 unary CLOSED
pre.004 + fix Subscribe foundation/common CLOSED
pre.005 + fix Accounts + Slots CLOSED
pre.006 namespace privé HTTP explicite CLOSED
pre.007 Transactions + transaction_status CLOSED
pre.008 + fix Blocks + block_meta + entry CLOSED
pre.009 + fix bidi, Ping/Pong, backpressure, half-close, shutdown CLOSED
pre.010 + fix reconnect, from_slot, ReplayInfo, gaps/duplicates CLOSED
pre.011 Config V3 + protocol/provider + PublicNode Mainnet CLOSED
pre.012 réaudit + smoke ajouté + docs préparatoires LOCAL GATE PASS
pre.012-fix.001 règles de séparation de fermeture + recalibrage CLOSED
pre.013 + fixes gate technique/live PublicNode + graphes finaux CLOSED
pre.014 réconciliation documentaire finale ACTIVE CANDIDATE
pre.015 prompt + CHANGELOG + ROADMAP PLANNED

Gate opérateur déterministe de pre.012 reçu le 2026-08-24 :

fmt/audit Rust              PASS sans warning
Markdown tables             PASS, 87 tableaux / 252 fichiers
cargo check                 PASS
cargo clippy                PASS sans warning
Config unit                 113/113
Config public API           15/15
Config ownership            5/5
Transport unit              383/383
Transport public API        49/49
Transport completeness      43/43
Transport doctests          4/4
workspace dependencies      3/3
cargo test --workspace      PASS
PublicNode live smoke       NOT RUN, test ignored
cargo tree final            NOT PROVIDED / NOT RUN in supplied log

pre.012 reste historiquement une tranche mixte créée avant l'adoption de la nouvelle règle. Son fix ne réécrit pas cette histoire : le prompt 0.2.10 déjà présent est désormais considéré comme un brouillon à finaliser seulement en pre.015, et les README/USAGE préparés sont réconciliés par la présente pre.014.

Gate technique final pre.013-fix.004 reçu le 2026-08-24 :

fmt/audit Rust              PASS sans warning
Markdown tables             PASS, 87 tableaux / 258 fichiers
cargo check                 PASS
cargo clippy --all-targets  PASS sans warning
Transport unit              383/383
Transport public API        49/49
Transport completeness      43/43
Transport doctests          4/4
cargo test --workspace      PASS
cargo graphs finaux         inspectés, stack Yellowstone unique tonic/prost 0.14
PublicNode Mainnet Subscribe + slot   PASS
PublicNode Testnet Subscribe + slot   PASS
live smoke total            2/2 PASS

Les graphes inspectés avant fix.004 restent valides : ce fix n'a modifié ni dépendance ni feature Cargo. La stack Yellowstone résolue est yellowstone-grpc-proto 12.6.0, tonic/tonic-prost 0.14.6, prost/prost-types 0.14.4, sans yellowstone-grpc-client runtime.

13. Forecast restant recalibré

pre.013 — gate technique/live final — CLOSED

Le couloir technique a finalement nécessité quatre fixes : découverte de l'auth PublicNode, ajout Testnet, adaptation de la fermeture live, puis conformité Clippy du harness. Le résultat consolidé est :

workspace.package.version final du couloir = 0.2.9-pre.13.fix.4
PublicNode Mainnet Subscribe + slot         PASS
PublicNode Testnet Subscribe + slot         PASS
secret wire                                 metadata x-token
secret versionné                            non
harness                                     deux lignes stdin, valeurs identiques autorisées
close provider                              succès ou timeout KSP borné après slot reçu
graphes Cargo                               inspectés
clippy --all-targets                        PASS sans warning
workspace                                   PASS

Les essais intermédiaires PERMISSION_DENIED sans credential sont conservés dans les deltas pre.013-fix.001 à fix.003; ils ne sont pas réinterprétés comme une régression du moteur standard.

pre.014 — réconciliation documentaire finale — ACTIVE CANDIDATE

Payload :

workspace.package.version = 0.2.9-pre.14
plan 016 réconcilié avec le gate technique/live réel
validation 012 fermée avec uniquement des preuves exécutées
README/USAGE Transport relus et corrigés
références durables liées à 0.2.9 réconciliées si nécessaire
audit Markdown final des documents touchés
aucune modification fonctionnelle/runtime
aucune finalisation CHANGELOG/ROADMAP/prompt suivant

Si une correction de code ou de smoke apparaît ici, pre.014 ne l'absorbe pas : une nouvelle tranche technique est ouverte, puis la réconciliation documentaire est rejouée.

pre.015 — préparation de publication minimale

Payload fonctionnel strict :

workspace.package.version = 0.2.9-pre.15
prompt 0.2.10 OrbitFlare finalisé
CHANGELOG.md synchronisé pour 0.2.9
ROADMAP.md état global 0.2.9 / suite synchronisé
delta pre.015

Aucun README, USAGE, plan, validation, règle, code, test, schema ou config n'est modifié dans cette tranche. Si l'un de ces fichiers nécessite encore une correction, la tranche de publication est décalée après une nouvelle prerelease dédiée.

rel.001 — publication stable

rel.001 ne sert qu'à la mécanique de publication stable : version Cargo finale 0.2.9, delta de release, commit final et tag v0.2.9 après gate. Aucun rattrapage technique ou documentaire n'y est mélangé.

14. Critères de split

Scinder avant dette silencieuse si :

  1. une évolution upstream matérielle invalide le wire retenu ;
  2. PublicNode exige une divergence provider-specific significative ;
  3. le replay nécessite un sous-système de fork/equivocation plus large que la foundation ;
  4. une tranche dépasse nettement le budget nominal sans frontière claire ;
  5. le gate technique/live montre une dette dépendance/licence ou une non-régression qui ne peut pas être corrigée proprement dans son couloir ;
  6. une correction appartenant à un couloir déjà franchi est découverte pendant un couloir ultérieur.

Le noyau à préserver reste :

moteur Yellowstone + façade Solana standard + Config provider-neutral + première intégration PublicNode minimale

15. Gates opérateur restants

Le gate pre.013 est fermé. Gate pre.014 :

python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.9
cargo test --workspace

Gate pre.015 : audit documentaire ciblé sur prompts/, CHANGELOG.md et ROADMAP.md, plus les validations minimales imposées par le changement de version Cargo. Aucun smoke n'est rejoué dans cette tranche en l'absence de changement technique.

16. Conditions de clôture 0.2.9

service/proto courant réconcilié
SubscribeDeshred explicitement OUT
7 unary verts
Subscribe standard et 9 updates verts
backend gRPC distinct de HTTP/WS
raw Tonic/Prost privé
secrets/metadata redacted
bounds/backpressure/shutdown verts
reconnect/replay documentés sans lossless implicite
Config V3 backward V1/V2
provider/protocol distincts
PublicNode Mainnet live Subscribe + slot PASS
PublicNode Testnet live Subscribe + slot PASS
auth PublicNode x-token secret documentée sans secret versionné
HTTP 52+14 non régressé
Standard WS 18/18 non régressé
Helius WS non régressé
cargo graphs finaux inspectés
README/USAGE finaux réconciliés en pre.014
validation 012 fermée en pre.014
prompt/CHANGELOG/ROADMAP seuls finalisés en pre.015
workspace final vert

17. Séquence après 0.2.9

La séquence active a été recalibrée par les fixes de pre.001; cette décision est conservée pendant le nettoyage documentaire :

0.2.9   moteur Yellowstone + Solana standard + PublicNode
0.2.10  OrbitFlare Yellowstone gRPC
0.2.11  Helius LaserStream gRPC
0.2.12  off-chain price transport
0.2.13  Price Desk + intégration prix Wallet Desk
0.2.14  interface/wire foundation
0.2.15  program-api foundation

Les intégrations suivantes restent dans le backlog non numéroté tant qu'aucune décision d'implémentation ne les fait entrer dans la séquence active :

TODO   eRPC
TODO   Triton
TODO   Alchemy
TODO   QuickNode
TODO   Chainstack
IDEAS  Tatum
IDEAS  Shyft
IDEAS  Solinfra
IDEAS  NodeFlare

OrbitFlare reste le provider dédié 0.2.10 et Helius LaserStream gRPC 0.2.11 selon la séquence recalibrée par les fixes de pre.001. Chaque release doit réauditer auth, capabilities, restrictions, extensions wire, replay/from_slot et lifecycle au lieu de supposer une équivalence complète avec N2.