v0.3.15-pre.006-fix.002

This commit is contained in:
2026-09-13 16:34:18 +02:00
parent f3ed86e54c
commit f12e67bdc7
10 changed files with 180 additions and 61 deletions

View File

@@ -1,12 +1,12 @@
# file: Cargo.toml
# version: 603
# version: 604
[workspace]
resolver = "3"
members = ["crates/ksp-app-backfill-desk", "crates/ksp-app-config-desk", "crates/ksp-app-raw-transaction-ingest-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-store-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-job-api", "crates/ksp-job-backfill-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-raw-transaction-lib", "crates/ksp-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib", "crates/ksp-worker-api", "crates/ksp-worker-raw-transaction-ingest-lib"]
[workspace.package]
version = "0.3.15-pre.6.fix.1"
version = "0.3.15-pre.6.fix.2"
edition = "2024"
license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"

View File

@@ -78,6 +78,7 @@
"url": "${KSP_PUBLIC_SOLANA_DEVNET_WS_URL:-wss://api.devnet.solana.com}",
"capabilities": [
"account",
"block",
"logs",
"program",
"root",
@@ -178,6 +179,7 @@
"url": "${KSP_PUBLIC_SOLANA_DEVNET_WS_URL:-wss://api.devnet.solana.com}",
"capabilities": [
"account",
"block",
"logs",
"program",
"root",
@@ -244,6 +246,7 @@
"url": "${KSP_PUBLIC_SOLANA_MAINNET_WS_URL:-wss://api.mainnet.solana.com}",
"capabilities": [
"account",
"block",
"logs",
"program",
"root",
@@ -398,6 +401,7 @@
"url": "${KSP_PUBLIC_SOLANA_MAINNET_WS_URL:-wss://api.mainnet.solana.com}",
"capabilities": [
"account",
"block",
"logs",
"program",
"root",
@@ -447,6 +451,7 @@
"url": "${KSP_PUBLIC_SOLANA_MAINNET_WS_URL:-wss://api.mainnet.solana.com}",
"capabilities": [
"account",
"block",
"logs",
"program",
"root",
@@ -512,6 +517,7 @@
"url": "wss://api.testnet.solana.com",
"capabilities": [
"account",
"block",
"logs",
"program",
"root",
@@ -561,6 +567,7 @@
"url": "wss://api.testnet.solana.com",
"capabilities": [
"account",
"block",
"logs",
"program",
"root",

View File

@@ -1,5 +1,5 @@
<!-- file: crates/ksp-app-raw-transaction-ingest-desk/README.md -->
<!-- version: 3 -->
<!-- version: 4 -->
# `ksp-app-raw-transaction-ingest-desk`
@@ -41,6 +41,8 @@ helius-transaction-hydrated
http-block-polling
```
Les profils standard publics committés déclarent `Block` en plus de `Logs`; `standard-block-direct` est donc composable depuis Config sur Devnet, Mainnet et Testnet. Cette déclaration ne transforme pas `blockSubscribe` en méthode stable et ne remplace pas la revalidation runtime.
Une route `Configured` est uniquement **composable depuis Config**. La disponibilité réelle des connexions, du Store et du Worker est revalidée lors du Start avant qu'une route puisse devenir `Running`.
## Ports de développement

View File

@@ -1,5 +1,5 @@
<!-- file: crates/ksp-app-raw-transaction-ingest-desk/USAGE.md -->
<!-- version: 3 -->
<!-- version: 4 -->
# Utilisation de `ksp-app-raw-transaction-ingest-desk`
@@ -29,7 +29,7 @@ raison sûre lorsque la route est indisponible
génération d'inventaire
```
`Configured` signifie uniquement que toutes les requirements Config nécessaires à la route sont cohérentes. Aucun probe réseau, aucune ouverture de Store et aucun Worker ne sont déclenchés par l'inventaire.
`Configured` signifie uniquement que toutes les requirements Config nécessaires à la route sont cohérentes. Les profils `solana-public` standard committés déclarent explicitement les capabilities `Logs` et `Block`; `standard-block-direct` est donc Configured sur les trois réseaux logiques tant que ce socle Config reste valide. Aucun probe réseau, aucune ouverture de Store et aucun Worker ne sont déclenchés par l'inventaire.
Un refresh reconstruit l'inventaire depuis Config et publie une nouvelle génération. La sélection visuelle d'un réseau ne choisit aucune URL, provider ou ressource physique et ne démarre aucune acquisition. Une capability optionnelle dont le secret n'est pas résolu rend uniquement la route qui en dépend indisponible ; les routes satisfaites par le Transport de base du même réseau restent composables.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-raw-transaction-ingest-desk/unit_tests/route_inventory.rs
// version: 2
// version: 3
#[test]
fn pre_006_route_requirement_projection_is_fail_closed_and_ordered() {
@@ -38,14 +38,19 @@ fn pre_006_unavailable_profile_projects_all_five_routes_without_physical_materia
}
#[test]
fn pre_006_fix_001_network_profiles_aggregate_same_network_transport_capabilities() {
fn pre_006_fix_002_network_profiles_aggregate_same_network_transport_capabilities_and_standard_block() {
let management = committed_management();
assert!(management.is_ok());
let management = match management {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let environment = all_optional_source_secrets();
let environment = ksp_config_lib::ConfigEnvironment::load();
assert!(environment.is_ok());
let environment = match environment {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let inventory = super::build_route_inventory_with_environment(&management, &environment, 7);
assert!(inventory.is_ok());
let inventory = match inventory {
@@ -63,32 +68,29 @@ fn pre_006_fix_001_network_profiles_aggregate_same_network_transport_capabilitie
if let std::option::Option::Some(devnet) = devnet {
assert!(devnet.is_default);
assert_eq!(devnet.network.as_deref(), std::option::Option::Some("devnet"));
assert_route(devnet, crate::RawIngestRouteId::YellowstoneHydrated, true, std::option::Option::None);
assert_optional_secret_route(devnet, crate::RawIngestRouteId::YellowstoneHydrated);
assert_route(devnet, crate::RawIngestRouteId::StandardLogsHydrated, true, std::option::Option::None);
assert_route(
devnet,
crate::RawIngestRouteId::StandardBlockDirect,
false,
std::option::Option::Some(crate::RawIngestRouteUnavailableReason::MissingWsBlockCapability),
);
assert_route(devnet, crate::RawIngestRouteId::HeliusTransactionHydrated, true, std::option::Option::None);
assert_route(devnet, crate::RawIngestRouteId::StandardBlockDirect, true, std::option::Option::None);
assert_optional_secret_route(devnet, crate::RawIngestRouteId::HeliusTransactionHydrated);
assert_route(devnet, crate::RawIngestRouteId::HttpBlockPolling, true, std::option::Option::None);
}
let mainnet = profile(&inventory, "mainnet");
assert!(mainnet.is_some());
if let std::option::Option::Some(mainnet) = mainnet {
assert_eq!(mainnet.network.as_deref(), std::option::Option::Some("mainnet"));
assert_route(mainnet, crate::RawIngestRouteId::YellowstoneHydrated, true, std::option::Option::None);
assert_optional_secret_route(mainnet, crate::RawIngestRouteId::YellowstoneHydrated);
assert_route(mainnet, crate::RawIngestRouteId::StandardLogsHydrated, true, std::option::Option::None);
assert_route(mainnet, crate::RawIngestRouteId::HeliusTransactionHydrated, true, std::option::Option::None);
assert_route(mainnet, crate::RawIngestRouteId::StandardBlockDirect, true, std::option::Option::None);
assert_optional_secret_route(mainnet, crate::RawIngestRouteId::HeliusTransactionHydrated);
assert_route(mainnet, crate::RawIngestRouteId::HttpBlockPolling, true, std::option::Option::None);
}
let testnet = profile(&inventory, "testnet");
assert!(testnet.is_some());
if let std::option::Option::Some(testnet) = testnet {
assert_eq!(testnet.network.as_deref(), std::option::Option::Some("testnet"));
assert_route(testnet, crate::RawIngestRouteId::YellowstoneHydrated, true, std::option::Option::None);
assert_optional_secret_route(testnet, crate::RawIngestRouteId::YellowstoneHydrated);
assert_route(testnet, crate::RawIngestRouteId::StandardLogsHydrated, true, std::option::Option::None);
assert_route(testnet, crate::RawIngestRouteId::StandardBlockDirect, true, std::option::Option::None);
assert_route(
testnet,
crate::RawIngestRouteId::HeliusTransactionHydrated,
@@ -119,37 +121,33 @@ fn pre_006_fix_001_network_profiles_aggregate_same_network_transport_capabilitie
}
#[test]
fn pre_006_fix_001_missing_optional_source_secret_does_not_disable_base_network_routes() {
fn pre_006_fix_002_base_network_routes_remain_configured_independently_of_optional_sources() {
let management = committed_management();
assert!(management.is_ok());
let management = match management {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let environment = ksp_config_lib::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
let environment = ksp_config_lib::ConfigEnvironment::load();
assert!(environment.is_ok());
let environment = match environment {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let inventory = super::build_route_inventory_with_environment(&management, &environment, 11);
assert!(inventory.is_ok());
let inventory = match inventory {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let mainnet = profile(&inventory, "mainnet");
assert!(mainnet.is_some());
if let std::option::Option::Some(mainnet) = mainnet {
assert_route(mainnet, crate::RawIngestRouteId::StandardLogsHydrated, true, std::option::Option::None);
assert_route(mainnet, crate::RawIngestRouteId::HttpBlockPolling, true, std::option::Option::None);
assert_route(
mainnet,
crate::RawIngestRouteId::YellowstoneHydrated,
false,
std::option::Option::Some(crate::RawIngestRouteUnavailableReason::MissingRequiredSecret),
);
assert_route(
mainnet,
crate::RawIngestRouteId::HeliusTransactionHydrated,
false,
std::option::Option::Some(crate::RawIngestRouteUnavailableReason::MissingRequiredSecret),
);
for profile_id in ["devnet", "mainnet", "testnet"] {
let selected = profile(&inventory, profile_id);
assert!(selected.is_some());
if let std::option::Option::Some(selected) = selected {
assert_route(selected, crate::RawIngestRouteId::StandardLogsHydrated, true, std::option::Option::None);
assert_route(selected, crate::RawIngestRouteId::StandardBlockDirect, true, std::option::Option::None);
assert_route(selected, crate::RawIngestRouteId::HttpBlockPolling, true, std::option::Option::None);
}
}
}
@@ -182,19 +180,24 @@ fn committed_management() -> ksp_core_lib::Result<ksp_config_lib::ConfigManageme
return std::result::Result::Ok(ksp_config_lib::ConfigManagement::new(ksp_config_lib::ConfigDocumentEngine::new(bootstrap, registry)));
}
fn all_optional_source_secrets() -> ksp_config_lib::ConfigEnvironment {
let mut process = std::collections::BTreeMap::<String, String>::new();
process.insert("KSP_SECRET_HELIUS_API_KEY".to_owned(), "helius-route-inventory-canary".to_owned());
process.insert("KSP_SECRET_ORBITFLARE_DEVNET_GRPC_X_TOKEN".to_owned(), "orbitflare-route-inventory-canary".to_owned());
process.insert("KSP_SECRET_PUBLICNODE_MAINNET_GRPC_X_TOKEN".to_owned(), "publicnode-mainnet-route-inventory-canary".to_owned());
process.insert("KSP_SECRET_PUBLICNODE_TESTNET_GRPC_X_TOKEN".to_owned(), "publicnode-testnet-route-inventory-canary".to_owned());
return ksp_config_lib::ConfigEnvironment::from_maps(process, std::collections::BTreeMap::new());
}
fn profile<'a>(inventory: &'a crate::RawIngestRouteInventoryDto, profile_id: &str) -> std::option::Option<&'a crate::RawIngestProfileInventoryDto> {
return inventory.profiles.iter().find(|profile| return profile.profile_id == profile_id);
}
fn assert_optional_secret_route(profile: &crate::RawIngestProfileInventoryDto, route_id: crate::RawIngestRouteId) {
let route = profile.routes.iter().find(|route| return route.route_id == route_id);
assert!(route.is_some());
if let std::option::Option::Some(route) = route {
if route.selectable {
assert_eq!(route.state, crate::RawIngestRouteState::Configured);
assert_eq!(route.reason, std::option::Option::None);
} else {
assert_eq!(route.state, crate::RawIngestRouteState::Unavailable);
assert_eq!(route.reason, std::option::Option::Some(crate::RawIngestRouteUnavailableReason::MissingRequiredSecret));
}
}
}
fn assert_route(
profile: &crate::RawIngestProfileInventoryDto,
route_id: crate::RawIngestRouteId,

View File

@@ -1,5 +1,5 @@
<!-- file: crates/ksp-config-lib/USAGE.md -->
<!-- version: 17 -->
<!-- version: 18 -->
# Utilisation de ksp-config-lib
@@ -164,7 +164,7 @@ L'accesseur historique `into_transport_settings()` conserve volontairement son t
Les protocoles WebSocket restent `kind = "solana_standard"` et `kind = "helius_laserstream"`. Chaque endpoint peut en plus déclarer `capabilities`, liste de descriptors `WsSubscriptionKind` (`account`, `block`, `logs`, `program`, `root`, `signature`, `slot`, `slots_updates`, `vote`, `helius_transaction`). L'absence de cette liste reste backward-compatible mais ne constitue aucune preuve de support ; une liste présente est mappée explicitement vers Transport puis validée contre le protocole. Config ne déduit jamais une capability depuis `provider`.
Les profils Solana standard versionnés déclarent conservativement les six familles stables `account/logs/program/root/signature/slot` et ne déclarent donc pas `block`. Le profil `testnet_public` fournit le socle HTTP + WS standard Testnet sans credential ; `publicnode_testnet` reste une source Yellowstone optionnelle séparée. Les profils `helius_devnet` et `helius_mainnet` déclarent les sept familles Helius déjà supportées par la façade (`account/logs/program/root/signature/slot/slots_updates`) plus `helius_transaction`, sans `block` ni `vote`. Helius LaserStream WebSocket conserve ses URLs Config-owned et sa clé `KSP_SECRET_HELIUS_API_KEY`; cette surface est indépendante de Yellowstone gRPC.
Les profils Solana standard publics versionnés déclarent `account/block/logs/program/root/signature/slot`. `block` reste une capability explicite, instable et validator/provider-gated : Config ne linfère jamais du protocol kind ni du provider, mais les endpoints publics committés lannoncent désormais explicitement pour rendre la route Standard Block composable. Le profil `testnet_public` fournit le socle HTTP + WS standard Testnet sans credential ; `publicnode_testnet` reste une source Yellowstone optionnelle séparée. Les profils `helius_devnet` et `helius_mainnet` déclarent les sept familles Helius déjà supportées par la façade (`account/logs/program/root/signature/slot/slots_updates`) plus `helius_transaction`, sans `block` ni `vote`. Helius LaserStream WebSocket conserve ses URLs Config-owned et sa clé `KSP_SECRET_HELIUS_API_KEY`; cette surface est indépendante de Yellowstone gRPC.
Les scalaires `*_ms` restent des valeurs Config et sont convertis en `std::time::Duration` par l'adapter. La dépendance reste unidirectionnelle : Config connaît les contrats Transport pour les construire ; Transport ne connaît ni Config, ni `.env`, ni les variables KSP.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-config-lib/unit_tests/transport.rs
// version: 15
// version: 16
#[test]
fn fixture_transport_profile_maps_complete_runtime_contract() {
@@ -146,7 +146,7 @@ fn committed_transport_document_maps_default_and_explicit_profiles() {
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);
assert!(ws.endpoints()[0].supports_subscription(ksp_onchain_transport_lib::WsSubscriptionKind::Logs));
assert!(!ws.endpoints()[0].supports_subscription(ksp_onchain_transport_lib::WsSubscriptionKind::Block));
assert!(ws.endpoints()[0].supports_subscription(ksp_onchain_transport_lib::WsSubscriptionKind::Block));
}
}
if let std::result::Result::Ok(mainnet) = mainnet {
@@ -160,7 +160,7 @@ fn committed_transport_document_maps_default_and_explicit_profiles() {
if let std::option::Option::Some(ws) = ws {
assert_eq!(ws.endpoints()[0].url().as_str(), "wss://api.mainnet.solana.com");
assert!(ws.endpoints()[0].supports_subscription(ksp_onchain_transport_lib::WsSubscriptionKind::Logs));
assert!(!ws.endpoints()[0].supports_subscription(ksp_onchain_transport_lib::WsSubscriptionKind::Block));
assert!(ws.endpoints()[0].supports_subscription(ksp_onchain_transport_lib::WsSubscriptionKind::Block));
}
}
}
@@ -232,7 +232,7 @@ fn v0_3_15_pre_006_fix_001_testnet_public_is_keyless_standard_transport_without_
assert_eq!(ws.endpoints()[0].cluster().as_str(), "testnet");
assert_eq!(ws.endpoints()[0].protocol(), ksp_onchain_transport_lib::WsProtocolKind::SolanaStandard);
assert!(ws.endpoints()[0].supports_subscription(ksp_onchain_transport_lib::WsSubscriptionKind::Logs));
assert!(!ws.endpoints()[0].supports_subscription(ksp_onchain_transport_lib::WsSubscriptionKind::Block));
assert!(ws.endpoints()[0].supports_subscription(ksp_onchain_transport_lib::WsSubscriptionKind::Block));
}
assert!(resolved.grpc_settings().is_none(), "keyless Testnet base profile must not conflate PublicNode Yellowstone with the network itself");
}
@@ -588,6 +588,35 @@ fn helius_laserstream_mainnet_and_devnet_api_key_map_to_protocol_and_safe_redact
assert!(devnet_debug.contains(crate::REDACTED_CONFIG_VALUE));
}
#[test]
fn v0_3_15_pre_006_fix_002_committed_public_standard_profiles_declare_block_capability() {
let engine = committed_engine();
let engine = match engine {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let environment = crate::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
for (profile_id, cluster) in [("devnet_public", "devnet"), ("mainnet_public", "mainnet"), ("testnet_public", "testnet")] {
let resolved = engine.load_resolved_transport_config(std::option::Option::Some(profile_id), &environment);
assert!(resolved.is_ok(), "committed public standard profile {profile_id} should resolve: {resolved:?}");
let resolved = match resolved {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => continue,
};
let ws = resolved.ws_settings();
assert!(ws.is_some(), "committed public standard profile {profile_id} should expose WebSocket settings");
if let std::option::Option::Some(ws) = ws {
assert_eq!(ws.endpoints().len(), 1);
let endpoint = &ws.endpoints()[0];
assert_eq!(endpoint.provider().as_str(), "solana-public");
assert_eq!(endpoint.cluster().as_str(), cluster);
assert_eq!(endpoint.protocol(), ksp_onchain_transport_lib::WsProtocolKind::SolanaStandard);
assert!(endpoint.supports_subscription(ksp_onchain_transport_lib::WsSubscriptionKind::Logs));
assert!(endpoint.supports_subscription(ksp_onchain_transport_lib::WsSubscriptionKind::Block));
}
}
}
#[test]
fn v0_3_15_pre_003_committed_helius_profiles_map_transaction_capability_without_block_or_vote() {
let engine = committed_engine();

View File

@@ -0,0 +1,70 @@
<!-- file: deltas/0.3.15/pre.006-fix.002.md -->
<!-- version: 1 -->
# Delta `0.3.15-pre.006-fix.002`
## Objet
Corriger la seconde qualification de `pre.006` : `standard-block-direct` ne doit pas rester structurellement indisponible lorsque les endpoints Standard WS committés portent cette capability, et les canaris Desk ne doivent pas appeler une API Config crate-private.
## Standard Block
Les endpoints `solana-public` Standard WS committés de `devnet`, `mainnet` et `testnet` déclarent désormais explicitement :
```text
account
block
logs
program
root
signature
slot
```
La route `standard-block-direct` devient donc `Configured` sur les trois profils réseau du Raw Transaction Ingest Desk.
Cette déclaration reste une preuve de composabilité Config. `blockSubscribe` demeure unstable et validator/provider-gated ; aucun provider n'est inféré depuis son nom et aucune I/O n'est ajoutée à l'inventaire.
Helius LaserStream reste inchangé et ne gagne pas `Block`.
## Canaris Desk
`pre.006-fix.001` utilisait `ConfigEnvironment::from_maps` depuis `ksp-app-raw-transaction-ingest-desk`, alors que ce helper est crate-private et compilé uniquement dans les tests de `ksp-config-lib`.
Le fix retire cet appel invalide. Les canaris utilisent l'API publique `ConfigEnvironment::load()` pour l'environnement opérateur, valident de manière stricte les routes standard réseau-centriques et traitent les sources optionnelles Helius/Yellowstone comme conditionnelles aux secrets réellement présents.
## Frontières conservées
```text
aucun probe réseau dans l'inventaire ;
aucune ouverture Store ;
aucune session Transport ;
aucun Worker ;
aucun Start/Stop ;
aucune inférence provider ;
aucun secret/URL/URI/handle projeté au frontend.
```
## Version Cargo
```text
header Cargo.toml : 603 -> 604
workspace.package.version : 0.3.15-pre.6.fix.1 -> 0.3.15-pre.6.fix.2
```
## Gate opérateur attendu
```bash
cargo fmt --all
cargo fmt --all -- --check
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.3.15
cargo check --workspace
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test -p ksp-config-lib --all-targets --all-features
cargo test -p ksp-app-raw-transaction-ingest-desk --all-targets --all-features
cargo test --workspace --all-targets --all-features
(cd crates/ksp-app-raw-transaction-ingest-desk && cargo tauri dev)
```
Aucun `npm run build` direct n'appartient au gate.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/plans/036-V0_3_15_RAW_TRANSACTION_INGEST_DESK_PLAN.md -->
<!-- version: 7 -->
<!-- version: 8 -->
# Plan v0.3.15 — Raw Transaction Ingest Desk
@@ -193,10 +193,10 @@ Aucun gap Store n'est identifié. L'application ne dépend jamais de `ksp-store-
| Profil/config | Réseau | Surfaces | Secret requis | Committed ? | Observation |
|-------------------------|---------|---------------------------------------|-------------------|-------------|-----------------------------------------------------------------|
| `devnet_public` | devnet | HTTP + WS Solana standard | non | oui | capabilities stables déclarées ; `Logs` oui ; `Block` non |
| `devnet_public` | devnet | HTTP + WS Solana standard | non | oui | capabilities explicites ; `Logs` oui ; `Block` oui |
| `helius_devnet` | devnet | HTTP public + WS Helius | API key Helius | oui | `HeliusTransaction` oui ; `Block`/`Vote` non |
| `orbitflare_devnet` | devnet | HTTP + WS standard + Yellowstone gRPC | token gRPC secret | oui | `Logs` déclaré ; gRPC composable seulement si secret résolu |
| `mainnet_public` | mainnet | HTTP + WS Solana standard | non | oui | capabilities stables déclarées ; `Logs` oui ; `Block` non |
| `mainnet_public` | mainnet | HTTP + WS Solana standard | non | oui | capabilities explicites ; `Logs` oui ; `Block` oui |
| `helius_mainnet` | mainnet | HTTP public + WS Helius | API key Helius | oui | `HeliusTransaction` oui ; `Block`/`Vote` non |
| `mainnet_backfill_pool` | mainnet | plusieurs HTTP + WS standard | selon endpoints | oui | `Logs` déclaré ; profil conservé hors composite Raw Ingest Desk |
| `publicnode_mainnet` | mainnet | HTTP + WS standard + Yellowstone gRPC | x-token secret | oui | source Yellowstone optionnelle du profil applicatif `mainnet` |
@@ -240,7 +240,7 @@ Le frontend reçoit uniquement la projection sûre de ce modèle. Les ressources
|-------------------------------|------------------|----------------------------------------------------------------|-------------------------------------------------|---------------------------------|-----------------------------------------------|-----------------------------------------------------------------------------------|------------------------------------------------|--------------------------------------|
| `yellowstone-hydrated` | réseau du profil | Yellowstone gRPC Subscribe + HTTP `getTransaction` même réseau | scan HTTP repair si rôle compatible | gRPC + HTTP role | `RawTransactionIngestYellowstoneSource` | oui sur profil complet et secrets résolus | aucun contrat essentiel | provider-gated selon profil |
| `standard-logs-hydrated` | réseau du profil | WS Solana standard `logsSubscribe` + HTTP `getTransaction` | scan HTTP repair si rôle compatible | `Logs` + HTTP role | `RawTransactionIngestStandardLogsSource` | oui côté Config sur profils déclarant `Logs` ; enforcement Worker en `pre.004` | enforcement Worker minimal | oui, notamment Devnet keyless |
| `standard-block-direct` | réseau du profil | WS Solana standard `blockSubscribe` explicitement déclaré | aucune hydration obligatoire | WS capability `Block` | `RawTransactionIngestStandardBlockSource` | non : aucun profil committed ne déclare `Block` | enforcement Worker minimal + endpoint qualifié | seulement endpoint prouvé compatible |
| `standard-block-direct` | réseau du profil | WS Solana standard `blockSubscribe` explicitement déclaré | aucune hydration obligatoire | WS capability `Block` | `RawTransactionIngestStandardBlockSource` | oui sur les profils publics standard qui déclarent explicitement `Block` | enforcement Worker minimal + endpoint qualifié | seulement endpoint prouvé compatible |
| `helius-transaction-hydrated` | réseau du profil | WS Helius `transactionSubscribe` + HTTP `getTransaction` | scan HTTP repair si rôle compatible | `HeliusTransaction` + HTTP role | `RawTransactionIngestHeliusTransactionSource` | oui côté Config sur `helius_*` si secret résolu ; enforcement Worker en `pre.004` | enforcement Worker minimal | provider-gated Helius |
| `http-block-polling` | réseau du profil | HTTP `getSlot` + `getBlocksWithLimit` + `getBlock` | `getTransaction` pour known-reference hydration | HTTP role | `RawTransactionIngestHttpBlockPollingSource` | oui lorsque le rôle résolu annonce les trois méthodes | aucun contrat essentiel | oui sur endpoint public compatible |
@@ -282,7 +282,7 @@ capability WS explicite = Block
commitment confirmed ou finalized
```
Aucun HTTP n'est obligatoire pour le chemin nominal, car le matériau bloc est RAW-direct. Cette route reste non sélectionnable tant que la capability `Block` n'est pas explicitement déclarée dans le profil exact.
Aucun HTTP n'est obligatoire pour le chemin nominal, car le matériau bloc est RAW-direct. Cette route reste non sélectionnable tant que la capability `Block` n'est pas explicitement déclarée dans un Transport same-network. Les profils publics standard committed Devnet/Mainnet/Testnet la déclarent désormais explicitement ; la méthode reste unstable et sera revalidée au Start.
### 8.4 `helius-transaction-hydrated`
@@ -728,7 +728,7 @@ Restauration du template Desk KSP clair, bornage de la grille de routes et corre
Résoudre Config côté Rust et projeter les routes composables/non composables avec raisons sûres et génération d'inventaire, sans endpoint, URL, secret ou source key côté frontend.
**État : implémenté puis corrigé par `pre.006-fix.001`.** Le Desk ouvre uniquement la dépendance directe `ksp-onchain-transport-lib` nécessaire aux capabilities typées. Le composite applicatif est réseau-centrique (`devnet`, `mainnet`, `testnet`) et peut référencer plusieurs profils Transport du même réseau. Le backend agrège leurs capabilities sans inférer quoi que ce soit du provider : PublicNode Mainnet Yellowstone est donc une capability de `mainnet`, pas un pseudo-réseau ni un profil opérateur séparé. Les sources optionnelles Helius/Yellowstone dont le secret manque ne désactivent que la route qui dépend de cette source. HTTP est prouvé directement par rôles + `request_kinds`, WS par `WsProtocolKind` + `WsSubscriptionKind`, Yellowstone par présence d'un endpoint gRPC activé. Aucun Store, session Transport ou Worker n'est ouvert.
**État : implémenté puis corrigé par `pre.006-fix.001` et `pre.006-fix.002`.** Le Desk ouvre uniquement la dépendance directe `ksp-onchain-transport-lib` nécessaire aux capabilities typées. Le composite applicatif est réseau-centrique (`devnet`, `mainnet`, `testnet`) et peut référencer plusieurs profils Transport du même réseau. Le backend agrège leurs capabilities sans inférer quoi que ce soit du provider : PublicNode Mainnet Yellowstone est donc une capability de `mainnet`, pas un pseudo-réseau ni un profil opérateur séparé. Les sources optionnelles Helius/Yellowstone dont le secret manque ne désactivent que la route qui dépend de cette source. HTTP est prouvé directement par rôles + `request_kinds`, WS par `WsProtocolKind` + `WsSubscriptionKind`, Yellowstone par présence d'un endpoint gRPC activé. Aucun Store, session Transport ou Worker n'est ouvert.
Budget cible : une tranche composition.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/validation/032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md -->
<!-- version: 8 -->
<!-- version: 9 -->
# Validation v0.3.15 — Raw Transaction Ingest Desk
@@ -362,12 +362,20 @@ Le forecast durable est porté par la section 20 du plan `036` sous forme hiéra
### `pre.006` — inventaire composable
**Statut : gate opérateur de `pre.006` en échec fonctionnel, corrigé par `pre.006-fix.001`.** Le premier inventaire classait encore les capabilities par profil Transport fournisseur : `Yellowstone + HTTP hydration` Mainnet n'était donc `Configured` que sous `publicnode_mainnet`. Le fix remplace cette projection par trois profils réseau (`devnet`, `mainnet`, `testnet`) qui agrègent les sources Transport same-network. PublicNode, Helius et OrbitFlare restent des sources de capabilities et ne deviennent jamais des identités réseau. Un secret optionnel absent n'invalide que la route dépendante. Le canari qui dépendait du Config utilisateur de la machine est remplacé par un moteur Config committé et un environnement injecté. Aucun I/O runtime n'est ajouté.
**Statut : gate opérateur de `pre.006` en échec fonctionnel, puis corrections `pre.006-fix.001` et `pre.006-fix.002`.** Le premier inventaire classait encore les capabilities par profil Transport fournisseur : `Yellowstone + HTTP hydration` Mainnet n'était donc `Configured` que sous `publicnode_mainnet`. Le fix remplace cette projection par trois profils réseau (`devnet`, `mainnet`, `testnet`) qui agrègent les sources Transport same-network. PublicNode, Helius et OrbitFlare restent des sources de capabilities et ne deviennent jamais des identités réseau. Un secret optionnel absent n'invalide que la route dépendante. Le canari qui dépendait du Config utilisateur de la machine est remplacé par un moteur Config committé et un environnement injecté. Aucun I/O runtime n'est ajouté.
#### `pre.006-fix.001` — correction classification réseau
Attendus : `mainnet` expose Yellowstone dès que la source PublicNode Mainnet est effectivement résolue, quelle que soit la source HTTP/WS standard retenue dans le même profil réseau ; `mainnet` reste utilisable pour Standard Logs et HTTP Block Polling lorsque les secrets Helius/PublicNode sont absents. Le même principe s'applique à Devnet et Testnet.
#### `pre.006-fix.002` — Standard Block réellement composable
Le gate de `pre.006-fix.001` révèle deux points : les canaris Desk ne compilent pas car ils appellent `ConfigEnvironment::from_maps`, helper `pub(crate)`/`cfg(test)` de `ksp-config-lib`, et `standard-block-direct` reste systématiquement indisponible parce qu'aucun endpoint standard committed ne déclare `Block`.
Le fix déclare explicitement `Block` sur les endpoints `solana-public` Standard WS committés de Devnet, Mainnet et Testnet, sans toucher Helius LaserStream. L'inventaire réseau-centrique projette donc `standard-block-direct` en `Configured` sur les trois réseaux. Cette classification reste une preuve Config de composabilité, pas une preuve live permanente : `blockSubscribe` reste unstable et validator/provider-gated, donc `pre.007`/Start devra reconstruire et revalider la ressource exacte avant exécution.
Les canaris Desk n'utilisent plus l'API crate-private de Config. Ils valident les routes standard à partir de `ConfigEnvironment::load()` et acceptent que les routes optionnelles Helius/Yellowstone soient soit `Configured`, soit `missing_required_secret` selon l'environnement opérateur.
### `pre.007` — ressources Start
Reconstruction/revalidation Start des ressources Transport pour les cinq routes, sans launch Worker.