27 KiB
Plan 0.2.9 — moteur Yellowstone gRPC + standard Solana + PublicNode
Statut courant :
0.2.9-pre.010-fix.001est fermée par gate opérateur sans warning.0.2.9-pre.011est la tranche active : Config Transport V3, séparation explicite protocol/provider, mapping Config -> Yellowstone gRPC et premier profil PublicNode Mainnet. Le présent document a été réorganisé pendantpre.011: les décisions actives restent ici ; les journaux historiques détaillés restent dansdeltas/0.2.9/.
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 sans credential 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 du gate
Le gate d'ouverture a réaudité Yellowstone courant et a constaté des versions indépendantes entre plugin, client et proto :
release GitHub observée v15.1.2+solana.4.2.0 — 2026-08-18
yellowstone-grpc-client 13.3.0
yellowstone-grpc-proto 12.6.0
prost/prost-types 0.14.x
tonic 0.14.x
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
Une réaudite finale est requise en pre.012 si l'upstream a changé matériellement pendant la release.
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
La surface publique réauditée le 2026-08-24 confirme Yellowstone gRPC Solana Mainnet et affiche le host/port :
solana-yellowstone-grpc.publicnode.com:443
Le wrapper KSP attend une URL http/https; Config matérialise donc le même endpoint TLS sous la forme https://solana-yellowstone-grpc.publicnode.com:443.
pre.011 versionne donc un profil :
profile_id = publicnode_mainnet
provider = publicnode
cluster = mainnet-beta
protocol = solana_yellowstone
credential = aucun
La partie HTTP/WS de ce profil reste standard Solana ; seul l'endpoint gRPC est PublicNode.
9.2 Testnet
PublicNode affiche toujours une offre Solana Testnet gRPC, mais le hostname exact n'est pas exposé de façon suffisamment autoritative dans la surface publique inspectable pendant pre.011.
Décision :
existence Testnet gRPC confirmée
hostname Testnet exact non inventé
profil Testnet committé non en pre.011
réaudit endpoint/live pre.012
Un échec à confirmer le hostname Testnet ne bloque pas la fondation Mainnet ; il doit être documenté explicitement au gate final.
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
Transport programmatic -> PublicNode Testnet seulement si endpoint exact confirmé
Config V3 -> Transport -> PublicNode via composition légitime, 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. S'il n'existe pas encore de surface d'intégration appropriée, la procédure reste opérateur/documentée en pre.012.
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 | ACTIVE CANDIDATE |
pre.012 |
live/compliance/docs/graph/prompt suivant | PLANNED |
Gate opérateur de fermeture pre.010-fix.001 :
fmt/audit/check/clippy PASS sans warning
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
13. Forecast restant
pre.011 — Config V3 + PublicNode Mainnet
Cible :
workspace.package.version = 0.2.9-pre.11
schema std.transport V3 strict + branches V1/V2
Config adapter gRPC
metadata publique/secrète + provenance
protocol/provider distincts
profil publicnode_mainnet
compatibilité API V2 conservée
plan 016 + validation 012 réorganisés
Preuves :
schema Draft 2020-12 valide
fixtures V1/V2/V3
Config unit/public API/ownership
Transport non-régressé
workspace complet
pre.012 — fermeture technique et live
Cible :
réaudit upstream final
réaudit PublicNode Testnet hostname
smoke PublicNode Mainnet opt-in
Testnet opt-in seulement si endpoint exact confirmé
compliance HTTP 52 current + 14 historical
compliance Standard WS 18/18
Helius WebSocket non régressé
cargo tree direct + duplicates final
README/USAGE Transport synchronisés
matrice validation fermée
prompt 0.2.10 préparé selon la séquence active
workspace final vert
Si pre.012 devient trop large, une pre.013+ est créée ; le numéro n'est pas une deadline.
14. Critères de split
Scinder avant dette silencieuse si :
- une évolution upstream matérielle invalide le wire retenu ;
- PublicNode exige une divergence provider-specific significative ;
- le replay nécessite un sous-système de fork/equivocation plus large que la foundation ;
- une tranche dépasse nettement le budget nominal sans frontière claire ;
- le gate final montre une dette dépendance/licence ou une non-régression qui ne peut pas être corrigée proprement dans la tranche.
Le noyau à préserver reste :
moteur Yellowstone + façade Solana standard + Config provider-neutral + première intégration PublicNode minimale
15. Gates opérateur
Après changement Rust :
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
cargo check --workspace
cargo clippy --workspace --all-targets
Pour pre.011 :
cargo test -p ksp-config-lib
cargo test -p ksp-config-lib --test public_api
cargo test -p ksp-config-lib --test ownership
cargo test -p ksp-onchain-transport-lib
cargo test -p ksp-core-lib --test workspace_dependencies
cargo test --workspace
Le graphe de dépendances gRPC n'est pas modifié par pre.011; son gate final complet reste en pre.012 :
cargo tree -p ksp-onchain-transport-lib
cargo tree -p ksp-onchain-transport-lib --duplicates
cargo tree --duplicates
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 validé
Testnet validé si endpoint exact confirmé, sinon report factuel documenté
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 synchronisés
validation 012 fermée
prompt release suivante prêt
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.