v0.2.8-pre.010

This commit is contained in:
2026-08-23 18:35:44 +02:00
parent 9c0d4fc197
commit d8bfd7cd2e
7 changed files with 341 additions and 35 deletions

View File

@@ -1,5 +1,5 @@
<!-- file: crates/ksp-onchain-transport-lib/README.md -->
<!-- version: 19 -->
<!-- version: 20 -->
# `ksp-onchain-transport-lib`
@@ -199,6 +199,24 @@ Ces familles restent explicitement **unstable**. Le moteur commun `subscribe_typ
`SolanaVoteNotification` conserve `votePubkey`, `slots`, `hash`, `timestamp` et `signature`. Le timestamp reste optionnel : omission et `null` deviennent `None`, tandis qu'une valeur `i64` est préservée. Transport ne transforme pas ces votes gossip pre-consensus en vérité ledger.
## Helius LaserStream WebSocket
La façade `HeliusLaserStreamWsSession` utilise le même actor physique `WsSession` mais expose uniquement la surface provider actuellement retenue par laudit Helius :
```text
standard réutilisé : account / logs / program / root / signature / slot / slotsUpdates
extension Helius : transactionSubscribe / transactionUnsubscribe
absent Helius : block / vote
```
`slotsUpdates` reste **unstable** et conserve le warning centralisé du moteur standard. `block` et `vote` restent absents de la façade Helius même sils existent sur la façade Solana standard. `transactionSubscribe` reste provider-specific et nest jamais ajouté à `SolanaStandardWsSession`.
Les endpoints Helius mainnet/devnet utilisent un `api-key` dans lURL. KSP recommande de les construire via `ksp-config-lib` et `KSP_SECRET_HELIUS_API_KEY`; la valeur réelle atteint Transport mais les projections sûres, `Debug`, snapshots, erreurs et diagnostics nexposent pas le credential. Transport ne lit jamais lenvironnement et ne dépend jamais de Config.
Pour Helius, lactor envoie automatiquement un control frame WebSocket `Ping` toutes les 60 secondes sur une session active. Cette policy est provider-owned, non configurable et ne sapplique pas aux sessions `SolanaStandard`. Une perte physique suit le reconnect/resubscribe borné déjà décrit; aucun replay/lossless nest promis par la couche WebSocket.
LaserStream **gRPC** reste un backend distinct, hors de cette façade, de `WsProtocolKind` et de la Config WebSocket `helius_laserstream`.
## Résilience
L'admission est calculée par couple endpoint/rôle. Le pool applique :
@@ -260,6 +278,8 @@ Le smoke WebSocket Transport cible uniquement la famille stable `slotSubscribe`
Les trois tests sont `ignored` par défaut. Les deux 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.
## Documentation
- [`USAGE.md`](USAGE.md) — consommation directe, Config -> Transport, API typed/raw, smokes et inspection runtime ;

View File

@@ -1,5 +1,5 @@
<!-- file: crates/ksp-onchain-transport-lib/USAGE.md -->
<!-- version: 19 -->
<!-- version: 20 -->
# Utilisation de `ksp-onchain-transport-lib`
@@ -183,6 +183,46 @@ let mut blocks = match session
Les trois familles utilisent le même `WsSubscription::unsubscribe().await`; aucun remote subscription ID n'entre dans l'API publique.
### Façade Helius LaserStream WebSocket
Pour un endpoint Config `kind = "helius_laserstream"`, le consumer doit sélectionner lendpoint WebSocket résolu puis ouvrir la façade Helius, sans reconstruire ni journaliser lURL contenant lAPI key :
```rust
let resolved = match engine.load_resolved_transport_config(Some("helius_devnet"), &environment) {
Ok(value) => value,
Err(error) => return Err(error),
};
let ws_settings = match resolved.ws_settings() {
Some(value) => value,
None => return Err(ksp_core_lib::Error::new(ksp_onchain_transport_lib::ERROR_CODE_INVALID_SETTINGS, "Helius profile requires WebSocket settings")),
};
let endpoint = match ws_settings
.endpoints()
.iter()
.find(|candidate| candidate.enabled() && candidate.protocol() == ksp_onchain_transport_lib::WsProtocolKind::HeliusLaserStream)
{
Some(value) => value.clone(),
None => return Err(ksp_core_lib::Error::new(ksp_onchain_transport_lib::ERROR_CODE_INVALID_SETTINGS, "Helius WebSocket endpoint is unavailable")),
};
let session = match ksp_onchain_transport_lib::HeliusLaserStreamWsSession::connect(endpoint).await {
Ok(value) => value,
Err(error) => return Err(error),
};
let mut slots = match session.slot_subscribe().await {
Ok(value) => value,
Err(error) => return Err(error),
};
let notification = slots.recv().await;
let removed = slots.unsubscribe().await;
let closed = session.close().await;
```
La façade Helius réutilise `account`, `logs`, `program`, `root`, `signature`, `slot` et `slotsUpdates`. `slotsUpdates` reste unstable. `block` et `vote` ne sont pas exposés. `transaction_subscribe()` prend `HeliusTransactionSubscribeRequest` et retourne `WsSubscription<HeliusTransactionNotification>` ; son unsubscribe reste porté par le handle et produit `transactionUnsubscribe` sans exposer lID distant.
Le heartbeat Helius est automatique : `WsSession` envoie un control frame `Ping` toutes les 60 secondes tant que la session Helius est active. Le consumer ne configure pas un second timer et ne lance pas un task heartbeat parallèle. Cette règle ne vaut pas pour `SolanaStandardWsSession`.
`KSP_SECRET_HELIUS_API_KEY` appartient à Config. Ne pas lire lenvironnement dans Transport, ne pas recopier lURL résolue dans un log et ne pas ajouter un dev-dependency inverse `Transport -> Config`. LaserStream gRPC reste un backend différent et ne doit pas réutiliser `WsProtocolKind::HeliusLaserStream`.
### Reconnect automatique borné
Les settings de session contrôlent le reconnect physique. Une perte de socket publie `Reconnecting { attempt }`, invalide les remote IDs et incrémente `continuity_gap_count`. Avec la policy par défaut `ActiveSubscriptions`, les handles logiques gardent leur `WsSubscriptionId` et passent temporairement en `Resubscribing`; l'actor recrée leurs subscriptions dans l'ordre local avant de republier `Active`.
@@ -358,7 +398,25 @@ cargo test -p ksp-config-lib --test transport_devnet_smoke -- --ignored --nocapt
Il valide le profil committé `devnet_public` et les quatre canaris foundation. Il reste transitoirement hébergé dans Config : les futurs smokes cross-crates ne doivent pas faire de Config leur destination générale et devront migrer vers une surface d'intégration/orchestration dédiée lorsqu'elle existera.
Les endpoints publics Solana sont rate-limités et non destinés à la production. Un échec réseau externe n'est pas assimilé automatiquement à une régression locale ; les fixtures HTTP et WebSocket locales restent les gates reproductibles.
### Smoke Helius live
Aucun nouveau test Helius live nest committé en `0.2.8-pre.010`. La raison est architecturale : Transport ne peut pas lire `KSP_SECRET_HELIUS_API_KEY` ni dépendre de Config, et Config ne doit pas devenir la destination générale des futurs smokes `Config + autre crate`. Créer un quatrième smoke dans lune de ces deux crates contournerait donc une frontière déjà documentée.
Lorsque la surface KSP dintégration/orchestration dédiée existera, le smoke live minimal recommandé sera :
```text
Config helius_devnet
-> endpoint helius_laserstream résolu avec KSP_SECRET_HELIUS_API_KEY
-> HeliusLaserStreamWsSession::connect
-> slotSubscribe
-> une slotNotification sous timeout
-> slotUnsubscribe
-> close
```
Ce scénario utilise une méthode standard stable sur lendpoint Helius et teste donc auth + façade provider + actor + unsubscribe sans dépendre dune entitlement particulière de `transactionSubscribe`. Un smoke `transactionSubscribe` pourra être ajouté séparément comme opt-in provider-specific si lenvironnement opérateur possède les droits nécessaires ; il ne doit pas devenir un gate réseau obligatoire de la release.
Les endpoints publics/provider sont des dépendances externes. Un rate-limit, refus dauth, entitlement absente ou incident réseau nest pas assimilé automatiquement à une régression locale ; les fixtures HTTP/WebSocket locales et les gates déterministes restent autoritaires.
Pour auditer les dépendances, inspecter également le graphe effectif après résolution Cargo :

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-onchain-transport-lib/tests/release_completeness.rs
// version: 31
// version: 32
//! Release-level completeness canaries for staged HTTP and WebSocket Transport coverage.
@@ -974,3 +974,25 @@ fn release_v0_2_8_pre_009_config_secret_and_dependency_boundaries_remain_wired()
let env_example = std::fs::read_to_string(workspace.join(".env.example")).expect(".env.example must be readable");
assert!(env_example.contains("KSP_SECRET_HELIUS_API_KEY"));
}
#[test]
fn release_v0_2_8_pre_010_live_smoke_policy_preserves_secret_and_dependency_ownership() {
let manifest_directory = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
let workspace = manifest_directory.parent().and_then(std::path::Path::parent).expect("Transport integration test must resolve the workspace root");
let transport_manifest = std::fs::read_to_string(manifest_directory.join("Cargo.toml")).expect("Transport manifest must be readable");
assert!(!transport_manifest.contains("ksp-config-lib"));
let transport_ws_smoke =
std::fs::read_to_string(manifest_directory.join("tests/websocket_devnet_smoke.rs")).expect("Transport WebSocket smoke must be readable");
assert!(!transport_ws_smoke.contains("KSP_SECRET_HELIUS_API_KEY"));
assert!(!transport_ws_smoke.contains("ConfigEnvironment"));
assert!(!workspace.join("crates/ksp-config-lib/tests/helius_websocket_smoke.rs").exists());
let readme = std::fs::read_to_string(manifest_directory.join("README.md")).expect("Transport README must be readable");
assert!(readme.contains("Aucun smoke Helius live supplémentaire nest committé en `0.2.8-pre.010`"));
assert!(readme.contains("KSP_SECRET_HELIUS_API_KEY"));
let usage = std::fs::read_to_string(manifest_directory.join("USAGE.md")).expect("Transport USAGE must be readable");
assert!(usage.contains("### Smoke Helius live"));
assert!(usage.contains("surface KSP dintégration/orchestration dédiée"));
assert!(usage.contains("cargo tree -p ksp-onchain-transport-lib"));
let env_example = std::fs::read_to_string(workspace.join(".env.example")).expect(".env.example must be readable");
assert!(env_example.contains("KSP_SECRET_HELIUS_API_KEY"));
}