7 Commits

Author SHA1 Message Date
afaa9c0239 v0.2.10-rel.001 2026-08-25 16:40:46 +02:00
7d456b41dc v0.2.10-pre.005 2026-08-25 15:43:15 +02:00
0edeed1c48 v0.2.10-pre.004 2026-08-25 15:26:04 +02:00
be5e3464ee v0.2.10-pre.003 2026-08-25 15:18:28 +02:00
1967e845b0 v0.2.10-pre.002-fix.001 2026-08-25 14:17:40 +02:00
af807afff5 v0.2.10-pre.002 2026-08-25 10:29:16 +02:00
7f567bc1bb v0.2.10-pre.001 2026-08-25 10:14:37 +02:00
25 changed files with 3881 additions and 54 deletions

View File

@@ -1,5 +1,5 @@
# file: .env.example
# version: 8
# version: 9
# KSP Logging root directory. Used by config/std.logging.json for relative log output paths.
# The current Config document fallback is "logs" when neither the process environment nor .env defines this variable.
@@ -42,6 +42,10 @@ KSP_PUBLIC_SOLANA_MAINNET_WS_URL=wss://api.mainnet-beta.solana.com
# Keep the real value only in the process environment or local .env; do not assume that a Mainnet token is authorized on Testnet.
# KSP_SECRET_PUBLICNODE_TESTNET_GRPC_X_TOKEN=replace-me
# OrbitFlare Solana Free License Key used as secret x-token metadata by the committed Devnet Yellowstone gRPC profile.
# Keep the real ORBIT-* value only in the process environment or local .env; do not substitute the Customer API X-ORBIT-KEY.
# KSP_SECRET_ORBITFLARE_DEVNET_GRPC_X_TOKEN=replace-me
# Helius API key used by the LaserStream WebSocket endpoint in config/examples/std.transport.example.json.
# Keep the real credential only in the process environment or local .env; never commit it.
# KSP_SECRET_HELIUS_API_KEY=replace-me

View File

@@ -1,8 +1,16 @@
<!-- file: CHANGELOG.md -->
<!-- version: 13 -->
<!-- version: 14 -->
# Changelog KSP
## 0.2.10 — OrbitFlare Yellowstone gRPC — 2026-08-25
`0.2.10` ajoute OrbitFlare comme provider Yellowstone gRPC sur le moteur N1 et le standard N2 acquis dans `0.2.9`, sans modifier le moteur gRPC ni créer de façade provider inutile. L'audit et le smoke live ont établi le contrat réel du service Solana Free Devnet : endpoint `http://devnet.rpc.orbitflare.com:10000`, authentification par metadata gRPC secrète `x-token` portant la License Key OrbitFlare, et émission du Ping Yellowstone standard. Config Transport V3 représente ce provider avec `provider = orbitflare`, `cluster = devnet`, `protocol = solana_yellowstone` et `secret_metadata`, tandis que `.env.example` inventorie `KSP_SECRET_ORBITFLARE_DEVNET_GRPC_X_TOKEN` sans valeur réelle.
La première tentative live sans metadata a volontairement échoué avec `Unauthenticated`, ce qui a conduit à `pre.002-fix.001` après audit des sources OrbitFlare actuelles. Le smoke corrigé reçoit la License Key uniquement par stdin opérateur, ouvre `Subscribe`, observe un Slot non nul et un `SubscribeUpdate::Ping`, puis ferme la session proprement. Le même smoke est repassé au gate technique final `pre.003`. La clôture conserve donc N1/N2 inchangés, n'ajoute ni heartbeat OrbitFlare, ni SDK provider, ni Config V4, et confirme que la réponse automatique au Ping standard existante suffit.
Le gate final conserve l'ensemble du workspace vert : audits Rust/Markdown, `cargo check`, Clippy, tests Config et Transport, `cargo test --workspace`, canaris de dépendances et graphes Cargo. Helius LaserStream gRPC, audité comme largement wire-compatible Yellowstone mais non raisonnablement live-testable sans abonnement payant, est reporté dans les TODO provider sans numéro de release. La séquence active passe donc à `0.2.11 — off-chain price transport`; `prompts/016-V0_2_11_START_PROMPT.md` l'ouvre exclusivement depuis le tag stable `v0.2.10` avec audit actuel des sources de prix, de leurs sémantiques et de leur testabilité avant toute implémentation lourde.
Ce changelog résume uniquement les releases KSP considérées comme stables, dans l'ordre chronologique décroissant. Les détails de chaque livraison restent dans `deltas/`.
## 0.2.9 — Yellowstone gRPC standard/provider-neutral — 2026-08-24

View File

@@ -1,12 +1,12 @@
# file: Cargo.toml
# version: 261
# version: 268
[workspace]
resolver = "3"
members = ["crates/ksp-app-config-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-logging-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-wallet-lib"]
[workspace.package]
version = "0.2.9"
version = "0.2.10"
edition = "2024"
license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"

View File

@@ -1,5 +1,5 @@
<!-- file: ROADMAP.md -->
<!-- version: 86 -->
<!-- version: 87 -->
# Roadmap KSP
@@ -54,15 +54,15 @@ Le roadmap décrit les objectifs à atteindre et les grandes étapes prévues. U
- [X] `0.2.7` — WebSocket Solana standard stable : 9 familles subscribe/unsubscribe typées (18/18 opérations), sessions physiques multiples explicites, subscriptions logiques typées, lifecycle/reconnect/resubscribe/backpressure/shutdown bornés, Config V2, non-régression HTTP 52+14, compliance finale, smoke WebSocket Devnet et audit de dépendances validés ; publication `rel.001` et prompt `0.2.8` prêts.
- [X] `0.2.8` — Helius LaserStream WebSocket stable : façade provider dédiée sur lactor WebSocket partagé, sept familles standard réutilisées (`account/logs/program/root/signature/slot/slotsUpdates`) + `transactionSubscribe`/`transactionUnsubscribe`, `block/vote` absents, heartbeat Ping 60 s Helius-only, Config V2/secrets redacted, lifecycle adversarial, compliance HTTP 52+14 / Standard WS 18/18 et graphes Cargo finaux validés ; prompt `0.2.9` prêt.
- [X] `0.2.9` — Yellowstone gRPC standard/provider-neutral stable : moteur Tonic/Protobuf KSP partagé, sept unary standard retenues, `Subscribe` bidi et neuf variantes dupdate, lifecycle/backpressure/reconnect/replay bornés sans promesse lossless, Config Transport V3 backward V1/V2 avec provider/protocol séparés, profils PublicNode Mainnet/Testnet authentifiés par `x-token`, smoke live `Subscribe -> Slot` 2/2 PASS et graphes Cargo finaux inspectés ; `SubscribeDeshred` reste hors scope.
- [ ] `0.2.10`Ajouter lintégration Yellowstone gRPC OrbitFlare sur le moteur/standard acquis, avec audit explicite des auth/capabilities/restrictions/extensions et Devnet comme cible gratuite prioritaire.
- [ ] `0.2.11`Ajouter lintégration Helius LaserStream gRPC sur le moteur Yellowstone partagé, avec audit de compatibilité/replay/auth/extensions, sans confondre cette surface avec LaserStream WebSocket.
- [ ] `0.2.12` — Introduire `ksp-offchain-transport-lib` avec un premier lecteur de prix, au minimum SOL/USD et SOL/EUR.
- [ ] `0.2.13` — Introduire une petite application desk de visualisation/validation des prix offchain, puis intégrer cette capacité dans `ksp-app-wallet-desk` sans dupliquer la logique de récupération/normalisation possédée par le composant spécialisé.
- [ ] `0.2.14` — Introduire la première surface de `ksp-interface-lib`, comprenant une API wire publique utilisable par les implémentations officielles et externes.
- [ ] `0.2.15` — Introduire `ksp-program-api` comme premier contrat Program extensible, sans imposer encore `ksp-program-lib` complet.
- [X] `0.2.10`OrbitFlare Yellowstone gRPC stable : profil Config V3 Devnet, License Key injectée comme metadata secrète `x-token`, smoke live `Subscribe -> Slot + Ping` validé deux fois, sans modification du moteur N1/N2 ni heartbeat provider.
- [ ] `0.2.11`Introduire `ksp-offchain-transport-lib` avec un premier lecteur de prix provider-neutral, au minimum SOL/USD et SOL/EUR, après audit actuel des sémantiques provider et de la testabilité live.
- [ ] `0.2.12` — Introduire une petite application desk de visualisation/validation des prix offchain, puis intégrer cette capacité dans `ksp-app-wallet-desk` sans dupliquer la logique de récupération/normalisation possédée par le composant spécialisé.
- [ ] `0.2.13` — Introduire la première surface de `ksp-interface-lib`, comprenant une API wire publique utilisable par les implémentations officielles et externes.
- [ ] `0.2.14` — Introduire `ksp-program-api` comme premier contrat Program extensible, sans imposer encore `ksp-program-lib` complet.
### TODO/IDEAS — providers Yellowstone non planifiés
- [ ] **TODO** — Helius LaserStream gRPC : réauditer lorsque l'accès live gRPC est raisonnablement disponible ; conserver N1/N2 Yellowstone inchangés, vérifier auth/endpoints/Subscribe/Ping/replay/from_slot/erreurs provider et traiter les preprocessed transactions comme extension Helius séparée.
- [ ] **TODO** — eRPC : réauditer accès, auth/IP policy, capabilities et produits complémentaires avant toute décision dimplémentation.
- [ ] **TODO** — Triton : réauditer la frontière Yellowstone upstream / extensions Triton, notamment Deshred et futures extensions.
- [ ] **TODO** — Alchemy : réauditer auth, replay, limites et capabilities Yellowstone avant toute intégration.

View File

@@ -79,6 +79,64 @@
}
]
},
{
"profile_id": "orbitflare_devnet",
"endpoints": [
{
"name": "solana_devnet_public",
"enabled": true,
"provider": "solana-public",
"cluster": "devnet",
"url": "${KSP_PUBLIC_SOLANA_DEVNET_HTTP_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": 5,
"burst_capacity": 10,
"max_concurrent_requests": 8,
"pause_after_rate_limit_ms": 1000
}
}
]
}
],
"ws_endpoints": [
{
"name": "solana_devnet_public_ws",
"enabled": true,
"provider": "solana-public",
"cluster": "devnet",
"kind": "solana_standard",
"url": "${KSP_PUBLIC_SOLANA_DEVNET_WS_URL:-wss://api.devnet.solana.com}"
}
],
"grpc_endpoints": [
{
"name": "orbitflare_solana_devnet_yellowstone",
"enabled": true,
"provider": "orbitflare",
"cluster": "devnet",
"protocol": "solana_yellowstone",
"url": "http://devnet.rpc.orbitflare.com:10000",
"metadata": [],
"secret_metadata": [
{
"key": "x-token",
"value": "${KSP_SECRET_ORBITFLARE_DEVNET_GRPC_X_TOKEN}"
}
]
}
]
},
{
"profile_id": "mainnet_public",
"endpoints": [

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-config-lib/unit_tests/transport.rs
// version: 10
// version: 12
#[test]
fn fixture_transport_profile_maps_complete_runtime_contract() {
@@ -133,6 +133,52 @@ fn committed_transport_document_maps_default_and_explicit_profiles() {
}
}
#[test]
fn committed_v3_orbitflare_devnet_profile_maps_provider_neutral_yellowstone_grpc_with_license_x_token() {
let engine = committed_engine();
let engine = match engine {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let canary = "ORBITFLARE-DEVNET-LICENSE-X-TOKEN-CANARY";
let mut process = std::collections::BTreeMap::<String, String>::new();
process.insert("KSP_SECRET_ORBITFLARE_DEVNET_GRPC_X_TOKEN".to_owned(), canary.to_owned());
let environment = crate::ConfigEnvironment::from_maps(process, std::collections::BTreeMap::new());
let resolved = engine.load_resolved_transport_config(std::option::Option::Some("orbitflare_devnet"), &environment);
assert!(resolved.is_ok(), "committed OrbitFlare Devnet Transport profile should map from its License Key x-token: {resolved:?}");
if let std::result::Result::Ok(resolved) = resolved {
assert_eq!(resolved.profile_id(), "orbitflare_devnet");
assert_eq!(resolved.selection_source(), crate::ConfigProfileSelectionSource::Explicit);
assert_eq!(resolved.settings().endpoints()[0].cluster().as_str(), "devnet");
assert_eq!(resolved.settings().endpoints()[0].url().as_str(), "https://api.devnet.solana.com");
let ws = resolved.ws_settings();
assert!(ws.is_some(), "OrbitFlare Devnet profile should retain the standard Solana Devnet WebSocket companion endpoint");
if let std::option::Option::Some(ws) = ws {
assert_eq!(ws.endpoints()[0].url().as_str(), "wss://api.devnet.solana.com");
assert_eq!(ws.endpoints()[0].protocol(), ksp_onchain_transport_lib::WsProtocolKind::SolanaStandard);
}
let grpc = resolved.grpc_settings();
assert!(grpc.is_some(), "OrbitFlare Devnet profile should expose one provider-neutral Yellowstone gRPC endpoint");
if let std::option::Option::Some(grpc) = grpc {
assert_eq!(grpc.endpoints().len(), 1);
let endpoint = &grpc.endpoints()[0];
assert_eq!(endpoint.name(), "orbitflare_solana_devnet_yellowstone");
assert_eq!(endpoint.provider().as_str(), "orbitflare");
assert_eq!(endpoint.cluster().as_str(), "devnet");
assert_eq!(endpoint.url().as_str(), "http://devnet.rpc.orbitflare.com:10000");
assert!(!endpoint.url().uses_tls(), "OrbitFlare shared Devnet URL must remain explicitly HTTP/2 plaintext");
assert_eq!(endpoint.metadata().len(), 1);
assert_eq!(endpoint.metadata()[0].key(), "x-token");
assert!(endpoint.metadata()[0].is_secret());
assert!(grpc.validate().is_ok(), "Config-produced OrbitFlare Yellowstone gRPC settings should satisfy Transport validation");
let debug = format!("{grpc:?}");
assert!(!debug.contains("orbitflare.com"), "Transport settings Debug must not expose OrbitFlare gRPC endpoint URLs");
assert!(!debug.contains(canary), "OrbitFlare License Key x-token must stay redacted from Transport settings Debug");
assert!(!debug.contains("X-ORBIT-KEY"), "OrbitFlare Customer API credentials must never enter Yellowstone Transport settings");
}
}
}
#[test]
fn committed_v3_publicnode_profiles_map_provider_neutral_yellowstone_grpc_with_network_scoped_secret_x_tokens() {
let engine = committed_engine();

View File

@@ -1,5 +1,5 @@
<!-- file: crates/ksp-onchain-transport-lib/README.md -->
<!-- version: 22 -->
<!-- version: 24 -->
# `ksp-onchain-transport-lib`
@@ -220,7 +220,6 @@ Pour Helius, lactor envoie automatiquement un control frame WebSocket `Ping`
LaserStream **gRPC** reste un backend distinct, hors de cette façade, de `WsProtocolKind` et de la Config WebSocket `helius_laserstream`.
## Yellowstone gRPC standard
La foundation `0.2.9` ajoute un troisième backend réseau distinct de HTTP et WebSocket. Le moteur est KSP-owned : `yellowstone-grpc-proto` fournit le wire publié, tandis que Tonic reste encapsulé derrière les types crate-root KSP. Aucun client Tonic brut ni type protobuf upstream nest réexporté.
@@ -246,6 +245,8 @@ Config Transport V3 peut mapper des `grpc_endpoints` vers ces settings sans inve
Le smoke live opt-in ouvre un `Subscribe` slots authentifié sur PublicNode Mainnet et Testnet, attend un update `Slot` non nul puis ferme de manière bornée. Les deux endpoints sont versionnés. Le harness reçoit ses personal tokens sur stdin ; aucun secret nest committé ni placé dans lURL. Le même token a été validé par lopérateur sur les deux réseaux, même si Config conserve deux variables distinctes pour laisser cette policy flexible.
`0.2.10` ajoute le profil `orbitflare_devnet` sur le même moteur provider-neutral. Lendpoint validé est `http://devnet.rpc.orbitflare.com:10000`; la License Key `ORBIT-*` est fournie par Config via `KSP_SECRET_ORBITFLARE_DEVNET_GRPC_X_TOKEN` et devient la metadata secrète `x-token`. Le smoke live dédié a validé `Subscribe` slots à commitment confirmed, un `Slot` non nul et un `SubscribeUpdate::Ping` standard. Cette preuve confirme quaucune façade OrbitFlare, aucun heartbeat provider et aucune modification N1/N2 ne sont nécessaires.
## Résilience
L'admission est calculée par couple endpoint/rôle. Le pool applique :
@@ -300,16 +301,20 @@ Transport WebSocket pur : settings programmatiques -> WsSession
Composition historique : Config -> std.transport/devnet_public -> HttpTransportPool
-> getHealth/getGenesisHash/getVersion/getBalance
Transport Yellowstone gRPC : settings programmatiques + x-token secret
-> PublicNode Mainnet + Testnet
-> TLS -> Subscribe slots -> Slot non nul -> close borné
Transport Yellowstone gRPC PublicNode : settings programmatiques + x-token secret
-> Mainnet + Testnet
-> TLS -> Subscribe slots -> Slot non nul -> close borné
Transport Yellowstone gRPC OrbitFlare : settings programmatiques + License Key x-token
-> Devnet
-> Subscribe slots -> Slot non nul + server Ping -> close borné
```
Le smoke HTTP Transport utilise pour sa branche Token la forme Devnet documentée `getTokenAccountsByOwner(owner, { programId }, { commitment: finalized, encoding: jsonParsed })`. L'owner est une Pubkey ordinaire de l'exemple officiel ; aucune présence de token account n'est exigée, donc une liste vide reste valide.
Le smoke WebSocket Transport cible uniquement la famille stable `slotSubscribe` sur l'endpoint public Devnet `wss://api.devnet.solana.com`. Il borne connexion, attente de notification, unsubscribe et fermeture ; il ne transforme aucune famille unstable en gate live.
Les quatre tests sont `ignored` par défaut ; le test Yellowstone contient deux cas live Mainnet/Testnet. Les trois familles de smokes Transport appartiennent durablement à cette crate ; le smoke cross-crates hébergé dans Config reste transitoire jusqu'à l'existence d'une surface KSP d'intégration/orchestration appropriée. Un rate-limit, refus externe ou incident Devnet n'est pas assimilé automatiquement à une régression locale.
Les cinq tests sont `ignored` par défaut ; le test PublicNode Yellowstone contient deux cas live Mainnet/Testnet et le test OrbitFlare contient un cas Devnet. Les trois familles de smokes Transport appartiennent durablement à cette crate ; le smoke cross-crates hébergé dans Config reste transitoire jusqu'à l'existence d'une surface KSP d'intégration/orchestration appropriée. Un rate-limit, refus externe ou incident Devnet n'est pas assimilé automatiquement à une régression locale.
Aucun smoke Helius live supplémentaire nest committé en `0.2.8-pre.010`. Un tel test devrait à la fois obtenir `KSP_SECRET_HELIUS_API_KEY` via Config et exercer Transport ; lajouter dans Transport violerait lownership environnement/secret, tandis que lajouter dans Config étendrait lexception cross-crates que le projet veut au contraire résorber. La première surface KSP dintégration/orchestration dédiée devra héberger ce smoke. Le scénario live recommandé est alors `helius_devnet -> HeliusLaserStreamWsSession -> slotSubscribe -> notification -> unsubscribe -> close`; `transactionSubscribe` reste un smoke optionnel dépendant des droits provider et ne devient pas un gate stable de release.
@@ -325,4 +330,6 @@ Aucun smoke Helius live supplémentaire nest committé en `0.2.8-pre.010`. Un
- [`../../docs/validation/007-V0_2_4_HTTP_FINAL_COMPLIANCE.md`](../../docs/validation/007-V0_2_4_HTTP_FINAL_COMPLIANCE.md) — matrice finale validée `52/52 + 14/14` et audit `KSP-TRANSPORT-007` global ;
- [`../../docs/plans/016-V0_2_9_YELLOWSTONE_GRPC_PLAN.md`](../../docs/plans/016-V0_2_9_YELLOWSTONE_GRPC_PLAN.md) — plan Yellowstone gRPC standard/provider-neutral et PublicNode ;
- [`../../docs/validation/012-V0_2_9_YELLOWSTONE_GRPC.md`](../../docs/validation/012-V0_2_9_YELLOWSTONE_GRPC.md) — matrice de compliance Yellowstone ;
- [`../../docs/plans/017-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC_PLAN.md`](../../docs/plans/017-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC_PLAN.md) — composition OrbitFlare Devnet sur le standard Yellowstone ;
- [`../../docs/validation/013-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC.md`](../../docs/validation/013-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC.md) — validation finale OrbitFlare `x-token`, `Slot + Ping` et non-régression N1/N2 ;
- [`../../config/std.transport.json`](../../config/std.transport.json) — configuration standard Transport V3 HTTP + WebSocket + gRPC, avec lecture backward V1/V2.

View File

@@ -1,5 +1,5 @@
<!-- file: crates/ksp-onchain-transport-lib/USAGE.md -->
<!-- version: 22 -->
<!-- version: 23 -->
# Utilisation de `ksp-onchain-transport-lib`
@@ -331,6 +331,36 @@ let channel = ksp_onchain_transport_lib::YellowstoneGrpcChannel::connect(endpoin
`protocol = solana_yellowstone` est validé par Config et reste distinct du descripteur `provider`. Une valeur provider nautorise pas Transport à introduire une API provider-specific sans divergence réelle.
### Profil OrbitFlare Devnet
Le profil committé `orbitflare_devnet` réutilise exactement le même accès Config -> Transport :
```rust
let resolved = match engine.load_resolved_transport_config(Some("orbitflare_devnet"), &environment) {
Ok(value) => value,
Err(error) => return Err(error),
};
let grpc_settings = match resolved.grpc_settings() {
Some(value) => value,
None => return Err(ksp_core_lib::Error::new(
ksp_onchain_transport_lib::ERROR_CODE_INVALID_SETTINGS,
"selected OrbitFlare profile has no Yellowstone gRPC endpoint",
)),
};
let endpoint = match grpc_settings.endpoints().iter().find(|candidate| candidate.enabled()) {
Some(value) => value,
None => return Err(ksp_core_lib::Error::new(
ksp_onchain_transport_lib::ERROR_CODE_INVALID_SETTINGS,
"selected OrbitFlare profile has no enabled Yellowstone gRPC endpoint",
)),
};
let channel = ksp_onchain_transport_lib::YellowstoneGrpcChannel::connect(endpoint).await;
```
Config résout `KSP_SECRET_ORBITFLARE_DEVNET_GRPC_X_TOKEN` vers la metadata secrète `x-token`. Sa valeur effective est la License Key `ORBIT-*` du produit Solana ; `X-ORBIT-KEY` et le Bearer du Customer API ne doivent pas être utilisés pour Yellowstone. Transport ne lit jamais cette variable lui-même.
Lendpoint validé par `0.2.10` est `http://devnet.rpc.orbitflare.com:10000`. Il reste volontairement en `http` : KSP ne remplace pas le transport provider par `https` sans endpoint TLS explicitement fourni.
### Subscribe bidirectionnel
Une session standard part dune requête typed complète :
@@ -519,6 +549,26 @@ Testnet https://solana-testnet-yellowstone-grpc.publicnode.com:443
Les profils Config conservent deux variables secrètes distinctes afin d'autoriser des credentials différents si nécessaire ; cette séparation ne signifie pas que PublicNode impose actuellement un token différent par réseau. Un timeout KSP de half-close après réception du slot est accepté par le smoke comme fermeture bornée du provider ; aucune absence de slot ni autre erreur n'est masquée.
Le smoke **Transport Yellowstone gRPC OrbitFlare** est lui aussi indépendant de Config. Il lit une seule License Key sur stdin, la classe comme metadata secrète `x-token`, ouvre le standard `Subscribe`, demande `slots` à commitment confirmed, attend un Slot non nul et un `SubscribeUpdate::Ping`, puis ferme la session de manière bornée :
```bash
read -rsp 'OrbitFlare License Key: ' ORBITFLARE_LICENSE_KEY
echo
printf '%s\n' "$ORBITFLARE_LICENSE_KEY" \
| cargo test -p ksp-onchain-transport-lib \
--test yellowstone_orbitflare_smoke \
-- --ignored --nocapture
unset ORBITFLARE_LICENSE_KEY
```
Endpoint validé :
```text
Devnet http://devnet.rpc.orbitflare.com:10000
```
Le gate `0.2.10-pre.003` a passé ce scénario en live avec `Slot + Ping`. Le Ping reçu est le message Yellowstone standard auquel N1 sait déjà répondre sans remplacer la dernière requête complète mémorisée ; aucun heartbeat OrbitFlare supplémentaire nest donc requis.
Le smoke de **composition Config -> Transport** reste également disponible :
```bash

View File

@@ -0,0 +1,150 @@
// file: crates/ksp-onchain-transport-lib/tests/yellowstone_orbitflare_smoke.rs
// version: 2
//! Opt-in live OrbitFlare Devnet characterization smoke for authenticated provider-neutral Yellowstone gRPC Subscribe.
use std::io::IsTerminal; // rust-rules: trait-import
fn orbitflare_license_x_token() -> std::string::String {
assert!(
!std::io::stdin().is_terminal(),
"pipe the OrbitFlare Solana Free License Key to this ignored smoke on stdin; never pass it as a command-line argument"
);
let mut token = std::string::String::new();
match std::io::stdin().read_line(&mut token) {
std::result::Result::Ok(_) => {},
std::result::Result::Err(error) => panic!("OrbitFlare License Key x-token must be readable from smoke stdin: {error}"),
}
let token = token.trim().to_owned();
assert!(!token.is_empty(), "OrbitFlare License Key x-token provided on smoke stdin must not be empty");
return token;
}
fn orbitflare_devnet_endpoint(x_token: &str) -> ksp_core_lib::Result<ksp_onchain_transport_lib::YellowstoneGrpcEndpointSettings> {
let url = match ksp_onchain_transport_lib::YellowstoneGrpcEndpointUrl::parse("http://devnet.rpc.orbitflare.com:10000") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let session = ksp_onchain_transport_lib::YellowstoneGrpcSessionSettings::new(
std::time::Duration::from_secs(10),
std::time::Duration::from_secs(10),
std::time::Duration::from_secs(5),
ksp_onchain_transport_lib::YellowstoneGrpcReconnectSettings::new(0, std::time::Duration::from_millis(250), std::time::Duration::from_secs(2)),
8,
8,
16 * 1024 * 1024,
4 * 1024 * 1024,
);
let endpoint = ksp_onchain_transport_lib::YellowstoneGrpcEndpointSettings::new(
"orbitflare_devnet_yellowstone",
true,
ksp_onchain_transport_lib::YellowstoneGrpcProviderName::new("orbitflare"),
ksp_onchain_transport_lib::YellowstoneGrpcClusterName::new("devnet"),
url,
session,
);
let metadata = match ksp_onchain_transport_lib::YellowstoneGrpcMetadataEntry::secret("x-token", x_token) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return endpoint.with_metadata(vec![metadata]);
}
fn slot_request() -> ksp_core_lib::Result<ksp_onchain_transport_lib::YellowstoneSubscribeRequest> {
let mut request = ksp_onchain_transport_lib::YellowstoneSubscribeRequest::new();
let name = match ksp_onchain_transport_lib::YellowstoneSubscribeFilterName::new("slots") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
match request.insert_slot_filter(name, ksp_onchain_transport_lib::YellowstoneSubscribeSlotFilter::new()) {
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => return std::result::Result::Err(error),
}
request.set_commitment(std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Confirmed));
return std::result::Result::Ok(request);
}
#[tokio::test(flavor = "current_thread")]
#[ignore = "opt-in live OrbitFlare Devnet Yellowstone gRPC characterization; reads one Solana Free License Key x-token from stdin, waits for Slot plus standard server Ping, then closes"]
async fn orbitflare_devnet_yellowstone_streams_slots_and_emits_standard_server_ping_with_license_x_token() {
let x_token = orbitflare_license_x_token();
let endpoint = match orbitflare_devnet_endpoint(x_token.as_str()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("programmatic OrbitFlare Devnet Yellowstone settings must accept the License Key as secret x-token metadata: {error:?}")
},
};
assert_eq!(endpoint.metadata().len(), 1);
assert_eq!(endpoint.metadata()[0].key(), "x-token");
assert!(endpoint.metadata()[0].is_secret());
assert!(!endpoint.url().uses_tls(), "the documented OrbitFlare shared Devnet endpoint is intentionally HTTP/2 plaintext");
let endpoint_debug = format!("{endpoint:?}");
assert!(!endpoint_debug.contains("orbitflare.com"), "OrbitFlare endpoint URL must not appear in endpoint Debug");
assert!(!endpoint_debug.contains(x_token.as_str()), "OrbitFlare License Key x-token must not appear in endpoint Debug");
assert!(!endpoint_debug.contains("X-ORBIT-KEY"), "OrbitFlare Customer API credentials must not enter Yellowstone settings");
let channel = match ksp_onchain_transport_lib::YellowstoneGrpcChannel::connect(&endpoint).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("OrbitFlare Devnet Yellowstone authenticated connection must succeed: {error:?}"),
};
assert_eq!(channel.endpoint_name(), "orbitflare_devnet_yellowstone");
assert_eq!(channel.provider().as_str(), "orbitflare");
assert_eq!(channel.cluster().as_str(), "devnet");
let request = match slot_request() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("OrbitFlare Yellowstone slot request must be valid: {error:?}"),
};
let mut session = match channel.open_standard_subscribe(request).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("OrbitFlare Devnet standard Subscribe with License Key x-token must open: {error:?}"),
};
let deadline = tokio::time::sleep(std::time::Duration::from_secs(45));
tokio::pin!(deadline);
let mut slot_seen = false;
let mut ping_seen = false;
loop {
tokio::select! {
() = &mut deadline => break,
next_update = session.next_update() => {
let next_update = match next_update {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("OrbitFlare Devnet Yellowstone Subscribe update must decode: {error:?}"),
};
match next_update {
std::option::Option::Some(ksp_onchain_transport_lib::YellowstoneSubscribeUpdate::Slot(update)) => {
if update.slot() > 0 {
slot_seen = true;
}
},
std::option::Option::Some(ksp_onchain_transport_lib::YellowstoneSubscribeUpdate::Ping(_)) => ping_seen = true,
std::option::Option::Some(_) => {},
std::option::Option::None => break,
}
if slot_seen && ping_seen {
break;
}
},
}
}
let close_timeout_result = tokio::time::timeout(std::time::Duration::from_secs(7), session.close()).await;
let close_result = match close_timeout_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("KSP Yellowstone Subscribe close must remain bounded beyond the configured five-second provider half-close deadline: {error}")
},
};
match close_result {
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => {
assert_eq!(
error.code(),
ksp_onchain_transport_lib::ERROR_CODE_TIMEOUT,
"after live characterization, OrbitFlare close may time out waiting for provider half-close but must not fail for another reason: {error:?}"
);
},
}
assert!(slot_seen, "OrbitFlare Devnet Yellowstone must publish at least one non-zero confirmed slot during the 45-second characterization window");
assert!(
ping_seen,
"OrbitFlare Devnet must emit the standard Yellowstone server Ping during the 45-second characterization window before any provider-specific heartbeat is justified"
);
}

327
deltas/0.2.10/pre.001.md Normal file
View File

@@ -0,0 +1,327 @@
<!-- file: deltas/0.2.10/pre.001.md -->
<!-- version: 1 -->
# Delta `0.2.10-pre.001` — audit/sizing OrbitFlare Yellowstone gRPC
## 1. Base requise et vérifiée
Archive autoritaire fournie :
```text
khadhroony-solana-project-v0.2.9-full-from-gitea.zip
```
État vérifié :
```text
workspace.package.version = 0.2.9
deltas/0.2.9/rel.001.md présent
prompts/015-V0_2_10_START_PROMPT.md présent
metadata .git absente de l'archive Gitea
```
Cette livraison ouvre :
```text
workspace.package.version = 0.2.10-pre.1
commit attendu = v0.2.10-pre.001
aucun tag prerelease
```
## 2. Objet
`pre.001` exécute le gate documentaire obligatoire :
```text
lecture base/règles/architecture
baseline stable
réaudit OrbitFlare actuel
réaudit Yellowstone actuel
auth control-plane/data-plane
endpoints/network/security
Free Devnet gRPC
heartbeat
capabilities/unary/replay
limits/quotas
architecture N1/N2 immuable
threat model
sizing et forecast
```
Aucun code provider lourd, SDK OrbitFlare, metadata secrète ou heartbeat supplémentaire n'est ajouté.
## 3. Baseline opérateur acquise
Preuves fournies le 2026-08-25 sur `v0.2.9` :
```text
cargo fmt --all OK
python3 scripts/audit_rust_workspace_rules.py OK / clean
python3 scripts/audit_markdown_tables.py OK / 87 tables / 261 files
cargo check --workspace OK
cargo clippy --workspace --all-targets OK
cargo test --workspace OK
cargo tree -p ksp-onchain-transport-lib --duplicates fourni
```
Transport pendant le workspace test :
```text
383 unit
49 public_api
43 release_completeness
4 doctests
```
Les live smokes restent opt-in/ignored par défaut.
Le `cargo tree -p ksp-onchain-transport-lib` complet n'a pas encore été fourni et doit être rejoué avant commit.
## 4. Graphe de dépendances inspecté
Versions utiles visibles :
```text
yellowstone-grpc-proto 12.6.0
tonic 0.14.6
tonic-prost 0.14.6
prost 0.14.4
tokio 1.53.1
http 1.5.0
reqwest 0.13.4
```
Aucune divergence Tonic/Prost/Yellowstone ne justifie une intervention. Les doublons restants sont sans impact matériel pour OrbitFlare.
## 5. Réaudit OrbitFlare du 2026-08-25
### Free Devnet
Le pricing actuel confirme :
```text
Free 0 USD/mois
RPC 10 RPS
transactions 1 TPS
gRPC Devnet only
credit limits unlimited
```
Le CLI documente :
```text
RPC Devnet = http://devnet.rpc.orbitflare.com
gRPC Devnet = http://devnet.rpc.orbitflare.com:10000
```
Objectif opérationnel fermé : utiliser OrbitFlare comme provider gRPC Devnet gratuit pour les tests KSP futurs, en complément de PublicNode Mainnet/Testnet.
### Auth
Classification :
```text
X-ORBIT-KEY Customer API only
Bearer Customer API v2
RPC api_key HTTP RPC only
account API key ne prouve pas une auth Yellowstone
gRPC Dashboard token possible selon licence/service
x-token mécanisme Yellowstone plausible si token gRPC distinct
aucune metadata documentée par SDK Go pour endpoints régionaux
```
La clé de compte opérateur ne doit pas être fournie au dépôt ni injectée par supposition dans gRPC.
### Heartbeat
OrbitFlare recommande un `SubscribeRequest::Ping` périodique. Yellowstone upstream envoie déjà un `SubscribeUpdate::Ping` périodique et KSP N1 répond automatiquement par un Ping standard sans modifier le dernier full request mémorisé.
Décision :
```text
aucun timer OrbitFlare ajouté au moteur
aucun changement YellowstoneGrpcSessionSettings
aucune modification grpc_stream.rs
pre.002 doit observer le Ping serveur live OrbitFlare Devnet
```
Si ce Ping est observé, la policy standard N1 est suffisante. Sinon, une divergence provider doit être démontrée puis composée au-dessus de N1 sans le modifier.
### Limits
```text
50 connexions gRPC simultanées par IP sur shared
subscriptions par connexion annoncées unlimited
idle timeout environ 10 minutes
RESOURCE_EXHAUSTED au dépassement
backoff exponentiel conseillé
```
Ces valeurs restent provider-owned.
## 6. Réaudit Yellowstone actuel
État observé :
```text
release GitHub courante v15.1.2+solana.4.2.0
yellowstone-grpc-proto publié 12.6.0
```
Aucune évolution matérielle ne remet en cause le standard N2 de `0.2.9`. Aucune dependency n'est modifiée.
## 7. Invariant architectural supersédant l'hypothèse initiale
Décision opérateur explicite :
```text
le moteur gRPC ne change jamais pour un provider
le moteur WebSocket ne change jamais pour un provider
les fonctionnalités provider se composent au-dessus du moteur
```
Pour `0.2.10` :
```text
N1 moteur Yellowstone = IMMUTABLE
N2 standard Solana = IMMUTABLE
N3 OrbitFlare = profile/capability/facade seulement si besoin démontré
```
Le prompt `015` reste autoritatif sauf sur toute formulation qui permettrait de mettre une policy OrbitFlare directement dans le moteur partagé.
## 8. Config V3
La shape existante suffit :
```text
profile_id = orbitflare_devnet
provider = orbitflare
cluster = devnet
protocol = solana_yellowstone
url = http://devnet.rpc.orbitflare.com:10000
metadata = []
secret_metadata = []
```
Pas de V4. Pas de secret ajouté avant preuve live. Pas de champ heartbeat moteur.
## 9. Smoke prévu
`pre.002` doit commencer par un canari standard N2 :
```text
connect OrbitFlare Devnet Free sans metadata
Subscribe slots confirmed
recevoir Slot non nul
observer SubscribeUpdate::Ping pendant une fenêtre bornée de caractérisation
fermer proprement
```
Puis sonder séparément les unary et replay sans rendre le smoke minimal dépendant d'entitlements provider non garantis.
## 10. Forecast recalibré
Chemin standard probable :
```text
pre.001 audit/sizing
pre.002 Config Devnet + characterization smoke standard N2
pre.003 gate technique/live final
pre.004 réconciliation documentaire finale
pre.005 publication minimale
rel.001 stable
```
Chemin avec divergence provider démontrée :
```text
pre.001 audit/sizing
pre.002 Config Devnet + characterization smoke
pre.003 provider overlay minimal au-dessus de N1/N2
pre.004 gate technique/live final
pre.005 réconciliation documentaire finale
pre.006 publication minimale
rel.001 stable
```
## 11. Fichiers ajoutés
```text
docs/plans/017-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC_PLAN.md
docs/validation/013-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC.md
deltas/0.2.10/pre.001.md
```
## 12. Fichiers modifiés
```text
Cargo.toml
docs/000-README.md
docs/plans/000-README.md
docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md
docs/validation/000-README.md
```
## 13. Fichiers volontairement inchangés
```text
ROADMAP.md
CHANGELOG.md
.env.example
config/**
crates/**
docs/architecture/**
crates/ksp-onchain-transport-lib/README.md
crates/ksp-onchain-transport-lib/USAGE.md
```
Aucune implémentation provider ou Config runtime n'est introduite dans `pre.001`.
## 14. Sources externes réauditées
```text
https://docs.orbitflare.com/llms.txt
https://docs.orbitflare.com/cli
https://docs.orbitflare.com/authentication
https://docs.orbitflare.com/data-streaming/yellowstone
https://docs.orbitflare.com/data-streaming/yellowstone-quickstart
https://docs.orbitflare.com/sdk/go-grpc
https://orbitflare.com/pricing
https://orbitflare.com/products/rpc-nodes
https://orbitflare.com/products/solana-grpc
https://github.com/rpcpool/yellowstone-grpc
https://github.com/rpcpool/yellowstone-grpc/releases
https://github.com/rpcpool/yellowstone-grpc/blob/master/README.md
https://github.com/rpcpool/yellowstone-grpc/blob/master/yellowstone-grpc-proto/proto/geyser.proto
```
## 15. Validations de préparation
Le sandbox a exécuté :
```text
inspection archive/version/rel/prompt OK
lecture code N1/N2/Config et docs 0.2.9 OK
réaudit OrbitFlare/Yellowstone actuel OK
inspection cargo tree --duplicates fourni par opérateur OK
```
Le sandbox ne dispose pas de Cargo ; aucun `cargo check/clippy/test/tree` post-overlay n'est déclaré réussi localement.
## 16. Validation opérateur attendue avant commit
Comme `pre.001` change uniquement la version Cargo et la documentation :
```bash
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.10
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test --workspace
cargo tree -p ksp-onchain-transport-lib
cargo tree -p ksp-onchain-transport-lib --duplicates
```
Attendu : aucun changement de dependency et aucun changement sous `crates/`.

View File

@@ -0,0 +1,196 @@
<!-- file: deltas/0.2.10/pre.002-fix.001.md -->
<!-- version: 1 -->
# Delta `0.2.10-pre.002-fix.001` — OrbitFlare License Key `x-token`
## 1. Base et identité
Base exacte :
```text
0.2.10-pre.002 commité
```
Le fix porte une correction runtime/configuration de lauth provider. La version Cargo devient donc :
```text
workspace.package.version = 0.2.10-pre.2.fix.1
commit attendu = v0.2.10-pre.002-fix.001
aucun tag prerelease
```
## 2. Défaut démontré par le live `pre.002`
Le gate statique/workspace `pre.002` est vert. Le smoke live sans metadata a atteint OrbitFlare puis louverture du stream a échoué de manière sûre :
```text
grpc_operation = SubscribeOpen
grpc_status = Unauthenticated
grpc_code = The request does not have valid authentication credentials
```
Le Dashboard opérateur montre un produit `Solana Free` avec License Key `ORBIT-*` et `API Key Mode Active`.
La référence Yellowstone OrbitFlare précise ensuite le contrat data-plane :
```text
ORBITFLARE_LICENSE_KEY
-> metadata gRPC x-token
-> Yellowstone
```
Le `X-ORBIT-KEY` et le Bearer Device Flow restent des credentials Customer API et ne sont pas utilisés par Transport Yellowstone.
## 3. Correction Config V3
`config/std.transport.json` conserve le même endpoint et ajoute uniquement la metadata secrète requise :
```text
provider = orbitflare
cluster = devnet
protocol = solana_yellowstone
url = http://devnet.rpc.orbitflare.com:10000
metadata = []
secret_metadata = x-token <- ${KSP_SECRET_ORBITFLARE_DEVNET_GRPC_X_TOKEN}
```
Aucun format Config V4 nest créé.
`.env.example` inventorie désormais :
```text
KSP_SECRET_ORBITFLARE_DEVNET_GRPC_X_TOKEN
```
La valeur locale attendue est la License Key `ORBIT-*`. Une `X-ORBIT-KEY` Customer API ne doit jamais être placée dans cette variable.
## 4. Preuve Config déterministe
Le test `committed_v3_orbitflare_devnet_profile_maps_provider_neutral_yellowstone_grpc_with_license_x_token` injecte un canari secret via `ConfigEnvironment` et prouve :
```text
profil explicite orbitflare_devnet
companions HTTP/WS Devnet inchangés
endpoint Yellowstone OrbitFlare inchangé
metadata key = x-token
metadata classée secret
validation Transport PASS
URL provider absente du Debug
canari secret absent du Debug
X-ORBIT-KEY absent du Transport
```
## 5. Smoke live corrigé
`yellowstone_orbitflare_smoke.rs` ne dépend toujours pas de Config et ne lit aucun environnement. Il suit le pattern de sécurité déjà utilisé par les smokes PublicNode :
```text
une License Key lue sur stdin
aucun secret en argument de ligne de commande
YellowstoneGrpcMetadataEntry::secret("x-token", ...)
reconnect max_retries = 0
Subscribe slots confirmed
fenêtre Slot + Ping = 45 s
close borné
```
Le smoke vérifie également que la License Key napparaît pas dans `Debug`.
Commande opérateur :
```bash
read -rsp 'OrbitFlare License Key: ' ORBITFLARE_LICENSE_KEY; echo
printf '%s\n' "$ORBITFLARE_LICENSE_KEY" | cargo test -p ksp-onchain-transport-lib --test yellowstone_orbitflare_smoke -- --ignored --nocapture
unset ORBITFLARE_LICENSE_KEY
```
## 6. Invariant architectural
Toujours inchangés :
```text
src/grpc_settings.rs
src/grpc_channel.rs
src/grpc_unary.rs
src/grpc_subscribe.rs
src/grpc_stream.rs
```
Le standard Yellowstone N2 reste inchangé. Aucun SDK OrbitFlare runtime nest ajouté.
Lauth provider est entièrement composée par la surface metadata secrète déjà existante.
## 7. Heartbeat
Ce fix ne matérialise aucun heartbeat OrbitFlare.
Le rerun authentifié doit dabord qualifier :
```text
Slot non nul
SubscribeUpdate::Ping serveur
```
Si les deux sont observés, N1 répond déjà automatiquement au Ping standard et aucune couche provider heartbeat nest requise.
Si le stream authentifié fonctionne mais német aucun Ping serveur dans la fenêtre de caractérisation, `pre.003` devient une tranche provider-specific au-dessus de N1/N2. Il reste interdit dajouter un timer OrbitFlare au moteur gRPC.
## 8. Validation disponible dans lenvironnement de génération
Exécuté :
```text
python3 scripts/audit_rust_workspace_rules.py
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.10
Markdown table audit: clean (99 table(s), 99 file(s))
config/std.transport.json
JSON parse PASS
JSON Schema validation PASS
comparaison N1 avec pre.002
grpc_settings.rs inchangé
grpc_channel.rs inchangé
grpc_unary.rs inchangé
grpc_subscribe.rs inchangé
grpc_stream.rs inchangé
```
Aucune commande Cargo nest déclarée exécutée dans lenvironnement de génération.
## 9. Gates opérateur
Après application du fix :
```bash
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.10
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-config-lib
cargo test -p ksp-onchain-transport-lib
cargo test -p ksp-core-lib --test workspace_dependencies
cargo test --workspace
```
Puis exécuter le live authentifié décrit en section 5.
## 10. Fichiers ajoutés/modifiés
```text
Cargo.toml
.env.example
config/std.transport.json
crates/ksp-config-lib/unit_tests/transport.rs
crates/ksp-onchain-transport-lib/tests/yellowstone_orbitflare_smoke.rs
docs/plans/017-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC_PLAN.md
docs/validation/013-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC.md
deltas/0.2.10/pre.002-fix.001.md
```
Le delta commité `deltas/0.2.10/pre.002.md` reste immuable.

287
deltas/0.2.10/pre.002.md Normal file
View File

@@ -0,0 +1,287 @@
<!-- file: deltas/0.2.10/pre.002.md -->
<!-- version: 1 -->
# Delta `0.2.10-pre.002` — OrbitFlare Devnet Config + characterization smoke
## 1. Base et version
Base de travail :
```text
0.2.10-pre.001
```
Validation opérateur fournie sur cette base le 2026-08-25 :
```text
cargo fmt --all PASS
python3 scripts/audit_rust_workspace_rules.py PASS
python3 scripts/audit_markdown_tables.py PASS / 97 tables / 233 files
cargo check --workspace PASS
cargo clippy --workspace --all-targets PASS
cargo test --workspace PASS
cargo tree -p ksp-onchain-transport-lib fourni
cargo tree -p ksp-onchain-transport-lib --duplicates fourni
```
Le graphe confirme notamment :
```text
yellowstone-grpc-proto 12.6.0
tonic 0.14.6
tonic-prost 0.14.6
prost 0.14.4
tokio 1.53.1
```
Cette livraison passe à :
```text
workspace.package.version = 0.2.10-pre.2
commit attendu = v0.2.10-pre.002
aucun tag prerelease
```
## 2. Invariant architectural
Aucun moteur physique n'est modifié :
```text
src/grpc_settings.rs inchangé
src/grpc_channel.rs inchangé
src/grpc_unary.rs inchangé
src/grpc_subscribe.rs inchangé
src/grpc_stream.rs inchangé
```
Le standard Yellowstone N2 reste également inchangé.
OrbitFlare est matérialisé uniquement par :
```text
Config provider profile
provider-neutral Yellowstone smoke
provider validation/documentation
```
Aucun SDK OrbitFlare, second client Tonic, proto provider, heartbeat moteur ou metadata secrète n'est ajouté.
## 3. Profil Config V3 `orbitflare_devnet`
`config/std.transport.json` ajoute un profil dédié :
```text
profile_id = orbitflare_devnet
```
Il conserve les companions standards déjà utilisés sur Devnet :
```text
HTTP = https://api.devnet.solana.com
WS = wss://api.devnet.solana.com
```
et ajoute exactement un endpoint Yellowstone :
```text
name = orbitflare_solana_devnet_yellowstone
provider = orbitflare
cluster = devnet
protocol = solana_yellowstone
url = http://devnet.rpc.orbitflare.com:10000
metadata = []
secret_metadata = []
```
Le format reste V3. `.env.example` n'est pas modifié et aucune API key de compte OrbitFlare n'est reclassifiée comme credential gRPC.
`ksp-config-lib` ajoute un test déterministe qui prouve :
```text
sélection explicite orbitflare_devnet
companions HTTP/WS Devnet préservés
provider/cluster/protocol gRPC corrects
URL plaintext conservée telle quelle
aucune metadata inventée
Debug gRPC sans URL provider
absence de X-ORBIT-KEY dans Transport settings
```
## 4. Smoke OrbitFlare Devnet
Nouveau test opt-in :
```text
crates/ksp-onchain-transport-lib/tests/yellowstone_orbitflare_smoke.rs
```
Le test utilise directement les API publiques Yellowstone existantes de Transport, sans dépendre de Config :
```text
endpoint Devnet OrbitFlare sans metadata
reconnect max_retries = 0
Subscribe slots
commitment = confirmed
fenêtre de caractérisation = 45 s
preuve Slot non nul
preuve SubscribeUpdate::Ping serveur
close borné
```
Le `Ping` reçu reste traité par le moteur N1 déjà livré en `0.2.9`, qui répond automatiquement sur le même stream. Le test n'ajoute aucune policy heartbeat.
Commande live :
```bash
cargo test -p ksp-onchain-transport-lib --test yellowstone_orbitflare_smoke -- --ignored --nocapture
```
Interprétation :
```text
Slot + Ping PASS
OrbitFlare reste standard ; aucune façade/policy provider heartbeat requise
connection/auth failure
classifier le service/licence réel avant toute metadata
Slot PASS mais Ping absent
qualifier une divergence heartbeat provider avant toute implémentation spécifique
```
## 5. Audit Markdown étendu
`scripts/audit_markdown_tables.py` passe en version 2 et contrôle désormais aussi l'équivalent structurel de :
```text
\n\s*\n\s*\n
```
c'est-à-dire au moins deux lignes vides consécutives hors fenced code block.
Le contrôle :
```text
considère une ligne whitespace-only comme vide
signale fichier + première ligne + longueur de la séquence
n'interprète pas les blancs internes aux ``` / ~~~ fenced blocks
ignore les répertoires générés ou tiers en parcours récursif
```
Répertoires récursifs exclus :
```text
.git
.idea
.venv
__pycache__
dist
node_modules
target
```
Cette exclusion rend le résultat déterministe entre une archive Gitea propre et un checkout développeur contenant des `node_modules`. Le nombre de fichiers audités peut donc diminuer par rapport à l'ancien script sans signifier une perte de couverture KSP-owned.
Une seule séquence de deux lignes vides existait dans le scope KSP actif de l'archive : elle est supprimée dans `crates/ksp-onchain-transport-lib/README.md`.
`docs/rules/RULES_DOCUMENTATION.md` formalise `DOC-BLANK-001` à `DOC-BLANK-003`, y compris l'immuabilité des anciens deltas publiés.
## 6. Documents de plan/validation
Le plan 017 et la validation 013 passent en version 2 pour enregistrer :
```text
pre.001 operator gate fermé
profil orbitflare_devnet matérialisé
smoke de caractérisation matérialisé
N1/N2 inchangés
live Devnet encore PENDING
```
Aucun README/USAGE OrbitFlare durable n'est finalisé ici ; cette responsabilité reste réservée au couloir documentaire final.
## 7. Validation disponible dans l'environnement de génération
Exécuté sans Cargo :
```text
python3 scripts/audit_rust_workspace_rules.py
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.10
clean / 98 tables / 98 KSP-owned files
config/std.transport.json
JSON parse PASS
JSON Schema validation PASS
```
Le nouvel audit Markdown a aussi été caractérisé avec des fixtures temporaires :
```text
deux lignes vides hors fence FAIL attendu
plusieurs lignes vides dans fence PASS attendu
node_modules récursif ignoré attendu
```
Aucune commande Cargo n'est déclarée exécutée dans l'environnement de génération.
## 8. Gates opérateur `pre.002`
Exécuter :
```bash
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.10
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-config-lib
cargo test -p ksp-onchain-transport-lib
cargo test -p ksp-core-lib --test workspace_dependencies
cargo test --workspace
cargo tree -p ksp-onchain-transport-lib
cargo tree -p ksp-onchain-transport-lib --duplicates
```
Puis le live dédié :
```bash
cargo test -p ksp-onchain-transport-lib --test yellowstone_orbitflare_smoke -- --ignored --nocapture
```
`pre.002` ne ferme son gate provider que lorsque le résultat live est classifié. Un échec d'auth ou l'absence de `Ping` ne doit jamais être corrigé par modification du moteur N1.
## 9. Fichiers ajoutés/modifiés
```text
Cargo.toml
config/std.transport.json
crates/ksp-config-lib/unit_tests/transport.rs
crates/ksp-onchain-transport-lib/README.md
crates/ksp-onchain-transport-lib/tests/yellowstone_orbitflare_smoke.rs
docs/plans/017-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC_PLAN.md
docs/rules/RULES_DOCUMENTATION.md
docs/validation/013-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC.md
scripts/audit_markdown_tables.py
deltas/0.2.10/pre.002.md
```
## 10. Suite conditionnelle
Si le live retourne `Slot + Ping PASS` :
```text
pre.003 = gate technique/live final
```
Si une divergence provider réelle est démontrée :
```text
pre.003 = tranche provider-specific minimale au-dessus de N1/N2
pre.004 = gate technique/live final
```
Le couloir documentaire final puis la prerelease de publication restent distincts dans les deux cas.

252
deltas/0.2.10/pre.003.md Normal file
View File

@@ -0,0 +1,252 @@
<!-- file: deltas/0.2.10/pre.003.md -->
<!-- version: 1 -->
# Delta `0.2.10-pre.003` — gate technique/live final OrbitFlare Yellowstone
## 1. Base requise
Base directe attendue :
```text
livraison : 0.2.10-pre.002-fix.001
Cargo : 0.2.10-pre.2.fix.1
commit : v0.2.10-pre.002-fix.001
```
Identité de cette tranche :
```text
livraison : 0.2.10-pre.003
Cargo : 0.2.10-pre.3
commit : v0.2.10-pre.003
aucun tag prerelease
```
Cette prerelease est exclusivement le couloir **technique/live final** de `0.2.10`. Elle ne porte aucune réconciliation README/USAGE/plan/validation, aucune finalisation CHANGELOG/ROADMAP et aucun prompt `0.2.11`.
## 2. Gate acquis de `pre.002-fix.001`
La validation opérateur reçue le 2026-08-25 ferme le défaut d'auth classifié dans `pre.002`.
Le gate déterministe est intégralement vert :
```text
cargo fmt --all PASS
python3 scripts/audit_rust_workspace_rules.py clean / 0 export candidate / clean
python3 scripts/audit_markdown_tables.py PASS, 99 tables / 99 files
cargo check --workspace PASS
cargo clippy --workspace --all-targets PASS
cargo test --workspace PASS
Config unit 114/114 PASS
Config ownership 5/5 PASS
Config public API 15/15 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
```
Le smoke OrbitFlare Devnet authentifié est également vert :
```text
endpoint http://devnet.rpc.orbitflare.com:10000
auth License Key ORBIT-* -> secret metadata x-token
Subscribe PASS
Slot > 0 PASS
server Ping PASS
close borné PASS
résultat 1 passed / 0 failed
```
Cette preuve ferme la branche de divergence heartbeat : OrbitFlare émet le `SubscribeUpdate::Ping` Yellowstone standard et le moteur N1 `0.2.9` possède déjà la réponse automatique déterministe. Aucun heartbeat provider, aucune façade OrbitFlare et aucune modification N1/N2 ne sont nécessaires.
## 3. Responsabilité de `pre.003`
`pre.003` ne matérialise aucun nouveau comportement. Elle rejoue et ferme les preuves techniques finales après le fix d'auth :
```text
version Cargo prerelease synchronisée
workspace complet sans warning
invariants Rust/Markdown
Config V3 OrbitFlare x-token non régressée
Yellowstone N1/N2 non régressés
HTTP 52 current + 14 historical non régressés
WebSocket standard et Helius non régressés
smoke live OrbitFlare Devnet final
smokes PublicNode Mainnet/Testnet finaux si credentials opérateur disponibles
graphes Cargo Transport et doublons inspectés
aucun SDK OrbitFlare runtime
aucune dépendance Transport -> Config
```
Le payload de `pre.003` ne change ni code runtime, ni test, ni Config, ni schema, ni dépendance. Le seul changement technique est le signal de version Cargo obligatoire pour une prerelease non-fix.
## 4. Invariants à fermer
Les fichiers moteur Yellowstone restent hors modification :
```text
crates/ksp-onchain-transport-lib/src/grpc_settings.rs
crates/ksp-onchain-transport-lib/src/grpc_channel.rs
crates/ksp-onchain-transport-lib/src/grpc_unary.rs
crates/ksp-onchain-transport-lib/src/grpc_subscribe.rs
crates/ksp-onchain-transport-lib/src/grpc_stream.rs
```
Le gate doit confirmer :
```text
N1 gRPC provider-neutral inchangé
N2 Yellowstone standard inchangé
OrbitFlare composé par endpoint + provider + cluster + x-token Config V3
X-ORBIT-KEY absent du data-plane Yellowstone
License Key jamais exposée par Debug/CLI
pas de format Config V4
pas de provider proto
pas de second raw Tonic client
pas de orbitflare-sdk runtime
```
## 5. Gate déterministe final
Après application de `pre.003` :
```bash
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.10
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-config-lib
cargo test -p ksp-onchain-transport-lib
cargo test -p ksp-core-lib --test workspace_dependencies
cargo test --workspace
```
Seuils minimaux attendus :
```text
Config unit 114
Config ownership 5
Config public API 15
Transport unit 383
Transport public API 49
Transport release completeness 43
Transport doctests 4
workspace dependency canary 3
```
Toute régression imputable à KSP ouvre `pre.003-fix.NNN`. Elle ne doit pas être repoussée dans le couloir documentaire `pre.004`.
## 6. Smoke OrbitFlare Devnet final
Saisir la License Key sans l'inscrire dans l'historique :
```bash
read -rsp 'OrbitFlare License Key: ' ORBITFLARE_LICENSE_KEY; echo
printf '%s\n' "$ORBITFLARE_LICENSE_KEY" \
| cargo test -p ksp-onchain-transport-lib --test yellowstone_orbitflare_smoke -- --ignored --nocapture
unset ORBITFLARE_LICENSE_KEY
```
Attendu :
```text
orbitflare_devnet_yellowstone_streams_slots_and_emits_standard_server_ping_with_license_x_token ... ok
1 passed
0 failed
0 ignored
```
Le smoke doit encore prouver dans une même session bornée :
```text
auth x-token acceptée
Subscribe standard ouvert
Slot non nul reçu
SubscribeUpdate::Ping standard observé
fermeture bornée
```
Un incident externe OrbitFlare/réseau démontré comme tel est documenté comme `EXTERNAL BLOCK`. Un échec de mapping, metadata, moteur, protocol ou fermeture imputable à KSP ouvre un fix technique.
## 7. PublicNode et graphes finaux
Les smokes PublicNode Mainnet/Testnet restent les canaris provider du standard livré par `0.2.9`. Si les deux credentials opérateur sont disponibles pendant ce gate, les rejouer :
```bash
read -rsp 'PublicNode Mainnet Yellowstone x-token: ' PUBLICNODE_MAINNET_TOKEN; echo
read -rsp 'PublicNode Testnet Yellowstone x-token: ' PUBLICNODE_TESTNET_TOKEN; echo
printf '%s\n%s\n' "$PUBLICNODE_MAINNET_TOKEN" "$PUBLICNODE_TESTNET_TOKEN" \
| cargo test -p ksp-onchain-transport-lib --test yellowstone_publicnode_smoke -- --ignored --nocapture
unset PUBLICNODE_MAINNET_TOKEN PUBLICNODE_TESTNET_TOKEN
```
Attendu lorsque rejoué :
```text
2 passed
0 failed
0 ignored
```
Inspecter ensuite :
```bash
cargo tree -p ksp-onchain-transport-lib
cargo tree -p ksp-onchain-transport-lib --duplicates
cargo tree --duplicates
```
Points obligatoires :
```text
yellowstone-grpc-proto 12.6.x sur la stack attendue
Tonic/Prost KSP-owned sans seconde génération incompatible
aucun yellowstone-grpc-client runtime
aucun orbitflare-sdk runtime
aucune dépendance ksp-onchain-transport-lib -> ksp-config-lib
aucun tracing direct Transport
aucun nouveau doublon introduit par 0.2.10
```
Les smokes PublicNode ne deviennent pas un blocker si leurs credentials ne sont simplement pas disponibles au moment du gate : leur non-régression déterministe reste couverte par les suites Transport/Config et le code live n'a pas changé. En revanche, tout échec effectivement observé lors d'un rerun doit être classifié, jamais ignoré.
## 8. Critère de fermeture technique
`pre.003` est fermée lorsque :
```text
gate déterministe final vert
OrbitFlare Devnet Subscribe -> Slot + Ping live PASS
graphes Cargo finaux inspectés
N1 inchangé
N2 inchangé
Config V3 x-token cohérente et redacted
aucun provider overlay nécessaire
aucun heartbeat provider nécessaire
aucun SDK OrbitFlare ajouté
aucun défaut technique reporté vers pre.004
```
Une fois ce gate acquis, la prochaine tranche est obligatoirement :
```text
0.2.10-pre.004 — réconciliation documentaire finale
```
Elle portera plan, validation, README/USAGE et références durables. Elle ne doit pas porter de nouveau correctif technique.
## 9. Modifications de cette livraison
```text
Cargo.toml
# version: 264 -> 265
workspace.package.version: 0.2.10-pre.2.fix.1 -> 0.2.10-pre.3
deltas/0.2.10/pre.003.md
ajout du présent gate technique/live final
```
Aucun autre fichier n'est modifié.

128
deltas/0.2.10/pre.004.md Normal file
View File

@@ -0,0 +1,128 @@
<!-- file: deltas/0.2.10/pre.004.md -->
<!-- version: 1 -->
# Delta `0.2.10-pre.004` — réconciliation documentaire finale OrbitFlare Yellowstone
## 1. Base
```text
0.2.10-pre.003
```
Cette tranche est exclusivement le couloir de réconciliation documentaire finale. Le gate technique/live de `pre.003` est vert ; aucun correctif runtime, Config exécutable, test, schema, dependency ou feature n'est introduit ici.
## 2. Version technique
Comme toute prerelease non-fix, la version Cargo est synchronisée mécaniquement :
```text
workspace.package.version = 0.2.10-pre.4
```
Aucune sémantique runtime ne change.
## 3. Preuve technique réconciliée
Le gate opérateur `pre.003` du 2026-08-25 est fermé avec :
```text
cargo fmt --all PASS
Rust workspace audit PASS / 0 export candidate
Markdown audit PASS / 99 tables / 100 files
cargo check --workspace PASS
cargo clippy --workspace --all-targets PASS
Config unit 114/114 PASS
Config ownership 5/5 PASS
Config public API 15/15 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
OrbitFlare Devnet Subscribe + Slot + Ping 1/1 PASS, 5.19 s
```
Les graphes Cargo direct Transport, doublons Transport et doublons workspace ont été inspectés. La pile reste :
```text
yellowstone-grpc-proto 12.6.0
tonic / tonic-prost 0.14.6
prost / prost-types 0.14.4
```
Aucun `yellowstone-grpc-client` runtime, aucun `orbitflare-sdk` runtime et aucune dépendance `Transport -> Config` n'ont été ajoutés.
## 4. OrbitFlare final
Contrat durable réconcilié :
```text
Devnet endpoint http://devnet.rpc.orbitflare.com:10000
auth License Key ORBIT-* -> secret metadata x-token
Config secret KSP_SECRET_ORBITFLARE_DEVNET_GRPC_X_TOKEN
Subscribe standard Yellowstone N2
live Slot non nul + server Ping
N1 inchangé
N2 inchangé
provider facade non nécessaire
heartbeat N3 non nécessaire
Config V4 non nécessaire
```
Le premier échec `pre.002` sans metadata reste conservé dans son delta historique comme caractérisation ayant conduit au fix d'auth `pre.002-fix.001`.
## 5. Documents réconciliés
```text
docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md
docs/plans/017-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC_PLAN.md
docs/validation/013-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC.md
crates/ksp-onchain-transport-lib/README.md
crates/ksp-onchain-transport-lib/USAGE.md
docs/000-README.md
```
Les anciennes affirmations `PENDING` sont remplacées par les résultats `pre.003` réellement observés. README/USAGE documentent désormais le profil OrbitFlare Devnet et son smoke live durable.
## 6. Frontière avec `pre.005`
Cette tranche ne touche pas :
```text
CHANGELOG.md
ROADMAP.md
prompt suivant
```
La décision sur la release suivante est prise pendant cette réconciliation, sans générer encore son prompt : Helius LaserStream gRPC est reporté dans les TODO Yellowstone sans numéro de release faute d'accès live raisonnable ; la séquence active avance et `0.2.11` devient `off-chain price transport`, suivie de `0.2.12` Price Desk + intégration Wallet Desk, `0.2.13` interface/wire foundation et `0.2.14` program-api foundation. Le futur `ROADMAP.md` de `pre.005` devra refléter exactement ce report et cette renumérotation.
Une fois cette décision prise, `pre.005` reste strictement publication-minimal : prompt suivant + `CHANGELOG.md` + `ROADMAP.md` + signal Cargo + delta.
Si un défaut technique de `0.2.10` est découvert après ce gate documentaire, il ne doit pas être glissé dans `pre.005`; ouvrir une nouvelle prerelease du couloir approprié puis rejouer les tranches finales nécessaires.
## 7. Fichiers modifiés/ajoutés
```text
Cargo.toml
docs/000-README.md
docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md
docs/plans/017-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC_PLAN.md
docs/validation/013-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC.md
crates/ksp-onchain-transport-lib/README.md
crates/ksp-onchain-transport-lib/USAGE.md
deltas/0.2.10/pre.004.md
```
## 8. Gate opérateur
```bash
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.10
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test --workspace
```
Aucun smoke live ni `cargo tree` n'est à rejouer si le delta est appliqué exactement : aucune surface technique ou dépendance n'est modifiée.

132
deltas/0.2.10/pre.005.md Normal file
View File

@@ -0,0 +1,132 @@
<!-- file: deltas/0.2.10/pre.005.md -->
<!-- version: 1 -->
# Delta `0.2.10-pre.005` — préparation de publication minimale
## 1. Base
```text
0.2.10-pre.004
```
Cette tranche est exclusivement le couloir de préparation de publication. Elle ne modifie aucun code, test, schema, config, README, USAGE, plan, validation ou règle normative.
## 2. Gate `pre.003` et réconciliation `pre.004`
Le gate technique final `pre.003` a confirmé :
```text
Rust workspace audit PASS
Markdown audit PASS
cargo check --workspace PASS
cargo clippy --workspace --all-targets PASS
cargo test --workspace PASS
OrbitFlare live Subscribe -> Slot+Ping PASS en 5.19 s
graphes Cargo inspectés
```
`pre.004` a ensuite réconcilié les documents durables : plan/validation OrbitFlare, README/USAGE Transport, index documentaire et séquence fonctionnelle. Helius LaserStream gRPC a été reporté dans les TODO faute d'accès live raisonnable sans abonnement payant, sans remise en cause de sa compatibilité wire Yellowstone largement observée.
## 3. Version technique
```text
workspace.package.version = 0.2.10-pre.5
```
La modification est mécanique ; aucune sémantique runtime ne change.
## 4. `CHANGELOG.md`
Le changelog stable est préparé pour `0.2.10 — OrbitFlare Yellowstone gRPC` avec les faits consolidés :
```text
N1/N2 Yellowstone inchangés
Config V3 orbitflare_devnet
x-token = License Key via secret_metadata
premier live sans metadata -> Unauthenticated
fix auth documenté
live Subscribe -> Slot + standard Ping PASS
second live final PASS
aucun heartbeat/provider SDK/Config V4 ajouté
workspace + graphes finaux verts
```
## 5. `ROADMAP.md`
`0.2.10` passe à l'état réalisé. Helius LaserStream gRPC quitte la séquence numérotée et rejoint les TODO Yellowstone. La séquence active devient :
```text
0.2.11 off-chain price transport
0.2.12 Price Desk + intégration prix Wallet Desk
0.2.13 interface/wire foundation
0.2.14 program-api foundation
```
## 6. Audit préparatoire de la future `0.2.11`
Avant de rédiger le prompt suivant, un audit externe rapide au 2026-08-25 a comparé plusieurs familles de sources :
```text
CoinGecko agrégateur, REST Demo, multi quote USD/EUR
Jupiter V3 prix Solana heuristique issu des swaps, USD seulement
Pyth Hermes oracle, REST/SSE, publish_time/confidence, auth en transition immédiate
Birdeye market data Solana, spot/batch/historique, API key
DexScreener prix DEX par pair/pool, accès public courant
```
Ce snapshot ne choisit pas le provider. Il démontre surtout que les sémantiques diffèrent et que `0.2.11-pre.001` doit auditer/choisir explicitement le contrat prix, le modèle numérique, la fraîcheur et le provider initial avant code lourd.
Pyth annonce un changement d'authentification le 2026-08-26 à 16:00 UTC ; le prompt exige donc un réaudit immédiat de Pyth à l'ouverture et interdit de figer le snapshot de `pre.005`.
## 7. Prompt `0.2.11`
Ajout de :
```text
prompts/016-V0_2_11_START_PROMPT.md
```
Le prompt ouvre `0.2.11 — Off-chain price transport` uniquement depuis `v0.2.10` et impose `pre.001 = lecture + audit actuel providers + matrice sémantique + numeric model + threat model + sizing + planification`.
Le scope initial reste volontairement borné :
```text
ksp-offchain-transport-lib
prix SOL/USD + SOL/EUR
premier provider live-testable sans abonnement payant dédié
Config -> Off-chain Transport
pas de Price Desk
pas de Wallet Desk
pas d'OHLCV/historique/quotes/metadata/multi-provider complexe
```
## 8. Fichiers modifiés/ajoutés
```text
Cargo.toml
CHANGELOG.md
ROADMAP.md
prompts/016-V0_2_11_START_PROMPT.md
deltas/0.2.10/pre.005.md
```
Aucun autre fichier ne doit changer dans cette tranche.
## 9. Gate opérateur
Comme cette tranche ne modifie aucun code/runtime/config/schema :
```bash
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.10
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test --workspace
```
Aucun smoke live ni `cargo tree` n'est à rejouer si le delta est appliqué exactement.
## 10. Suite
Si ce gate est vert, `0.2.10-rel.001` est la seule étape restante. Elle est limitée à la mécanique de publication stable : version Cargo `0.2.10`, delta `rel.001`, commit final et tag stable `v0.2.10`, sans rattrapage technique ou documentaire.

197
deltas/0.2.10/rel.001.md Normal file
View File

@@ -0,0 +1,197 @@
<!-- file: deltas/0.2.10/rel.001.md -->
<!-- version: 1 -->
# Delta `0.2.10-rel.001` — publication stable OrbitFlare Yellowstone gRPC
## 1. Base requise
Base directe attendue :
```text
0.2.10-pre.005
workspace.package.version = 0.2.10-pre.5
```
Commit attendu pour cette livraison :
```text
v0.2.10-rel.001
```
Tag stable attendu après validation :
```text
v0.2.10
```
## 2. Responsabilité de `rel.001`
Cette livraison effectue uniquement la mécanique de publication stable définie par le cycle KSP.
Elle :
- passe `workspace.package.version` de `0.2.10-pre.5` à `0.2.10` ;
- ajoute le présent delta `rel.001` ;
- ne corrige ni code, ni test, ni config/schema, ni README/USAGE, ni plan/validation, ni règle, ni prompt, ni `CHANGELOG.md`, ni `ROADMAP.md`.
Tout défaut appartenant à un couloir antérieur doit renvoyer vers une prerelease appropriée ; `rel.001` n'est jamais une tranche de rattrapage.
## 3. Preuves techniques acquises avant publication
Le gate technique/live final `0.2.10-pre.003` a été validé le **25 août 2026** avec :
```text
cargo fmt --all PASS
python3 scripts/audit_rust_workspace_rules.py PASS
General Rust rule audit clean
Rust export completeness audit 0 candidate(s)
KSP workspace Rust rule audit clean
python3 scripts/audit_markdown_tables.py PASS
cargo check --workspace PASS
cargo clippy --workspace --all-targets PASS
cargo test -p ksp-config-lib PASS
cargo test -p ksp-onchain-transport-lib PASS
cargo test -p ksp-core-lib --test workspace_dependencies PASS
cargo test --workspace PASS
```
Les suites directement concernées restent notamment :
```text
Config unit 114 passed
Transport unit 383 passed
Transport public API 49 passed
Transport release completeness 43 passed
Transport doctests 4 passed
```
Le smoke live final OrbitFlare a également été rejoué en `pre.003` :
```text
OrbitFlare Devnet Yellowstone
x-token = License Key
Subscribe -> Slot + standard server Ping
1/1 PASS en 5.19 s
```
Les graphes Cargo finaux ont été inspectés dans le même couloir. `pre.004` et `pre.005` n'ont ensuite modifié aucune source runtime, dépendance, config ou schema.
## 4. Version stable publiée
La version Cargo devient :
```text
workspace.package.version = 0.2.10
```
La release stable publiée est :
```text
0.2.10 — OrbitFlare Yellowstone gRPC
```
Les acquis fonctionnels et documentaires finalisés avant `rel.001` comprennent :
```text
réutilisation stricte du moteur N1 Yellowstone gRPC existant
réutilisation stricte du contrat N2 Yellowstone standard existant
aucune modification provider du moteur gRPC
profil Config V3 orbitflare_devnet
endpoint Devnet http://devnet.rpc.orbitflare.com:10000
authentification gRPC x-token via License Key secrète
premier smoke sans metadata -> Unauthenticated, caractérisé puis corrigé
live authentifié Subscribe -> Slot + standard Ping PASS
aucun heartbeat provider supplémentaire
aucun SDK OrbitFlare ajouté
aucun Config V4
```
La release confirme donc qu'OrbitFlare compose au-dessus de N1/N2 par configuration provider et metadata secrète, sans divergence protocolaire nécessitant une façade gRPC dédiée.
## 5. Fichiers ajoutés
```text
deltas/0.2.10/rel.001.md
```
## 6. Fichiers modifiés
```text
Cargo.toml
```
## 7. Fichiers supprimés
```text
aucun
```
## 8. Validations de préparation du delta
Contrôles statiques de l'overlay :
```text
Cargo.toml version 0.2.10
payload rel.001 2 fichiers exactement
aucun fichier de rattrapage présent
```
Aucune nouvelle compilation n'est requise pour construire l'archive elle-même. Le gate stable final doit toutefois être exécuté après application du delta et avant le commit/tag.
## 9. Validation stable après application
Exécuter avant publication :
```bash
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.10
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test --workspace
```
Le changement étant limité au signal de version Cargo et au présent delta :
- aucun smoke live OrbitFlare n'est à rejouer si l'overlay appliqué est exactement `0.2.10-rel.001` ;
- aucun `cargo tree` n'est à rejouer ;
- aucun smoke PublicNode n'est à rejouer.
## 10. Commit et tag stable
Après succès du gate stable :
```text
commit : v0.2.10-rel.001
tag : v0.2.10
```
Aucun tag intermédiaire `rel.001` n'est requis.
Commandes Git typiques :
```bash
git add Cargo.toml deltas/0.2.10/rel.001.md
git commit -m "v0.2.10-rel.001"
git tag v0.2.10
git push
git push origin v0.2.10
```
## 11. Suite
Après publication du tag stable `v0.2.10`, ouvrir :
```text
0.2.11-pre.001 — Off-chain price transport
```
avec :
```text
prompts/016-V0_2_11_START_PROMPT.md
```
`pre.001` reste une tranche d'audit actuel des providers, sémantiques de prix, modèle numérique, fraîcheur/provenance, threat model et sizing avant toute implémentation lourde.
Helius LaserStream gRPC reste reporté dans les TODO Yellowstone jusqu'à disponibilité d'un accès live pertinent ; il ne fait plus partie de la séquence numérotée active.

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
<!-- file: docs/plans/000-README.md -->
<!-- version: 60 -->
<!-- version: 61 -->
# Plans KSP
@@ -24,7 +24,8 @@ Un plan décrit le périmètre, les décisions déjà acquises, les questions ou
- [`013-V0_2_6_WALLET_DESK_PLAN.md`](013-V0_2_6_WALLET_DESK_PLAN.md) — plan historique clôturé de la release stable `0.2.6 — Wallet Desk`, ouvert par `pre.001`, étendu en `pre.015``pre.017` au wire binaire `.kspwallet` V2, aux APIs multi-version et à la migration V1 -> V2, puis fermé par `pre.018`/`fix.001` avec le runtime Tauri packagé et le build final vert avant publication `rel.001`.
- [`014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md`](014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md) — plan historique clôturé de la release stable `0.2.7 — WebSocket Solana standard`, ouvert par `pre.001`, exécuté jusquà `pre.014`, corrigé documentairement par `pre.014-fix.001` puis publié par `rel.001`; il conserve linventaire officiel 18 méthodes, le modèle session/subscription, le threat model, les preuves de compliance/smoke/dépendances et la préparation de `0.2.8`.
- [`015-V0_2_8_HELIUS_LASERSTREAM_WEBSOCKET_PLAN.md`](015-V0_2_8_HELIUS_LASERSTREAM_WEBSOCKET_PLAN.md) — plan historique clôturé de la release stable `0.2.8 — Helius LaserStream WebSocket`, ouvert par `pre.001`, fermé techniquement/documentairement par `pre.011` puis publié par `rel.001`; il conserve la surface finale Helius `7 standard + transaction`, heartbeat/Config/secrets, lhistorique des fixes heartbeat, la stratégie live architecture-safe, les graphes Cargo finaux et la préparation du prompt `0.2.9`.
- [`016-V0_2_9_YELLOWSTONE_GRPC_PLAN.md`](016-V0_2_9_YELLOWSTONE_GRPC_PLAN.md) — plan actif de `0.2.9 — Yellowstone gRPC standard/provider-neutral`, ouvert par `pre.001`; il fixe la stratégie `yellowstone-grpc-proto + Tonic KSP`, la surface `Subscribe + 7 unary`, lexclusion de `SubscribeDeshred`, Config V3, les règles replay/backpressure et les smokes gratuits PublicNode puis OrbitFlare.
- [`016-V0_2_9_YELLOWSTONE_GRPC_PLAN.md`](016-V0_2_9_YELLOWSTONE_GRPC_PLAN.md) — plan historique clôturé de la release stable `0.2.9 — Yellowstone gRPC standard/provider-neutral`, publiée par `rel.001`; il fixe le moteur N1, le standard N2, Config V3, le replay prudent et la validation PublicNode Mainnet/Testnet.
- [`017-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC_PLAN.md`](017-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC_PLAN.md) — plan actif de `0.2.10 — OrbitFlare Yellowstone gRPC`, ouvert par `pre.001`; il confirme le Devnet gRPC gratuit, rend N1/N2 immuables pour la release, classe auth/heartbeat/limits et dimensionne un chemin standard prioritaire sans code provider inutile.
Le `pre.001` de chaque release fonctionnelle peut introduire son propre plan détaillé lorsque la release s'ouvre.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md -->
<!-- version: 88 -->
<!-- version: 89 -->
# Séquence des releases fonctionnelles KSP
@@ -37,7 +37,7 @@ Séquence par défaut :
0.1.4 ksp-app-config-desk
```
`0.1.1`, `0.1.2`, `0.1.3`, `0.1.4` et `0.2.0` à `0.2.5` sont désormais des releases stables.
`0.1.1`, `0.1.2`, `0.1.3`, `0.1.4` et `0.2.0` à `0.2.9` sont désormais des releases stables.
`0.1.4 — ksp-app-config-desk` établit le modèle de référence des futures applications Tauri KSP sans déplacer la logique Config dans l'application. Sa matrice finale a été validée avant `rel.001`, avec le build Tauri exécuté en dernière opération.
@@ -365,11 +365,10 @@ Par défaut :
0.2.8 Helius LaserStream WebSocket
0.2.9 Yellowstone gRPC engine + 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 visualization desk + intégration prix dans Wallet Desk
0.2.14 interface/wire foundation
0.2.15 program-api foundation
0.2.11 off-chain price transport
0.2.12 price visualization desk + intégration prix dans Wallet Desk
0.2.13 interface/wire foundation
0.2.14 program-api foundation
```
`0.2.1-pre.001` a appliqué le gate de sizing et refusé le scope HTTP monolithique initial : l'inventaire du 2026-08-17 contient 52 méthodes courantes et 14 méthodes Deprecated historiques. Ce premier delta avait réparti la couverture typée sur `0.2.1``0.2.6`. `0.2.1-pre.001-fix.001` recalibre ensuite les 48 méthodes restantes sur trois releases complémentaires `0.2.2``0.2.4`, soit trois sessions nominales au maximum si chaque release utilise sa session complète. Si une release se clôt plus vite que prévu, la même session peut enchaîner la suivante après clôture complète de la précédente et nouveau gate de sizing positif. Un Wallet Desk utile doit pouvoir lire le solde du wallet : `getBalance` fait donc partie des quatre canaris de la foundation `0.2.1`, avant Wallet. Les transports live arrivent ensuite ; Interface/Program restent préparés avant les couches de données décodées.
@@ -475,26 +474,24 @@ Le réaudit final Helius du 23 août 2026 retient `helius_laserstream` comme pro
### `0.2.9` — Yellowstone gRPC engine + standard Solana + PublicNode
Mission : construire un moteur client Yellowstone gRPC partagé dans `ksp-onchain-transport-lib`, exposer la façade Solana Yellowstone standard puis valider une première intégration concrète PublicNode Mainnet/Testnet sans recopier le moteur.
Mission accomplie : `0.2.9-rel.001` publie le moteur Yellowstone N1, le standard Solana N2, Config Transport V3 et la première intégration provider-neutral PublicNode. Les smokes Mainnet et Testnet ont validé `Subscribe -> Slot` avec metadata `x-token` secrète sans façade PublicNode spécifique.
Le `pre.001` est un gate de sizing : chaque prerelease vise 1520 minutes de travail effectif et la release complète doit rester clôturable dans une seule session. Si cette clôture devient incertaine, `0.2.9` est scindée avant implémentation lourde supplémentaire.
Cette release devient la fondation stable des providers suivants. Le moteur physique et la surface standard ne sont pas redéfinis par une release provider : une divergence provider se compose au-dessus, ou devient un blocker architectural explicite si elle ne peut pas être composée proprement.
La séparation cible reprend le modèle WebSocket : moteur physique partagé -> protocole standard -> adaptations provider. PublicNode est la première intégration provider. Le moteur n'est jamais dupliqué ; le standard n'est réutilisé que pour les capacités réellement compatibles. Une couche provider peut restreindre le standard ou porter des extensions wire/lifecycle explicites.
### `0.2.10` — OrbitFlare Yellowstone gRPC
### `0.2.10``0.2.11` — providers Yellowstone retenus
Mission active : obtenir en priorité un provider Yellowstone gratuit sur **Devnet** pour les tests futurs, puis matérialiser uniquement les divergences OrbitFlare réellement prouvées. Le plan Free actuel annonce `gRPC Devnet only` et le CLI documente `http://devnet.rpc.orbitflare.com:10000`.
```text
0.2.10 OrbitFlare Yellowstone gRPC
0.2.11 Helius LaserStream gRPC
```
`0.2.10-pre.001` fixe un invariant renforcé : N1 gRPC et N2 Yellowstone restent immuables. L'auth Customer API, le token gRPC éventuel, le heartbeat, les quotas et les capabilities sont classifiés séparément. Le premier smoke doit tenter le standard N2 sans secret sur Devnet, observer un `Slot` et caractériser le `Ping` serveur Yellowstone avant toute façade/policy provider.
Chaque release réutilise le moteur de `0.2.9`, audite son delta avec Yellowstone upstream et matérialise uniquement les restrictions/extensions réelles du provider.
Le plan actif est [`017-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC_PLAN.md`](017-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC_PLAN.md) et la matrice active [`../validation/013-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC.md`](../validation/013-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC.md).
### TODO/IDEAS — providers Yellowstone en attente
Aucune release n'est réservée pour :
```text
TODO Helius LaserStream gRPC — réauditer lorsque l'accès live gRPC est disponible
TODO eRPC
TODO Triton
TODO Alchemy
@@ -507,23 +504,25 @@ IDEAS NodeFlare
IDEAS autres providers à réauditer
```
Helius LaserStream gRPC est explicitement reporté : l'audit 2026-08-25 indique une compatibilité wire Yellowstone élevée et une intégration KSP probablement légère au-dessus de N1/N2, mais aucun accès live Helius gRPC n'est actuellement disponible sans plan payant. Il ne reçoit donc plus de numéro de release tant que l'auth, les endpoints, le Subscribe, le Ping, le replay/from_slot et les erreurs provider ne peuvent pas être validés live. Les extensions Helius non standard, notamment les preprocessed transactions, restent un sujet provider séparé.
Ces providers ne déplacent pas la séquence active et ne reçoivent ni façade, ni Config profile, ni smoke tant qu'une décision explicite d'implémentation n'est pas prise.
### `0.2.12` / `0.2.13` — Off-chain price + app
### `0.2.11` / `0.2.12` — Off-chain price + app
`0.2.12` introduit `ksp-offchain-transport-lib` avec au minimum SOL/USD et SOL/EUR via une abstraction indépendante du premier provider.
`0.2.11` introduit `ksp-offchain-transport-lib` avec au minimum SOL/USD et SOL/EUR via une abstraction indépendante du premier provider.
`0.2.13` ajoute une petite application desk de visualisation/validation. Après stabilisation de cette application spécialisée, la même release doit intégrer la capacité de prix offchain dans `ksp-app-wallet-desk` sans dupliquer la récupération/normalisation appartenant au composant spécialisé.
`0.2.12` ajoute une petite application desk de visualisation/validation. Après stabilisation de cette application spécialisée, la même release doit intégrer la capacité de prix offchain dans `ksp-app-wallet-desk` sans dupliquer la récupération/normalisation appartenant au composant spécialisé.
Metadata HTTP/IPFS/Arweave viendra au premier besoin Metadata réel.
### `0.2.14` — Interface foundation
### `0.2.13` — Interface foundation
`ksp-interface-lib` devient la façade wire officielle et expose une API publique wire utilisable par les implementations officielles et externes.
Aucune `ksp-interface-api` séparée n'est retenue pour l'instant.
### `0.2.15` — Program API foundation
### `0.2.14` — Program API foundation
Introduire `ksp-program-api`, sans suffixe `-lib`, comme contrat d'extension Program.

View File

@@ -0,0 +1,659 @@
<!-- file: docs/plans/017-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC_PLAN.md -->
<!-- version: 4 -->
# Plan `0.2.10` — OrbitFlare Yellowstone gRPC
**Statut courant : le gate technique/live final `0.2.10-pre.003` est vert. OrbitFlare Devnet est validé avec la License Key `ORBIT-*` portée par la metadata secrète `x-token` ; le smoke final observe un Slot non nul puis le `SubscribeUpdate::Ping` Yellowstone standard et ferme proprement la session. N1 et N2 restent inchangés, aucune façade provider ni heartbeat N3 nest nécessaire. `0.2.10-pre.004` est exclusivement la réconciliation documentaire finale.**
## 1. Base et autorité
Base stable autoritaire :
```text
v0.2.9
```
État vérifié dans l'archive Gitea fournie :
```text
workspace.package.version = 0.2.9
deltas/0.2.9/rel.001.md présent
prompts/015-V0_2_10_START_PROMPT.md présent
plan 016 Yellowstone présent
validation 012 Yellowstone présente
Config Transport V3 présente
PublicNode Mainnet + Testnet présents
```
Ordre d'autorité pendant `0.2.10` :
```text
règles normatives KSP
archive stable v0.2.9 et code réellement livré
décisions du présent plan et validation 013
deltas immuables de 0.2.10
prompt 015 pour les contraintes non supersédées
sources provider/upstream actuelles pour les faits externes
```
Le présent gate supersède une hypothèse du prompt : une divergence lifecycle OrbitFlare ne doit jamais être résolue par modification du moteur N1. Si elle ne peut pas être proprement composée au-dessus du moteur, elle devient un blocker/split explicite.
## 2. Mission opérationnelle réelle
L'objectif prioritaire de `0.2.10` est de disposer d'au moins un provider Yellowstone **gratuit et durable sur Solana Devnet** pour les validations futures de KSP.
PublicNode/Allnodes a déjà validé le standard KSP sur :
```text
Mainnet
Testnet
```
OrbitFlare complète cette couverture avec :
```text
Devnet
```
Le pricing OrbitFlare observé le 2026-08-25 confirme :
```text
plan Free 0 USD/mois
RPC 10 RPS
transactions 1 TPS
gRPC Devnet only
credit limits unlimited
carte bancaire non requise selon la page d'accueil
```
Sources principales :
```text
https://orbitflare.com/pricing
https://orbitflare.com/products/rpc-nodes
https://docs.orbitflare.com/cli
```
Mainnet OrbitFlare payant n'est pas un gate de cette release. Le résultat minimal utile est un smoke Yellowstone standard KSP sur l'endpoint Devnet gratuit.
## 3. Invariant architectural renforcé
### 3.1 N1 gRPC immuable
Les modules qui constituent le moteur Yellowstone livré par `0.2.9` ne doivent pas être modifiés pour OrbitFlare :
```text
src/grpc_settings.rs
src/grpc_channel.rs
src/grpc_unary.rs
src/grpc_subscribe.rs
src/grpc_stream.rs
```
Le même principe vaut durablement pour les moteurs physiques :
```text
moteur HTTP provider-neutral
moteur WebSocket provider-neutral
moteur gRPC provider-neutral
```
Une release provider peut modifier :
```text
provider facade
provider policy
provider capability projection
Config provider profile
provider smoke/compliance
provider-specific tests
```
Elle ne modifie pas le moteur pour faire rentrer une différence fournisseur.
### 3.2 N2 standard Yellowstone immuable
La surface Solana Yellowstone standard publiée par `0.2.9` reste la seule surface wire de référence :
```text
Subscribe
SubscribeReplayInfo
Ping
GetLatestBlockhash
GetBlockHeight
GetSlot
IsBlockhashValid
GetVersion
accounts
slots
transactions
transactions_status
blocks
blocks_meta
entry
commitment
accounts_data_slice
ping
from_slot
9 variantes SubscribeUpdate
```
OrbitFlare ne redéfinit aucun DTO, filtre, unary, request ou update standard.
### 3.3 N3 provider uniquement si divergence démontrée
Architecture cible :
```text
OrbitFlare N3
-> standard Yellowstone N2 inchangé
-> moteur gRPC N1 inchangé
```
Une façade OrbitFlare publique n'est créée que si un consumer KSP doit réellement voir une restriction ou une extension provider. Un simple endpoint, un `provider = orbitflare`, une policy Config ou un smoke ne suffisent pas à justifier une nouvelle API publique.
## 4. Baseline opérateur `v0.2.9`
Preuves fournies le 2026-08-25 :
| Gate | Résultat |
|-------------------------------------------|------------------------------------------|
| `cargo fmt --all` | PASS |
| audit Rust workspace | PASS, 0 candidate export |
| audit Markdown tables | PASS, 97 tables et 233 files sur `pre.1` |
| `cargo check --workspace` | PASS |
| `cargo clippy --workspace --all-targets` | PASS |
| `cargo test --workspace` | PASS |
| Transport unit | 383 sur 383 PASS |
| Transport public API | 49 sur 49 PASS |
| Transport release completeness | 43 sur 43 PASS |
| Config unit | 113 sur 113 PASS |
| PublicNode Yellowstone live | ignored opt-in comme attendu |
| `cargo tree --duplicates` Transport | fourni et inspecté |
| `cargo tree -p ksp-onchain-transport-lib` | fourni sur `0.2.10-pre.1` |
Versions visibles dans le graphe fourni :
```text
yellowstone-grpc-proto 12.6.0
tonic 0.14.6
tonic-prost 0.14.6
prost 0.14.4
tokio 1.53.1
http 1.5.0
reqwest 0.13.4
```
Aucune seconde génération Tonic/Prost/Yellowstone incompatible n'est observée. Les doublons restants ne justifient aucun changement pour `0.2.10`.
## 5. Réaudit Yellowstone upstream du 2026-08-25
État courant observé :
```text
release GitHub courante v15.1.2+solana.4.2.0
publication 2026-08-18
yellowstone-grpc-proto publié 12.6.0
stack proto prost 0.14 / tonic 0.14
```
Sources :
```text
https://github.com/rpcpool/yellowstone-grpc/releases
https://github.com/rpcpool/yellowstone-grpc/blob/master/README.md
https://github.com/rpcpool/yellowstone-grpc/blob/master/yellowstone-grpc-proto/proto/geyser.proto
https://crates.io/crates/yellowstone-grpc-proto
```
Aucune évolution matérielle n'impose de changer le contrat KSP de `0.2.9`. Il n'y a donc aucun bump de dependency Yellowstone/Tonic/Prost prévu dans `0.2.10`.
## 6. Endpoint et network OrbitFlare
Les sources actuelles distinguent plusieurs formes de service.
| Élément | Observation actuelle | Décision KSP |
|------------------------|-----------------------------------------------|---------------------------------------------|
| Devnet gRPC | `http://devnet.rpc.orbitflare.com:10000` | premier endpoint live à tester |
| endpoint régional | `http://{region}.rpc.orbitflare.com:10000` | descriptif, pas de fallback inventé |
| endpoint licence/dédié | `https://your-endpoint.grpc.orbitflare.com` | utiliser uniquement si Dashboard le fournit |
| `http` | HTTP/2 plaintext | ne jamais présenter comme TLS |
| `https` | HTTP/2 TLS | conserver tel quel |
| Mainnet | disponible selon plan/service | non requis pour le gate gratuit |
| Devnet | explicitement documenté | **IN** |
| Testnet | CLI accepte le label mais endpoint non prouvé | non requis, ne rien inventer |
Sources :
```text
https://docs.orbitflare.com/cli
https://docs.orbitflare.com/sdk/go-grpc
https://docs.orbitflare.com/data-streaming/yellowstone-quickstart
```
L'endpoint Dashboard opérateur reste autoritaire lorsqu'il existe. KSP ne transforme jamais automatiquement une URL `http` en `https`, ne change pas de région et ne construit pas un hostname provider non documenté.
## 7. Auth OrbitFlare : classification live corrigée
Le live `pre.002` et les sources OrbitFlare permettent désormais de séparer les credentials sans supposition.
| Credential ou mécanisme | Usage observé | Classification après `pre.002` |
|-------------------------|---------------------------|------------------------------------------------------|
| `X-ORBIT-KEY` | Customer API | control-plane, interdit sur Yellowstone |
| Bearer Device Flow | Customer API v2 | control-plane |
| `api_key` RPC | Solana HTTP RPC | data-plane HTTP |
| License Key `ORBIT-*` | produit Solana Free | data-plane provider |
| metadata `x-token` | Yellowstone gRPC | transport prouvé de la License Key |
| aucune metadata | premier smoke KSP | rejetée `Unauthenticated` au `SubscribeOpen` |
| IP whitelist | autre mode dauth produit | non retenu pour le service opérateur en API Key Mode |
La référence Yellowstone OrbitFlare donne explicitement le modèle suivant :
```text
ORBITFLARE_LICENSE_KEY
-> metadata gRPC x-token
-> Yellowstone
```
Le Dashboard opérateur confirme parallèlement que le produit `Solana Free` est en `API Key Mode Active`, donc utilisable depuis toute IP avec la License Key. Le `X-ORBIT-KEY` et le Bearer issus du Device Flow restent réservés au Customer API.
Le CLI OrbitFlare courant ne constitue pas un canari gRPC dauth fiable : son `ping` ouvre le canal sans injecter la License Key et reçoit `invalid x-token: api key not found`. Ce défaut du CLI ne modifie pas le contrat provider documenté.
Décision :
```text
ne jamais versionner la License Key réelle
ne jamais injecter X-ORBIT-KEY dans Yellowstone
représenter la License Key par secret_metadata x-token dans Config V3
faire lire le secret du smoke par stdin, jamais par argument CLI
ne modifier ni N1 ni N2 pour cette auth provider
```
## 8. Heartbeat : réconciliation KSP / Yellowstone / OrbitFlare
### 8.1 Faits upstream
Yellowstone upstream documente que :
```text
les load balancers peuvent fermer un stream si le client reste silencieux
le serveur Yellowstone envoie un SubscribeUpdate::Ping périodique
le client peut répondre par un SubscribeRequest::Ping
le serveur répond alors par SubscribeUpdate::Pong
```
Source :
```text
https://github.com/rpcpool/yellowstone-grpc/blob/master/README.md
```
### 8.2 Faits OrbitFlare
OrbitFlare documente :
```text
idle timeout partagé environ 10 minutes
client ping recommandé toutes les 30 secondes
produit gRPC recommandation 15 à 30 secondes
SDK Go PingInterval default 10 secondes
SDK Go MaxMissedPongs 3
```
Sources :
```text
https://docs.orbitflare.com/data-streaming/yellowstone
https://docs.orbitflare.com/authentication
https://docs.orbitflare.com/sdk/go-grpc
https://orbitflare.com/products/solana-grpc
```
### 8.3 Couverture KSP déjà présente
Le moteur `0.2.9` possède déjà exactement la réponse standard :
```text
SubscribeUpdate::Ping reçu
-> send_automatic_ping(...)
-> SubscribeRequest::Ping envoyé sur le stream existant
-> request de subscription mémorisée inchangée
```
Cette propriété est testée déterministiquement par le moteur N1.
### 8.4 Décision `pre.001`
La décision initiale de ne pas modifier le moteur est confirmée par le live final.
Le smoke authentifié `pre.002-fix.001`, puis son rerun dans `pre.003`, ont observé le `SubscribeUpdate::Ping` standard sur OrbitFlare Devnet :
```text
OrbitFlare server Ping live
+
KSP N1 automatic Ping reply deterministic test
=
activité bidirectionnelle standard suffisante sans code provider supplémentaire
```
Résultat final :
```text
heartbeat OrbitFlare N3 non nécessaire
façade provider non nécessaire
YellowstoneGrpcSessionSettings inchangé
grpc_stream.rs inchangé
latest_request reconnect sémantique N1 préservée
```
Il reste interdit d'ajouter un timer dans `grpc_stream.rs` ou `YellowstoneGrpcSessionSettings` pour OrbitFlare. La recommandation provider dun ping client périodique ne devient pas une policy globale tant que le chemin standard serveur Ping -> réponse automatique N1 satisfait le service live.
Le point de sécurité initial reste durable : envoyer naïvement une requête `ping` seule via la mutation publique remplacerait la dernière subscription mémorisée pour reconnect. Si un futur provider exige un heartbeat proactif indépendant, cette policy doit être composée au-dessus de N1 sans corrompre cette sémantique.
## 9. Capabilities OrbitFlare
### 9.1 Streaming documenté
OrbitFlare documente actuellement :
```text
accounts
transactions
slots
blocks
blocks_meta
entry
commitment
accounts_data_slice
ping
```
Le CLI/SDK montre également les filtres account/transaction/slot/block standard nécessaires aux canaris principaux.
### 9.2 Surface non encore prouvée provider
Ces capacités existent dans N2 KSP mais n'ont pas été exhaustivement sondées sur le plan OrbitFlare Free pendant `0.2.10` :
```text
SubscribeReplayInfo
Ping unary
GetLatestBlockhash
GetBlockHeight
GetSlot
IsBlockhashValid
GetVersion
from_slot et retention réelle
transactions_status complet
champs récents compressed/cuckoo/token expansion selon endpoint déployé
```
La clôture de `0.2.10` ne transforme pas l'absence de probe en absence de support. Le live final requis portait sur le chemin opérationnel utile à la release : authentification, standard Subscribe, Slot, Ping serveur et close borné.
Classification durable :
```text
standard KSP disponible oui dans N2
provider support live prouvé Subscribe slots + commitment + Ping
provider entitlement éventuel provider-owned
reste unknown / non exhaustivement testé
```
Aucune capacité N2 n'est supprimée du standard global à cause d'une restriction ou d'un inconnu OrbitFlare.
## 10. Limits et quotas utiles
La documentation actuelle des services partagés indique :
| Limite | Valeur observée | Traitement KSP |
|-----------------------------|------------------------------------------|---------------------------------------------|
| connexions gRPC simultanées | 50 par IP | information provider, pas borne N1 |
| portée du cap | globale par IP et régions gRPC partagées | éviter les reconnect storms |
| subscriptions par connexion | unlimited | ne pas convertir en garantie universelle |
| idle timeout | environ 10 minutes | chemin Ping standard validé live |
| dépassement | gRPC `RESOURCE_EXHAUSTED` | status distant safe existant |
| reconnect conseillé | exponential backoff | KSP N1 possède déjà un budget/backoff borné |
Ces valeurs commerciales/opérationnelles ne deviennent pas des constantes du standard KSP.
## 11. Config V3
Le schéma V3 existant représente directement le contrat live corrigé :
```text
provider = orbitflare
cluster = devnet
protocol = solana_yellowstone
url = http://devnet.rpc.orbitflare.com:10000
metadata = []
secret_metadata = x-token <- ${KSP_SECRET_ORBITFLARE_DEVNET_GRPC_X_TOKEN}
```
La variable porte la License Key `ORBIT-*` du produit Solana Free. Elle ne porte jamais `X-ORBIT-KEY`.
Décision :
```text
pas de format_version 4
pas de champ region si lURL suffit
pas de heartbeat dans grpc_defaults
secret provider géré par Config V3 existante
redaction KSP obligatoire dans safe/debug projections
```
Le profil `orbitflare_devnet` conserve les companions HTTP/WS Solana Devnet standards et ajoute exactement un endpoint gRPC authentifié par secret metadata `x-token`.
## 12. Stratégie live Devnet
### 12.1 Résultat du premier canari `pre.002`
Le smoke initial utilisait le standard N2 directement, sans Config et sans metadata. Le canal physique a atteint le service OrbitFlare, puis louverture du Subscribe a retourné :
```text
grpc_operation = SubscribeOpen
grpc_status = Unauthenticated
grpc_code = The request does not have valid authentication credentials
```
Cette preuve ferme lhypothèse « Devnet Free sans metadata ».
### 12.2 Canari corrigé `pre.002-fix.001`
Le même test reste provider-neutral et ne dépend toujours pas de Config. Il lit une seule License Key sur stdin puis construit la metadata secrète standard :
```text
endpoint = http://devnet.rpc.orbitflare.com:10000
provider = orbitflare
cluster = devnet
protocol = solana_yellowstone
auth = x-token <- License Key lue sur stdin
request = Subscribe slots à commitment confirmed
preuve = au moins un Slot non nul + SubscribeUpdate::Ping
close = borné
```
La valeur secrète n'apparaît ni dans Debug ni dans la ligne de commande.
Résultats opérateur :
```text
pre.002-fix.001 Subscribe -> Slot + Ping PASS en 5.10 s
pre.003 final Subscribe -> Slot + Ping PASS en 5.19 s
```
Le second passage ferme le gate live sur la version `0.2.10-pre.3` utilisée pour la clôture technique.
### 12.3 Preuve heartbeat
Le `SubscribeUpdate::Ping` standard est observé live sur OrbitFlare Devnet avec l'auth correcte.
La combinaison suivante est donc prouvée :
```text
Subscribe authentifié
-> Slot non nul
-> SubscribeUpdate::Ping serveur
-> chemin de réponse automatique N1 déjà couvert déterministiquement
-> close borné
```
Verdict final : aucune divergence heartbeat OrbitFlare ne justifie une façade ou une policy provider. Le moteur gRPC et le standard Yellowstone restent inchangés.
### 12.4 Unary et replay
Les sept unary N2, `SubscribeReplayInfo` et la retention `from_slot` n'ont pas été rendus obligatoires pour le gate OrbitFlare Free.
Cette décision évite de confondre :
```text
complétude du standard KSP N2
support/entitlement d'un provider particulier
preuve minimale nécessaire à la mission Devnet de 0.2.10
```
Le moteur N1 reste capable d'utiliser `SubscribeReplayInfo` quand il est disponible et tolère son indisponibilité pendant reconnect en poursuivant avec le `from_slot` conservateur demandé. Aucun comportement provider n'est introduit pour combler un inconnu de service.
## 13. Threat model recalibré
Le gate retient :
```text
Customer API key injectée sur le mauvais plan
gRPC token confondu avec X-ORBIT-KEY
secret metadata leak
endpoint Dashboard sensible
plaintext http présenté comme TLS
IP/network entitlement mal classifié
heartbeat ajouté au moteur global par erreur
ping provider écrasant le last accepted full request
ping flood
missed pong surinterprété
RESOURCE_EXHAUSTED et reconnect storm
50 connections par IP consommées par des smokes mal fermés
region failover changeant de node/fork
from_slot hors retention
provider unary unavailable
remote Status arbitraire
SDK OrbitFlare introduit sans nécessité
```
## 14. Surface de code autorisée et interdite
### 14.1 Interdit par défaut
```text
modification grpc_stream.rs
modification grpc_channel.rs pour une policy OrbitFlare
modification grpc_settings.rs pour un heartbeat OrbitFlare
modification des DTOs grpc_subscribe.rs
second raw Tonic client
orbitflare-sdk-rs dependency
provider-specific proto
Transport -> Config/env
```
### 14.2 Autorisé si nécessaire
```text
profil Config V3 orbitflare_devnet
smoke provider utilisant les APIs N2 existantes
provider capability descriptor sans wire nouveau
petite façade OrbitFlare au-dessus de N2 seulement si divergence live prouvée
provider-specific tests/compliance
README/USAGE dans la tranche documentaire finale
```
## 15. Forecast recalibré
Le chemin effectif de `0.2.10` est désormais figé :
```text
pre.001 audit actuel + architecture immuable N1/N2 + auth/endpoints + Free Devnet + heartbeat + sizing
pre.002 profil Config V3 OrbitFlare Devnet + smoke de caractérisation sans metadata
pre.002-fix.001 auth corrigée : License Key -> secret x-token ; live Subscribe -> Slot + Ping PASS
pre.003 gate technique/live final ; workspace + graphes + rerun OrbitFlare PASS
pre.004 réconciliation documentaire finale
pre.005 publication minimale après décision explicite sur la release suivante
rel.001 publication stable stricte
```
La branche de divergence heartbeat n'a pas été déclenchée. Aucun couloir supplémentaire n'est requis.
Le numéro n'est jamais le critère de clôture ; l'ordre technique, documentaire puis publication reste obligatoire.
## 16. Critères de split
Les critères de split définis pendant l'audit étaient :
```text
OrbitFlare n'émet pas le Ping standard et exige un Ping client proactif
lauth x-token fonctionne mais révèle une exigence provider supplémentaire non composable au-dessus de N1/N2
le service requiert un mécanisme TLS/channel absent du moteur stable
un method/filter standard est remplacé par une extension wire OrbitFlare
le replay/from_slot exige une policy provider visible au consumer
un failover provider impose une sémantique que N1 ne peut pas composer sans modification
```
**Aucun de ces critères n'a été déclenché par le gate live final.**
Le principe reste durable pour les providers futurs : si une divergence ne peut pas être composée proprement au-dessus de N1/N2, ne pas modifier le moteur pour le provider ; qualifier le blocker et replanifier l'architecture.
## 17. Critères de clôture
État à l'entrée de `pre.004` :
```text
Devnet OrbitFlare Free atteint par KSP PASS
N1 gRPC inchangé PASS
N2 Yellowstone inchangé PASS
aucun SDK OrbitFlare runtime PASS
provider/auth correctement classifiés PASS
aucune Customer API key sur le data-plane Yellowstone PASS
heartbeat live classifié PASS
Config V3 cohérente sans nouveau format PASS
smoke provider architecture-safe PASS
PublicNode non régressé déterministiquement PASS
Yellowstone standard non régressé PASS
HTTP 52+14 non régressé PASS
WebSocket standard 18/18 non régressé PASS
Helius WebSocket non régressé PASS
workspace complet vert PASS
graphes Cargo inspectés PASS
réconciliation documentaire séparée EN COURS pre.004
publication minimale séparée À FAIRE pre.005
```
Le gate technique est donc fermé. La stabilité `0.2.10` reste conditionnée à la validation documentaire `pre.004`, au couloir publication-minimal `pre.005`, puis à `rel.001`.
## 18. Release suivante
La décision préalable à `pre.005` est désormais prise. L'ancien forecast :
```text
0.2.11 — Helius LaserStream gRPC
```
est reporté dans les TODO Yellowstone sans numéro de release. L'audit du 2026-08-25 conclut que la surface Helius reste largement wire-compatible Yellowstone et pourrait probablement se composer au-dessus de N1/N2, mais l'accès gRPC Helius exige actuellement un plan payant que l'opérateur ne retient pas uniquement pour ce test. La release provider est donc différée jusqu'à disponibilité d'un accès live permettant de valider réellement auth, endpoints, Subscribe, Ping, replay/from_slot et erreurs provider. Les extensions Helius non standard restent un sujet séparé.
La séquence active avance d'un cran :
```text
0.2.11 — off-chain price transport
0.2.12 — Price Desk + intégration prix Wallet Desk
0.2.13 — interface/wire foundation
0.2.14 — program-api foundation
```
Décision de publication :
```text
aucun prompt suivant dans pre.004
aucun changement CHANGELOG/ROADMAP dans pre.004
Helius gRPC -> TODO futur sans numéro
0.2.11 -> off-chain price transport
pre.005 préparera seulement le prompt 0.2.11 + CHANGELOG + ROADMAP
```
Quel que soit le provider futur, l'invariant reste le même : N1 gRPC et N2 Yellowstone sont des fondations stables ; les différences provider se composent au-dessus et ne modifient jamais le moteur pour satisfaire un fournisseur.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/rules/RULES_DOCUMENTATION.md -->
<!-- version: 5 -->
<!-- version: 6 -->
# Règles de documentation
@@ -41,6 +41,12 @@ Les règles `DOC-*` s'appliquent aux documents Markdown internes et à leur orga
- **DOC-TABLE-005** — Lorsquune modification touche une ligne dun tableau, le tableau entier est réaligné avant livraison. `python3 scripts/audit_markdown_tables.py <fichiers-markdown-modifiés>` est le canari mécanique recommandé pour les fichiers concernés ; les blocs de code fenced ne sont pas interprétés comme des tableaux.
- **DOC-TABLE-006** — Limmuabilité des deltas déjà publiés prime sur un reformatage rétrospectif : un ancien fichier `deltas/` nest jamais réécrit uniquement pour satisfaire une règle de présentation introduite ultérieurement. Tout nouveau delta et tout autre tableau modifiable créé ou touché doivent en revanche respecter immédiatement `DOC-TABLE-001` à `DOC-TABLE-005`.
## Espacement vertical Markdown
- **DOC-BLANK-001** — Hors bloc de code fenced, un document Markdown KSP ne contient jamais plus dune ligne vide consécutive. Une ligne composée uniquement despaces ou de tabulations est considérée comme vide.
- **DOC-BLANK-002** — `python3 scripts/audit_markdown_tables.py <fichiers-ou-répertoires>` contrôle aussi les lignes vides multiples. Lors dun parcours récursif, les répertoires générés ou tiers tels que `node_modules`, `dist`, `target`, `.git`, `.idea`, `.venv` et `__pycache__` sont exclus afin que le résultat porte uniquement sur les Markdown KSP-owned. Les blocs de code fenced conservent librement leur espacement interne.
- **DOC-BLANK-003** — Les deltas déjà publiés restent immuables et ne sont jamais réécrits uniquement pour satisfaire `DOC-BLANK-001`. Les audits de release portent sur le répertoire de delta actif et sur les autres Markdown modifiables ; un audit historique explicite peut donc signaler des écarts hérités sans autoriser leur correction rétroactive.
## Contenu et exactitude
- **DOC-CONTENT-001** — Une documentation décrit l'état réellement décidé ou validé et distingue explicitement les hypothèses, propositions, TODO et questions ouvertes.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/validation/000-README.md -->
<!-- version: 24 -->
<!-- version: 25 -->
# Validations KSP
@@ -20,4 +20,5 @@ Documents :
- [`009-V0_2_6_WALLET_DESK_COMPLIANCE.md`](009-V0_2_6_WALLET_DESK_COMPLIANCE.md) — matrice finale validée de la release stable `0.2.6`, couvrant Wallet Desk, les wires V1/V2, la migration explicite, le runtime Tauri packagé, les frontières sécurité/ownership et le gate opérateur `pre.018-fix.001` avec build final Linux vert.
- [`010-V0_2_7_ONCHAIN_WEBSOCKET.md`](010-V0_2_7_ONCHAIN_WEBSOCKET.md) — matrice finale validée de la release stable `0.2.7`, ouverte par `pre.001`, fermée techniquement par `pre.014` puis publiée par `rel.001` : inventaire 9 subscribe + 9 unsubscribe, lifecycle borné, statuts unstable, compliance 18/18, non-régression HTTP 52+14, composition Config V2, smoke WebSocket Devnet et audit de dépendances.
- [`011-V0_2_8_HELIUS_LASERSTREAM_WEBSOCKET.md`](011-V0_2_8_HELIUS_LASERSTREAM_WEBSOCKET.md) — matrice finale validée de la release stable `0.2.8`, ouverte par `pre.001`, consolidée jusquà `pre.011` puis publiée par `rel.001` : Helius `account/logs/program/root/signature/slot/slotsUpdates + transaction`, `block/vote` absents, heartbeat provider, sécurité/redaction, lifecycle adversarial, non-régressions HTTP/WS, stratégie smoke live et graphes Cargo finaux.
- [`012-V0_2_9_YELLOWSTONE_GRPC.md`](012-V0_2_9_YELLOWSTONE_GRPC.md) — matrice active ouverte par `0.2.9-pre.001` : service Geyser courant, `Subscribe` standard, 7 unary, filtres/updates, dépendances/licence, replay/backpressure, Config V3 et interopérabilité PublicNode/OrbitFlare.
- [`012-V0_2_9_YELLOWSTONE_GRPC.md`](012-V0_2_9_YELLOWSTONE_GRPC.md) — matrice finale de la release stable `0.2.9` : service Geyser courant, `Subscribe` standard, 7 unary, filtres/updates, dépendances/licence, replay/backpressure, Config V3 et smoke PublicNode Mainnet/Testnet.
- [`013-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC.md`](013-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC.md) — matrice active ouverte par `0.2.10-pre.001` : Free Devnet, auth provider, endpoint security, heartbeat standard, capabilities, Config V3, smoke live et invariant N1/N2 immuable.

View File

@@ -0,0 +1,394 @@
<!-- file: docs/validation/013-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC.md -->
<!-- version: 4 -->
# Validation `0.2.10` — OrbitFlare Yellowstone gRPC
**Statut courant : `0.2.10-pre.003` ferme le gate technique/live final. Le workspace complet est vert, les graphes Cargo ont été inspectés et le smoke OrbitFlare Devnet authentifié par License Key `ORBIT-*` en secret `x-token` observe `Subscribe -> Slot + Ping` puis ferme proprement. N1/N2 restent inchangés ; `pre.004` réconcilie uniquement la documentation finale.**
## 1. Autorités
```text
RULES.md + docs/rules/*
archive stable v0.2.9
plan 016 et validation 012 pour N1/N2
plan 017 courant
prompt 015 pour les contraintes non supersédées
deltas/0.2.10/*
OrbitFlare docs/pricing/SDK/CLI actuels
Yellowstone upstream actuel
```
## 2. Baseline stable
| Gate | Résultat |
|------------------------------------------|----------------------------------|
| version de base | `0.2.9` PASS |
| `deltas/0.2.9/rel.001.md` | PASS |
| prompt `015` | PASS |
| `cargo fmt --all` | PASS |
| audit Rust | PASS |
| audit Markdown | PASS |
| `cargo check --workspace` | PASS |
| `cargo clippy --workspace --all-targets` | PASS |
| `cargo test --workspace` | PASS |
| Transport unit | 383 sur 383 PASS |
| Transport public API | 49 sur 49 PASS |
| Transport release completeness | 43 sur 43 PASS |
| Config unit | 113 sur 113 PASS |
| graph duplicates Transport | fourni, aucun conflit N1 visible |
| graph complet Transport | fourni sur `0.2.10-pre.1` PASS |
## 3. Invariant architecture
| Exigence | Verdict |
|----------------------------------------|----------|
| moteur gRPC N1 modifié pour OrbitFlare | **NO** |
| standard Yellowstone N2 modifié | **NO** |
| second client Tonic | **NO** |
| SDK OrbitFlare runtime | **NO** |
| provider séparé du protocol | **PASS** |
| Config -> Transport seulement | **PASS** |
| Transport -> Config/env | **PASS** |
| façade provider sans divergence | **NO** |
Toute modification de `grpc_stream.rs`, `grpc_channel.rs`, `grpc_settings.rs`, `grpc_unary.rs` ou `grpc_subscribe.rs` motivée uniquement par OrbitFlare invalide ce gate.
## 4. Pricing et réseau
| Point | Preuve actuelle | Verdict |
|----------------------|------------------------------------------|---------------|
| plan Solana Free | 0 USD par mois | **PASS** |
| gRPC Free | Devnet only | **PASS** |
| RPS Free | 10 | INFO |
| TPS Free | 1 | INFO |
| crédit | unlimited dans le RPS | INFO |
| Devnet gRPC endpoint | `http://devnet.rpc.orbitflare.com:10000` | **PASS** |
| Devnet live KSP | Subscribe authentifié, Slot + Ping | **PASS LIVE** |
| Mainnet gratuit | non documenté | **OUT** |
| Testnet endpoint | non prouvé | **UNKNOWN** |
Sources :
```text
https://orbitflare.com/pricing
https://orbitflare.com/products/rpc-nodes
https://docs.orbitflare.com/cli
```
## 5. Transport security
| Forme endpoint | Classification | Verdict |
|---------------------------------------|-----------------------|----------|
| `http://*.rpc.orbitflare.com:10000` | HTTP/2 plaintext | **PASS** |
| `https://*.grpc.orbitflare.com` | HTTP/2 TLS | **PASS** |
| transformation automatique http/https | interdite | **PASS** |
| endpoint Dashboard | autoritaire si fourni | **PASS** |
KSP ne prétend pas qu'un canal gRPC plaintext bénéficie d'une sécurité TLS implicite.
## 6. Auth classification
| Élément | Usage prouvé | Verdict |
|-----------------------|-----------------------------|------------------------------|
| `X-ORBIT-KEY` | Customer API | **CONTROL-PLANE** |
| Bearer Device Flow | Customer API v2 | **CONTROL-PLANE** |
| RPC `api_key` | Solana HTTP RPC | **HTTP ONLY** |
| License Key `ORBIT-*` | produit Solana Free | **DATA-PLANE PROVIDER** |
| metadata `x-token` | Yellowstone gRPC OrbitFlare | **REQUIRED / PROVEN DOC** |
| sans metadata | live KSP `pre.002` | **REJECTED UNAUTHENTICATED** |
| IP whitelist | autre mode produit | **NOT ACTIVE FOR OPERATOR** |
Preuve live `pre.002` :
```text
grpc_operation = SubscribeOpen
grpc_status = Unauthenticated
grpc_code = The request does not have valid authentication credentials
```
Preuve provider : la référence Yellowstone OrbitFlare construit le client avec `X_TOKEN = ORBITFLARE_LICENSE_KEY` et indique que le token est porté par la metadata gRPC `x-token`.
Le Dashboard opérateur classe le produit `Solana Free` en `API Key Mode Active`. La License Key est donc le secret data-plane à utiliser ; elle nest pas une `X-ORBIT-KEY` Customer API.
Le CLI OrbitFlare courant nest pas utilisé comme oracle positif pour lauth gRPC : après stockage de la License Key, `orbitflare ping --service grpc --network devnet` retourne `invalid x-token: api key not found`, et son implémentation courante ninjecte pas le secret dans le canal gRPC.
## 7. Heartbeat
| Point | Preuve | Verdict |
|----------------------------------------|---------------------------------------------|----------|
| OrbitFlare idle timeout partagé | environ 10 minutes | **PASS** |
| OrbitFlare recommande client Ping | 30 secondes | **PASS** |
| Yellowstone serveur Ping périodique | upstream documenté | **PASS** |
| KSP auto reply au serveur Ping | test N1 `0.2.9` et non-régression `pre.003` | **PASS** |
| moteur N1 à modifier | non | **PASS** |
| Ping serveur OrbitFlare Devnet observé | live `pre.002-fix.001` + `pre.003` | **PASS** |
| heartbeat provider additionnel | aucune divergence live | **NO** |
Le gate est fermé : OrbitFlare émet le Ping Yellowstone standard et le chemin de réponse automatique N1 reste suffisant sans timer provider.
## 8. Capabilities streaming
| Capability | Docs OrbitFlare | KSP N2 | Live provider |
|---------------------|-----------------|--------|--------------------------|
| accounts | oui | PASS | non sondé exhaustivement |
| slots | oui | PASS | **PASS** |
| transactions | oui | PASS | non sondé exhaustivement |
| transactions_status | partiel | PASS | non sondé exhaustivement |
| blocks | oui | PASS | non sondé exhaustivement |
| blocks_meta | oui | PASS | non sondé exhaustivement |
| entry | oui | PASS | non sondé exhaustivement |
| commitment | oui | PASS | **PASS confirmed** |
| accounts_data_slice | oui | PASS | non sondé exhaustivement |
| Subscribe Ping/Pong | oui | PASS | **PASS Ping serveur** |
La release prouve le chemin provider nécessaire à sa mission sans transformer les familles non sondées en capacités absentes.
## 9. Unary et replay
| Capability | N2 KSP | OrbitFlare docs | Verdict provider |
|---------------------|--------|-----------------|-----------------------------------|
| SubscribeReplayInfo | PASS | non prouvé | UNKNOWN / non bloquant |
| Ping unary | PASS | non prouvé | UNKNOWN / non bloquant |
| GetLatestBlockhash | PASS | non prouvé | UNKNOWN / non bloquant |
| GetBlockHeight | PASS | non prouvé | UNKNOWN / non bloquant |
| GetSlot | PASS | non prouvé | UNKNOWN / non bloquant |
| IsBlockhashValid | PASS | non prouvé | UNKNOWN / non bloquant |
| GetVersion | PASS | non prouvé | UNKNOWN / non bloquant |
| from_slot | PASS | non prouvé | N1 disponible, provider non sondé |
| retention | N/A | non documentée | UNKNOWN / provider-owned |
Une restriction ou un inconnu provider ne rétrograde jamais la disponibilité de la méthode dans le standard global KSP. Le reconnect N1 tolère déjà l'indisponibilité de `SubscribeReplayInfo` et poursuit avec le `from_slot` conservateur demandé.
## 10. Limits provider
| Limite | Valeur | Verdict |
|------------------------------|--------------------------|---------|
| concurrent gRPC connections | 50 par IP | INFO |
| subscriptions par connection | unlimited | INFO |
| cap partagé entre régions | oui | INFO |
| idle timeout | environ 10 minutes | INFO |
| dépassement | `RESOURCE_EXHAUSTED` | INFO |
| backoff recommandé | exponentiel jusqu'à 30 s | INFO |
Ces valeurs restent provider-owned et ne deviennent pas des validations génériques de `YellowstoneGrpcSessionSettings`.
## 11. Config V3
| Exigence | Verdict |
|---------------------------------------|----------|
| nouveau format Config nécessaire | **NO** |
| `provider = orbitflare` | **PASS** |
| `protocol = solana_yellowstone` | **PASS** |
| `cluster = devnet` | **PASS** |
| Devnet URL publique | **PASS** |
| secret metadata `x-token` | **PASS** |
| valeur License Key via `KSP_SECRET_*` | **PASS** |
| `X-ORBIT-KEY` dans Transport Config | **NO** |
| heartbeat dans `grpc_defaults` | **NO** |
Contrat final :
```text
secret_metadata = x-token <- ${KSP_SECRET_ORBITFLARE_DEVNET_GRPC_X_TOKEN}
```
Le test Config prouve la classification secrète et la redaction ; le live prouve que ce credential ouvre le Subscribe OrbitFlare Devnet.
## 12. Smoke Devnet final
Smoke durable :
```text
License Key lue sur stdin
YellowstoneGrpcMetadataEntry::secret("x-token", ...)
provider = orbitflare
cluster = devnet
url = http://devnet.rpc.orbitflare.com:10000
open standard Subscribe
request slots confirmed
receive Slot non nul
observe Ping serveur
close borné
```
| Preuve | Verdict |
|----------------------------------------------|--------------------------------|
| endpoint parsé par N1 | **PASS** |
| sans metadata | **FAIL EXPECTED / CLASSIFIED** |
| secret `x-token` représentable par Config V3 | **PASS** |
| License Key redacted | **PASS** |
| connect authentifié | **PASS LIVE** |
| Subscribe authentifié | **PASS LIVE** |
| Slot reçu | **PASS LIVE** |
| server Ping reçu | **PASS LIVE** |
| auto reply KSP déterministe | **PASS N1** |
| close borné | **PASS LIVE** |
Résultats :
```text
pre.002-fix.001 1/1 PASS en 5.10 s
pre.003 final 1/1 PASS en 5.19 s
```
## 13. Provider overlay gate
Verdict final : **aucune façade/policy OrbitFlare n'est nécessaire**.
Les conditions de réutilisation directe sont toutes satisfaites :
```text
standard Subscribe fonctionne
heartbeat standard fonctionne
Config V3 exprime endpoint/auth
aucune restriction consumer-visible ne requiert un type provider
```
Les interdictions restent durables :
```text
N1 ne change pas
N2 ne change pas
pas de raw Tonic
pas de SDK OrbitFlare
```
## 14. Non-régressions finales
| Surface | Baseline | Final |
|-----------------------------------|--------------|------------------------|
| HTTP current typed | 52 sur 52 | **PASS** |
| HTTP historical | 14 sur 14 | **PASS** |
| WebSocket standard | 18 sur 18 | **PASS** |
| Helius LaserStream WebSocket | stable 0.2.8 | **PASS** |
| Yellowstone N1/N2 | stable 0.2.9 | **PASS** |
| PublicNode Mainnet/Testnet | stable 0.2.9 | **PASS deterministic** |
| Config V1/V2/V3 backward readable | stable 0.2.9 | **PASS** |
| dependency firewall | stable 0.2.9 | **PASS** |
Les smokes PublicNode live n'ont pas été rejoués en `pre.003` car N1/N2 et les profils PublicNode n'ont pas changé ; leurs preuves live de `0.2.9` restent la baseline et la non-régression déterministe est verte.
## 15. Gate `pre.001`
| Critère | Verdict |
|-------------------------------------|----------------------|
| base stable `v0.2.9` | **PASS** |
| baseline fmt/audits/check/clippy | **PASS** |
| workspace test | **PASS** |
| graph duplicates | **PASS** |
| graph complet Transport | **PASS** |
| sources OrbitFlare actuelles | **PASS** |
| sources Yellowstone actuelles | **PASS** |
| Free Devnet gRPC confirmé | **PASS** |
| endpoints classifiés | **PASS** |
| auth planes distingués | **PASS** |
| heartbeat KSP vs provider classifié | **PASS FINAL** |
| capabilities auditées | **PASS** |
| replay/from_slot | **UNKNOWN PROVIDER** |
| N1 immutable | **PASS** |
| N2 immutable | **PASS** |
| Config V3 shape | **PASS** |
| smoke ownership | **PASS** |
| threat model | **PASS** |
| sizing/forecast | **PASS** |
| provider runtime lourd ajouté | **NO** |
Verdict final : architecture positive, inconnus provider non requis qualifiés, aucun split déclenché.
## 16. Gate `pre.002` commité
Validation opérateur du 2026-08-25 :
| Critère | Verdict |
|------------------------------------------|---------------------------------|
| Cargo `0.2.10-pre.2` | **PASS** |
| `cargo fmt --all` | **PASS** |
| audit Rust | **PASS** |
| audit Markdown | **PASS / 98 tables / 98 files** |
| `cargo check --workspace` | **PASS** |
| `cargo clippy --workspace --all-targets` | **PASS** |
| Config unit | **114 sur 114 PASS** |
| Transport unit | **383 sur 383 PASS** |
| Transport public API | **49 sur 49 PASS** |
| Transport release completeness | **43 sur 43 PASS** |
| workspace test | **PASS** |
| graphes Transport | **FOURNIS / PAS DE CONFLIT N1** |
| moteur N1 modifié | **NO** |
| standard N2 modifié | **NO** |
| live sans metadata | **FAIL CLASSIFIED** |
| `SubscribeOpen` | **UNAUTHENTICATED** |
Le failure live est la caractérisation historique qui a imposé le fix d'auth, pas une régression du moteur.
## 17. Gate `pre.002-fix.001`
| Critère | Verdict |
|-----------------------------------------------------------------|------------------------|
| Cargo `0.2.10-pre.2.fix.1` | **PASS** |
| `.env.example` inventorie le secret OrbitFlare | **PASS** |
| Config `secret_metadata` contient `x-token` | **PASS** |
| valeur provenant de `KSP_SECRET_ORBITFLARE_DEVNET_GRPC_X_TOKEN` | **PASS** |
| test Config prouve redaction et classification secrète | **PASS** |
| smoke lit la License Key sur stdin | **PASS** |
| secret absent des arguments CLI | **PASS** |
| moteur N1 modifié | **NO** |
| standard N2 modifié | **NO** |
| SDK OrbitFlare ajouté | **NO** |
| connect authentifié | **PASS LIVE** |
| `Subscribe -> Slot` live | **PASS LIVE / 5.10 s** |
| `SubscribeUpdate::Ping` live | **PASS LIVE** |
| close live | **PASS LIVE** |
Le fix ferme l'auth et confirme que le chemin standard suffit.
## 18. Gate `pre.003` technique/live final
Gate opérateur du 2026-08-25 :
| Critère | Verdict |
|------------------------------------------|----------------------------------|
| Cargo `0.2.10-pre.3` | **PASS** |
| `cargo fmt --all` | **PASS** |
| audit Rust | **PASS / 0 export candidate** |
| audit Markdown | **PASS / 99 tables / 100 files** |
| `cargo check --workspace` | **PASS** |
| `cargo clippy --workspace --all-targets` | **PASS** |
| Config unit | **114 sur 114 PASS** |
| Config ownership | **5 sur 5 PASS** |
| Config public API | **15 sur 15 PASS** |
| Transport unit | **383 sur 383 PASS** |
| Transport public API | **49 sur 49 PASS** |
| Transport release completeness | **43 sur 43 PASS** |
| Transport doctests | **4 sur 4 PASS** |
| workspace dependency canary | **3 sur 3 PASS** |
| `cargo test --workspace` | **PASS** |
| OrbitFlare Devnet live | **1 sur 1 PASS / 5.19 s** |
| graphes Cargo | **INSPECTÉS / PAS DE BLOCAGE** |
| moteur N1 modifié | **NO** |
| standard N2 modifié | **NO** |
Pile directe réconciliée :
```text
yellowstone-grpc-proto 12.6.0
tonic / tonic-prost 0.14.6
prost / prost-types 0.14.4
```
Verdict : **gate technique/live final fermé**.
## 19. Gate `pre.004` documentaire
Cette tranche doit uniquement synchroniser :
```text
plan 017
validation 013
README Transport
USAGE Transport
docs/000-README.md
```
Elle ne change ni code, ni Config runtime, ni schema, ni test, ni dependency. `CHANGELOG.md`, `ROADMAP.md` et le prompt suivant restent réservés au couloir publication-minimal.
La décision de séquence est prise avant `pre.005` : Helius LaserStream gRPC est reporté dans les TODO Yellowstone sans numéro de release faute d'accès live raisonnable, et `0.2.11` devient `off-chain price transport`. Aucun prompt suivant n'est généré dans `pre.004`.

View File

@@ -0,0 +1,878 @@
<!-- file: prompts/016-V0_2_11_START_PROMPT.md -->
<!-- version: 1 -->
# Prompt de démarrage `0.2.11` — Off-chain price transport
## 1. Identité de la release et base exacte requise
La base attendue est **exclusivement** la release stable :
```text
v0.2.10
```
Ne pas ouvrir `0.2.11` depuis `0.2.10-pre.*`, depuis un ZIP intermédiaire ou depuis un souvenir de session. Si une archive opérateur de `v0.2.10` est fournie, cette archive réelle est l'autorité première devant les snippets, anciens prompts, anciens ZIP et mémoire de conversation.
La release à ouvrir est :
```text
0.2.11 — Off-chain price transport
```
La première tranche est :
```text
0.2.11-pre.001
```
`pre.001` est obligatoirement une tranche **lecture + audit externe actuel + comparaison des sémantiques de prix + brainstorming + threat model + sizing + planification**. Elle ne commence pas par coder un client CoinGecko, Jupiter, Pyth, Birdeye ou DexScreener, ni par figer un type numérique ou une API provider-neutral avant l'audit.
À l'ouverture, vérifier au minimum :
```text
git describe / tag stable si metadata Git disponible
workspace.package.version = 0.2.10
deltas/0.2.10/rel.001.md présent
prompts/016-V0_2_11_START_PROMPT.md présent
```
État fonctionnel attendu depuis `v0.2.10` :
```text
HTTP Solana standard 52/52 current typed + 14 historiques
WebSocket Solana standard 9 familles / 18 opérations
Helius LaserStream WebSocket façade provider existante
Yellowstone gRPC N1/N2 moteur + standard provider-neutral stables
PublicNode Yellowstone Mainnet/Testnet validés
OrbitFlare Yellowstone Devnet validé avec License Key x-token
Config Transport V3 backward-readable
Wallet .kspwallet V1/V2 + Wallet Desk stables
ksp-offchain-transport-lib absent au démarrage
Price Desk hors scope de cette release
```
---
## 2. Mission et résultat attendu
`0.2.11` doit créer `ksp-offchain-transport-lib` à partir d'un premier besoin métier réel : **lire des prix off-chain**, au minimum :
```text
SOL/USD
SOL/EUR
```
La release doit séparer le contrat KSP « lire un prix » de l'API propriétaire du premier provider retenu, sans créer de `ksp-offchain-transport-api` globale ni de trait universel artificiel couvrant prix, metadata, quotes, IPFS, Arweave et autres domaines hétérogènes.
Résultat attendu à la clôture :
```text
ksp-offchain-transport-lib créée
surface prix publique petite, typée et documentée
sémantique du prix explicitement définie
représentation numérique auditée et non choisie par facilité
settings runtime publics possédés par Off-chain Transport
premier provider réellement audité et live-testable
SOL/USD + SOL/EUR disponibles selon un contrat explicite
provenance/fraîcheur/absence de prix représentées sans ambiguïté
timeout + erreurs + observabilité sûrs
Config -> Off-chain Transport composé sans dépendance inverse
aucun secret provider dans code/logs/debug
smoke live opt-in contre un accès non payant raisonnable
README/USAGE + plan/validation finalisés
```
Le principe directeur est :
```text
contrat prix KSP minimal
!=
modèle universel de toute donnée de marché
```
---
## 3. Sources de vérité internes obligatoires — ordre de lecture
### 3.1 Règles globales
Lire d'abord :
```text
RULES.md
docs/000-README.md
docs/rules/RULES_GENERAL.md
docs/rules/RULES_KSP.md
docs/rules/RULES_RUST.md
docs/rules/RULES_DEPENDENCIES.md
docs/rules/RULES_DOCUMENTATION.md
docs/rules/FILE_CONTRACTS.md
docs/rules/VERSION_WORKFLOW.md
docs/rules/PROMPT_STRUCTURE.md
```
Rappels directement applicables :
```text
Rust 2024
unsafe / unwrap / expect / panic interdits selon les règles KSP
retours explicites et audit structurel KSP
third-party deps déclarées une seule fois au workspace
ksp-logging-lib propriétaire du tracing
ksp-config-lib propriétaire config/env/secrets
aucune lecture directe de KSP_* par Off-chain Transport
pas de pub mod ; exports crate-root explicites
```
Après toute modification Rust :
```bash
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
cargo check --workspace
cargo clippy --workspace --all-targets
```
Pour les Markdown touchés :
```bash
python3 scripts/audit_markdown_tables.py <fichiers-ou-répertoires-ciblés>
```
### 3.2 Architecture durable à préserver
Lire ensuite :
```text
docs/architecture/000-README.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
```
Frontières acquises :
```text
ksp-offchain-transport-lib est distinct de ksp-onchain-transport-lib
aucune ksp-offchain-transport-api globale
Off-chain Transport -X-> Config
Config -> Off-chain Transport autorisé via adapter/composition
Off-chain Transport peut contenir à terme plusieurs modules/APIs distincts
la première surface engagée est le prix
metadata HTTP/IPFS/Arweave et quotes attendent leur besoin réel
```
### 3.3 Plans et historique fonctionnel
Lire :
```text
docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md
docs/plans/007-V0_2_0_SERIES_PLANNING.md
docs/plans/017-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC_PLAN.md
docs/validation/013-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC.md
deltas/0.2.10/rel.001.md
```
Références historiques utiles, sans reprendre leurs anciens numéros de release :
```text
sections Off-chain price transport de docs/plans/007-V0_2_0_SERIES_PLANNING.md
ancienne préparation 0.2.x dans prompts/014-V0_2_9_START_PROMPT.md
TODO Wallet Desk demandant la future réutilisation de la capacité prix
```
### 3.4 Code réel à inventorier
Avant design, inspecter au minimum :
```text
Cargo.toml workspace
crates/ksp-core-lib
crates/ksp-logging-lib
crates/ksp-config-lib
crates/ksp-onchain-transport-lib uniquement comme référence de patterns, pas comme dépendance automatique
.env.example
config/
config/schemas/
```
Vérifier notamment les primitives déjà disponibles : `reqwest`, `serde`, `serde_json`, `tokio`, `http`, les contrats Error KSP et les adapters Config existants. Ne pas ajouter un SDK provider si `reqwest` + types KSP suffisent.
---
## 4. Sources externes à réauditer en `pre.001`
La fraîcheur est importante. Les faits ci-dessous sont un snapshot informatif au **2026-08-25**, jamais un contrat à recopier sans vérification.
### 4.1 CoinGecko
Sources primaires :
```text
https://docs.coingecko.com/reference/simple-price
https://docs.coingecko.com/docs/data-delivery-methods
https://docs.coingecko.com/reference/endpoint-overview
```
Snapshot :
```text
REST disponible y compris sur plan Demo
/simple/price accepte plusieurs vs_currencies, dont usd et eur
API Demo utilise typiquement x-cg-demo-api-key
la sémantique est celle d'un agrégateur de marché CoinGecko, pas d'un oracle on-chain
```
Vérifier limites réelles, auth, fraîcheur/cache, IDs d'actifs, disponibilité SOL/USD et SOL/EUR et conditions d'usage.
### 4.2 Jupiter Price API V3
Sources primaires :
```text
https://developers.jup.ag/docs/price
https://developers.jup.ag/docs/api-reference/price
```
Snapshot :
```text
GET https://api.jup.ag/price/v3
x-api-key requis
jusqu'à 50 mint addresses par requête
usdPrice unique par token
prix heuristique dérivé des swaps/liquidité/activité Solana
V2 deprecated
```
Jupiter est naturellement intéressant pour les SPL tokens mais son `usdPrice` ne satisfait pas à lui seul SOL/EUR. Ne pas introduire une conversion EUR externe silencieuse simplement pour forcer ce provider en premier.
### 4.3 Pyth Hermes
Sources primaires :
```text
https://docs.pyth.network/price-feeds/core/how-pyth-works/hermes
https://docs.pyth.network/price-feeds/core/fetch-price-updates
https://docs.pyth.network/price-feeds/core/api-instances-and-providers/hermes
```
Snapshot très sensible à la date :
```text
Hermes expose REST + SSE
prix = entier + exponent + confidence + publish_time
endpoint public soumis à rate limit
une transition Pyth Core est annoncée le 2026-08-26 à 16:00 UTC
authentication API key devient obligatoire selon la documentation courante
```
Comme cette transition intervient immédiatement après la préparation de ce prompt, `pre.001` doit **réauditer Pyth en priorité** et ne jamais utiliser le snapshot ci-dessus comme vérité actuelle.
Pyth représente une sémantique oracle différente d'un prix DEX/agrégateur. Cette différence doit rester visible dans le design.
### 4.4 Birdeye
Sources primaires :
```text
https://docs.birdeye.so/reference/get-defi-price
https://docs.birdeye.so/reference/get-defi-multi_price
https://docs.birdeye.so/reference/birdeye-api-authentication
```
Snapshot :
```text
X-API-KEY requis
x-chain = solana
spot single + batch jusqu'à 100 tokens
prix, liquidité et surfaces historiques/OHLCV plus larges disponibles
```
Ne pas élargir `0.2.11` vers OHLCV/historique simplement parce que le provider les offre.
### 4.5 DexScreener
Source primaire :
```text
https://docs.dexscreener.com/api/reference
```
Snapshot :
```text
API publique sans credential pour les endpoints DEX courants
prix porté par des paires/pools
jusqu'à 30 token addresses sur l'endpoint tokens courant
limite documentée autour de 300 requêtes/minute pour les endpoints DEX/pairs
```
La sémantique est pair/pool/DEX. Un `priceUsd` DexScreener ne doit pas être confondu sans audit avec un prix oracle ou le prix agrégé canonique d'un token.
### 4.6 Autres candidats
Coinbase, Kraken, Binance, CoinMarketCap et autres sources peuvent être audités si cela aide à satisfaire SOL/USD + SOL/EUR avec un accès stable et testable. Ils ne sont pas automatiquement dans le scope.
---
## 5. État validé à préserver depuis `v0.2.10`
### 5.1 Core / Logging / Config
Préserver :
```text
Error/Result central KSP
Pubkey via Core lorsque nécessaire
Logging facade KSP uniquement
Config seul propriétaire des fichiers/env/secrets
.env.example comme inventaire versionné
```
### 5.2 On-chain Transport
`ksp-onchain-transport-lib` reste indépendant et inchangé sauf nécessité architecturale démontrée. `0.2.11` n'est pas l'occasion de déplacer HTTP générique Solana, WebSocket ou Yellowstone dans une abstraction réseau universelle.
### 5.3 Wallet et prochaine Price Desk
La future `0.2.12` doit pouvoir consommer la surface prix dans une Price Desk puis dans Wallet Desk sans dupliquer récupération/normalisation. Cela justifie une API publique propre dès `0.2.11`, mais **pas** une UI ou une intégration Wallet anticipée.
---
## 6. Décisions acquises — ne pas redébattre sans contradiction réelle
```text
nom de crate : ksp-offchain-transport-lib
aucune ksp-offchain-transport-api globale
première surface : prix
minimum fonctionnel : SOL/USD + SOL/EUR
pas de Price Desk dans 0.2.11
pas d'intégration Wallet Desk dans 0.2.11
pas de metadata HTTP/IPFS/Arweave dans cette release
pas de quotes/routing dans cette release
pas d'agrégation multi-provider complexe dans cette release
Off-chain Transport ne dépend pas de Config
Config peut adapter ses documents vers les settings publics Off-chain Transport
premier provider doit être raisonnablement live-testable sans abonnement payant dédié
```
Helius LaserStream gRPC est en TODO provider Yellowstone et ne doit pas être réintroduit dans `0.2.11`.
---
## 7. Questions réellement ouvertes à trancher pendant `pre.001`
### 7.1 Sémantique du prix KSP
Décider ce que signifie précisément le résultat commun minimal :
```text
spot courant ou snapshot provider
base asset / quote asset
provider/source
instant de requête KSP
instant de publication/update provider lorsqu'il existe
fraîcheur/staleness
absence de prix
```
Ne pas inventer un champ commun `confidence`, `liquidity`, `block_id` ou `market_cap` si tous les providers ne partagent pas réellement cette sémantique. Les données provider spécifiques peuvent rester dans un DTO/provider API séparé ou un sous-contrat explicitement typé.
### 7.2 Représentation numérique
Auditer explicitement :
```text
f64 interdit ou accepté seulement comme wire temporaire ?
fixed-point / decimal string / mantissa + exponent ?
arrondis
comparaisons
conversion quote currency
serialization publique
```
Pour une future logique de trading, ne pas figer `f64` comme canon uniquement parce que certaines API renvoient un JSON number.
### 7.3 Identité des assets
Décider la forme minimale :
```text
SOL natif
mint SPL
symbol/provider id
quote fiat USD/EUR
```
Le contrat doit éviter les collisions de symboles. Si le premier provider utilise un ID propriétaire (`solana` chez CoinGecko, mint chez Jupiter/Birdeye, feed ID chez Pyth), ce mapping appartient au provider/adaptateur et ne devient pas automatiquement l'identité canonique KSP.
### 7.4 Provider initial
Comparer au minimum :
```text
CoinGecko Demo
Jupiter Price V3
Pyth Hermes
Birdeye
DexScreener
```
Critères :
```text
SOL/USD + SOL/EUR réalisables proprement
accès gratuit/live raisonnable
auth et secret ownership
fraîcheur et limites
batching
stabilité/documentation
sémantique du prix
surface Rust nécessaire
risque de lock-in
```
Le provider initial peut être CoinGecko si l'audit actuel confirme qu'il satisfait directement les deux quotes avec un accès Demo raisonnable. Ce n'est pas une décision pré-écrite : `pre.001` doit le démontrer.
### 7.5 Conversion SOL/EUR
Priorité : utiliser une source qui expose directement la quote EUR lorsque possible.
Si un provider ne donne que USD, ne pas calculer SOL/EUR à partir d'un FX externe non tracé. Toute dérivation doit être explicitement décidée, avec provenance complète et sans franchir le hors-périmètre multi-provider complexe.
### 7.6 HTTP, retry, cache et fraîcheur
Décider :
```text
request timeout
retry safe GET
traitement 429 / Retry-After
eventuel cache local minimal ou aucun cache
TTL/staleness contract
batch size
concurrency
```
Ne pas copier mécaniquement le moteur de résilience on-chain si le besoin prix est plus petit. Réutiliser des patterns, pas une dépendance injustifiée.
### 7.7 Config
Auditer la forme documentaire nécessaire pour la future Price Desk :
```text
nouveau document offchain ?
extension d'un document existant ?
provider id
base URL
API key/header secret
timeouts
profil par environnement
```
Toute variable ajoutée doit respecter `KSP_SECRET_*` / `KSP_PUBLIC_*` et être inventoriée dans `.env.example`.
### 7.8 Live smoke
Définir un smoke opt-in qui :
```text
ne hardcode aucun secret
n'affiche aucun secret
interroge réellement SOL/USD + SOL/EUR
vérifie valeurs positives/plausibles sans figer un cours exact
vérifie les identités/quotes/provenance
reste borné par timeout
```
Si un secret gratuit est requis, préférer un chemin operator-safe cohérent avec l'ownership Config et les smokes KSP existants.
---
## 8. Objectifs et livrables de `0.2.11`
Livrables attendus, sous réserve du sizing `pre.001` :
```text
crates/ksp-offchain-transport-lib/
Cargo workspace member
price module/API publique provider-neutral
settings runtime publics
provider initial + wire DTOs privés ou bornés
unit/integration tests
adapter Config -> Off-chain Transport
config/schema/example nécessaires
.env.example si secret/provider config
smoke live opt-in
README.md
USAGE.md
plan 0.2.11
validation 0.2.11
deltas complets
prompt 0.2.12 à la fermeture seulement
```
Noms documentaires recommandés :
```text
docs/plans/018-V0_2_11_OFFCHAIN_PRICE_TRANSPORT_PLAN.md
docs/validation/014-V0_2_11_OFFCHAIN_PRICE_TRANSPORT.md
```
---
## 9. Hors périmètre explicite
```text
Price Desk Tauri
modification Wallet Desk
OHLCV/candles
historique/backfill prix complet
order books
DEX routing/quotes
swap execution
metadata token
IPFS/Arweave
news/sentiment
agrégation/consensus multi-provider complexe
fallback automatique multi-provider avant besoin démontré
WebSocket/SSE streaming sauf si pre.001 démontre qu'il est indispensable au minimum de release
nouvelle abstraction réseau universelle partagée avec ksp-onchain-transport-lib
Helius gRPC
```
---
## 10. Contraintes sécurité, erreurs et API
### 10.1 Secrets
```text
aucune API key en source/config versionnée/delta/log/debug
Config possède la résolution des secrets
Off-chain Transport reçoit des settings déjà résolus
redaction des URLs/headers/DTO Debug
les erreurs remote ne recopient pas des payloads arbitraires dans KspError context
```
### 10.2 Numeric safety
Les prix et conversions doivent avoir des limites de taille/format déterministes. Un provider malveillant ou cassé ne doit pas provoquer allocations non bornées, NaN/Inf silencieux ou overflow.
### 10.3 API provider-neutral
Le contrat commun ne doit pas prétendre que toutes les sources sont équivalentes. La provenance provider et le type de donnée doivent rester observables. Les extensions propres à un provider ne gonflent pas le DTO commun sans justification.
### 10.4 Observabilité
Logging via `ksp-logging-lib` uniquement. Les logs peuvent exposer provider, opération, pair, latence, status/catégorie d'erreur et fraîcheur sûre ; jamais credential, URL secrète ou réponse brute non bornée.
---
## 11. Première mission `0.2.11-pre.001` — gate obligatoire
### 11.1 Baseline stable
Avant modification lourde :
```bash
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.11
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test --workspace
```
Si `deltas/0.2.11` n'existe pas encore, adapter seulement le chemin de l'audit Markdown ; ne pas considérer son absence comme erreur.
### 11.2 Audit interne
Inventorier :
```text
frontières architecture Off-chain Transport
patterns settings/errors/logging existants
adapter Config actuel
features reqwest/tokio déjà disponibles
aucune crate/naming collision
besoins futurs Price Desk/Wallet Desk qui imposent une API durable
```
### 11.3 Audit externe actuel
Pour chaque candidat retenu, consigner :
```text
source primaire
endpoint(s)
auth
plan gratuit réellement utilisable
rate limits
batch limit
quotes disponibles
freshness/cache/update time
identifiants d'actifs
wire types
null/missing/error semantics
historique/streaming seulement comme inventaire, pas comme scope automatique
```
Réauditer spécialement Pyth après le changement annoncé du 2026-08-26 16:00 UTC.
### 11.4 Matrice sémantique
Comparer séparément :
```text
market aggregator
Solana swap-derived heuristic price
oracle price + confidence
DEX pair/pool price
```
Identifier quelles données peuvent partager un contrat KSP sans perte de sens.
### 11.5 Threat model
Au minimum :
```text
credential leak
provider spoof/redirect
hostile JSON / huge body
NaN/Inf/extreme exponent
stale price
wrong asset mapping
symbol collision
wrong quote currency
partial batch response
rate-limit storm
retry amplification
provider returns null/missing
silent USD->EUR conversion error
```
### 11.6 Sizing
Estimer chaque tranche pour rester sous environ 1520 minutes de travail effectif. Si l'intégration Config + provider + numeric model + live smoke dépasse une session raisonnable, scinder avant code lourd.
### 11.7 Documents de sortie
`pre.001` doit produire au minimum :
```text
docs/plans/018-V0_2_11_OFFCHAIN_PRICE_TRANSPORT_PLAN.md
docs/validation/014-V0_2_11_OFFCHAIN_PRICE_TRANSPORT.md
deltas/0.2.11/pre.001.md
```
Aucun README final n'est requis avant que le contrat réel soit stabilisé.
### Critères de sortie de `pre.001`
Le gate est vert seulement si :
```text
baseline stable comprise
sources externes actuelles citées
auth/limites/testabilité des providers comparées
sémantique de prix explicitée
représentation numérique décidée ou question bloquante clairement isolée
provider initial choisi avec justification
SOL/USD + SOL/EUR faisables sans conversion opaque
surface Config envisagée
threat model écrit
forecast prerelease recalibré
aucun code provider lourd commencé prématurément
```
---
## 12. Prévision souple initiale des prereleases
Prévision initiale, à recalibrer à `pre.001` :
```text
pre.001 audit actuel providers + sémantique prix + numeric model + threat model + sizing + plan
pre.002 crate foundation + contrats prix/settings/errors provider-neutral + tests canaris
pre.003 premier provider + parsing/batching/fraîcheur + tests déterministes
pre.004 composition Config + profil/secret éventuel + smoke live provider
pre.005 gate technique/live final + graphes si dépendances changées
pre.006 réconciliation documentaire finale plan/validation/README/USAGE
pre.007 publication minimale : prompt 0.2.12 + CHANGELOG + ROADMAP
rel.001 publication stable
```
Cette numérotation est **souple**. Si `pre.001` démontre que Config peut être inclus proprement dans `pre.003`, fusionner plutôt que créer une tranche artificielle. Si un défaut nécessite un fix ou une tranche supplémentaire, l'insérer sans mélanger les couloirs finaux.
La séparation de fermeture reste normative :
```text
gate technique/live
puis réconciliation documentaire
puis publication minimale
```
---
## 13. Versionnement, deltas, commits, archives et tags
Rappels :
```text
workspace.package.version suit chaque prerelease non-fix
0.2.11-pre.001 -> Cargo 0.2.11-pre.1
fix -> 0.2.11-pre.1.fix.1
```
Deltas :
```text
deltas/0.2.11/pre.001.md
deltas/0.2.11/pre.001-fix.001.md
...
deltas/0.2.11/rel.001.md
```
Commit identifiers :
```text
v0.2.11-pre.001
v0.2.11-pre.001-fix.001
...
v0.2.11-rel.001
```
Aucun tag Git prerelease. Seule la publication stable crée :
```text
v0.2.11
```
Les archives d'échange restent des deltas minimaux `ksp-general-<delivery-id>.zip` contenant uniquement les fichiers ajoutés/modifiés et le delta correspondant.
---
## 14. Validation opérateur et application
Après chaque tranche Rust :
```bash
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
cargo check --workspace
cargo clippy --workspace --all-targets
```
Puis tests ciblés. Aux gates finaux :
```bash
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.11
cargo test --workspace
cargo tree -p ksp-offchain-transport-lib
cargo tree -p ksp-offchain-transport-lib --duplicates
cargo tree --duplicates
```
Ne jamais déclarer une commande PASS si elle n'a pas été exécutée.
Si un live smoke nécessite une clé gratuite, ne pas la fournir en argument CLI visible et ne jamais la recopier dans les deltas.
---
## 15. Tests attendus selon le scope retenu
Au minimum :
```text
construction/validation settings
redaction Debug
asset/quote identity
numeric decode and bounds
missing/null price semantics
partial batch response
provider wire fixtures
HTTP status/timeout/429 mapping
secret non-leak errors
Config adapter/provenance si secret
public API canaries
live SOL/USD + SOL/EUR opt-in
```
Tester aussi les cas adversariaux du provider choisi : prix à zéro/négatif si wire possible, valeur exponentielle énorme, timestamp absent/futur, token inconnu et corps de réponse oversized selon les capacités HTTP retenues.
---
## 16. Critères de clôture de `0.2.11`
La release ne peut être clôturée que si :
```text
ksp-offchain-transport-lib existe et respecte les frontières
surface prix provider-neutral petite et documentée
premier provider réellement intégré sans SDK inutile
SOL/USD + SOL/EUR validés
représentation numérique déterministe
provenance/fraîcheur explicites
Config compose sans dépendance inverse
secrets redacted
smoke live accessible et vert
workspace complet vert
README/USAGE/plan/validation réconciliés
prompt 0.2.12 préparé seulement dans la dernière prerelease
```
Aucune obligation de supporter plusieurs providers dans cette release. L'interchangeabilité doit être démontrée par la séparation du contrat et de l'adapter, pas par l'ajout prématuré de cinq implémentations.
---
## 17. Release/session suivante envisagée
Sous réserve du sizing futur :
```text
0.2.12 — Price Desk + intégration prix Wallet Desk
```
Mission envisagée :
```text
petite app Tauri spécialisée pour visualiser/rafraîchir les prix
provenance/état/fraîcheur observables
Config + ksp-offchain-transport-lib réutilisés
puis intégration de la même capacité dans ksp-app-wallet-desk
aucune duplication de récupération/normalisation dans les apps
```
Puis :
```text
0.2.13 — interface/wire foundation
0.2.14 — program-api foundation
```
Helius LaserStream gRPC reste en TODO jusqu'à disponibilité d'un accès live raisonnable.
---
## 18. Instruction d'ouverture
Au démarrage de la nouvelle session :
1. vérifier que la base réelle est exactement `v0.2.10` ;
2. lire les sources internes dans l'ordre imposé ;
3. exécuter la baseline avant modification lourde ;
4. réauditer les providers de prix depuis leurs sources primaires actuelles, **avec priorité au changement Pyth du 2026-08-26** ;
5. comparer les sémantiques agrégateur / swap-derived / oracle / DEX pair ;
6. trancher numeric model, asset identity, freshness et provider initial ;
7. produire plan + validation + sizing `pre.001` ;
8. **ne pas commencer le provider lourd avant que ce gate soit vert**.
Le premier message de la session doit donc commencer par l'audit et le sizing, pas par une proposition de code déjà figée.

View File

@@ -1,8 +1,8 @@
#!/usr/bin/env python3
# file: scripts/audit_markdown_tables.py
# version: 1
# version: 2
"""Validate KSP Markdown table formatting for explicitly supplied files or directories."""
"""Validate KSP Markdown tables and vertical spacing for explicitly supplied files or directories."""
from __future__ import annotations
@@ -12,6 +12,7 @@ import re
import sys
_SEPARATOR_CELL = re.compile(r"^:?-{3,}:?$")
_IGNORED_DIRECTORY_NAMES = frozenset({".git", ".idea", ".venv", "__pycache__", "dist", "node_modules", "target"})
def _markdown_files(paths: list[str]) -> list[pathlib.Path]:
@@ -19,7 +20,13 @@ def _markdown_files(paths: list[str]) -> list[pathlib.Path]:
for raw_path in paths:
path = pathlib.Path(raw_path)
if path.is_dir():
files.extend(sorted(candidate for candidate in path.rglob("*.md") if candidate.is_file()))
files.extend(
sorted(
candidate
for candidate in path.rglob("*.md")
if candidate.is_file() and not any(part in _IGNORED_DIRECTORY_NAMES for part in candidate.parts)
)
)
elif path.is_file() and path.suffix.lower() == ".md":
files.append(path)
else:
@@ -101,9 +108,47 @@ def _validate_table(path: pathlib.Path, start_line: int, rows: list[str]) -> lis
return errors
def _validate_blank_lines(path: pathlib.Path, lines: list[str]) -> list[str]:
errors: list[str] = []
fence_marker: str | None = None
blank_run_start: int | None = None
blank_run_length = 0
def flush_blank_run() -> None:
nonlocal blank_run_start, blank_run_length
if blank_run_start is not None and blank_run_length >= 2:
errors.append(
f"{path}:{blank_run_start}: {blank_run_length} consecutive blank lines are forbidden outside fenced code blocks; keep at most one"
)
blank_run_start = None
blank_run_length = 0
for line_number, line in enumerate(lines, start=1):
stripped = line.lstrip()
if stripped.startswith("```") or stripped.startswith("~~~"):
flush_blank_run()
marker = stripped[:3]
if fence_marker is None:
fence_marker = marker
elif marker == fence_marker:
fence_marker = None
continue
if fence_marker is not None:
continue
if line.strip() == "":
if blank_run_start is None:
blank_run_start = line_number
blank_run_length += 1
continue
flush_blank_run()
flush_blank_run()
return errors
def _audit_file(path: pathlib.Path) -> tuple[int, list[str]]:
lines = path.read_text(encoding="utf-8").splitlines()
errors: list[str] = []
errors = _validate_blank_lines(path, lines)
table_count = 0
fence_marker: str | None = None
index = 0
@@ -135,7 +180,7 @@ def _audit_file(path: pathlib.Path) -> tuple[int, list[str]]:
def main() -> int:
"""Audit Markdown tables in the explicitly selected scope."""
"""Audit KSP Markdown formatting in the explicitly selected scope."""
parser = argparse.ArgumentParser()
parser.add_argument("paths", nargs="+", help="Markdown files or directories to audit")