From 3c4ae51ae28f6ec4af60759320e564a1f5ff0e65 Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Wed, 2 Sep 2026 15:05:18 +0200 Subject: [PATCH] v0.3.7-pre.006 --- Cargo.toml | 4 +- config/composite.ksp-app-backfill-desk.json | 6 +- config/std.transport.json | 95 +++++++++++++++++++ .../ksp-app-backfill-desk/frontend/main.html | 35 +++++-- .../ksp-app-backfill-desk/frontend/ts/main.ts | 75 ++++++++++++++- crates/ksp-app-backfill-desk/src/app_state.rs | 6 +- .../ksp-app-backfill-desk/src/dto_common.rs | 27 ++++-- crates/ksp-app-backfill-desk/src/lib.rs | 6 +- .../src/transport_runtime.rs | 27 +++--- .../tests/config_composition.rs | 65 +++++++++++-- .../tests/desktop_contract.rs | 31 +++++- .../tests/desktop_security.rs | 20 +++- .../unit_tests/dto_common.rs | 6 +- .../unit_tests/transport_runtime.rs | 11 ++- .../composite.ksp-app-backfill-desk.json | 2 +- deltas/0.3.7/pre.006.md | 66 +++++++++++++ docs/plans/028-V0_3_7_BACKFILL_DESK_PLAN.md | 54 ++++++----- docs/validation/024-V0_3_7_BACKFILL_DESK.md | 21 +++- 18 files changed, 475 insertions(+), 82 deletions(-) create mode 100644 deltas/0.3.7/pre.006.md diff --git a/Cargo.toml b/Cargo.toml index d403e01..32a1d8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 422 +# version: 423 [workspace] resolver = "3" members = ["crates/ksp-app-backfill-desk", "crates/ksp-app-config-desk", "crates/ksp-app-solprices-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-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib"] [workspace.package] -version = "0.3.7-pre.5.fix.1" +version = "0.3.7-pre.6" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/config/composite.ksp-app-backfill-desk.json b/config/composite.ksp-app-backfill-desk.json index 11a2914..7c21eca 100644 --- a/config/composite.ksp-app-backfill-desk.json +++ b/config/composite.ksp-app-backfill-desk.json @@ -1,6 +1,6 @@ { "format_version": 1, - "default_profile": "devnet", + "default_profile": "mainnet", "profiles": [ { "profile_id": "devnet", @@ -28,12 +28,12 @@ { "component_id": "logging", "file_id": "cfg.std.logging", - "profile_id": "console_info" + "profile_id": "supertrace" }, { "component_id": "transport", "file_id": "cfg.std.transport", - "profile_id": "mainnet_public" + "profile_id": "mainnet_backfill_pool" }, { "component_id": "store", diff --git a/config/std.transport.json b/config/std.transport.json index 2a83af6..79c8582 100644 --- a/config/std.transport.json +++ b/config/std.transport.json @@ -178,6 +178,101 @@ } ] }, + { + "profile_id": "mainnet_backfill_pool", + "endpoints": [ + { + "name": "solana_mainnet_public", + "enabled": true, + "provider": "solana-public", + "cluster": "mainnet-beta", + "url": "${KSP_PUBLIC_SOLANA_MAINNET_HTTP_URL:-https://api.mainnet-beta.solana.com}", + "connect_timeout_ms": 5000, + "request_timeout_ms": 15000, + "max_idle_connections_per_host": 8, + "roles": [ + { + "role": "backfill_pool", + "enabled": true, + "request_kinds": [ + "*" + ], + "priority": 100, + "limits": { + "requests_per_second": 5, + "burst_capacity": 10, + "max_concurrent_requests": 8, + "pause_after_rate_limit_ms": 1000 + } + }, + { + "role": "backfill_solana_public", + "enabled": true, + "request_kinds": [ + "*" + ], + "priority": 100, + "limits": { + "requests_per_second": 5, + "burst_capacity": 10, + "max_concurrent_requests": 8, + "pause_after_rate_limit_ms": 1000 + } + } + ] + }, + { + "name": "publicnode_solana_mainnet_http", + "enabled": true, + "provider": "publicnode", + "cluster": "mainnet-beta", + "url": "https://solana-rpc.publicnode.com", + "connect_timeout_ms": 5000, + "request_timeout_ms": 15000, + "max_idle_connections_per_host": 8, + "roles": [ + { + "role": "backfill_pool", + "enabled": true, + "request_kinds": [ + "*" + ], + "priority": 100, + "limits": { + "requests_per_second": 5, + "burst_capacity": 10, + "max_concurrent_requests": 8, + "pause_after_rate_limit_ms": 1000 + } + }, + { + "role": "backfill_publicnode", + "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_mainnet_public_ws", + "enabled": true, + "provider": "solana-public", + "cluster": "mainnet-beta", + "kind": "solana_standard", + "url": "${KSP_PUBLIC_SOLANA_MAINNET_WS_URL:-wss://api.mainnet-beta.solana.com}" + } + ] + }, { "profile_id": "publicnode_mainnet", "endpoints": [ diff --git a/crates/ksp-app-backfill-desk/frontend/main.html b/crates/ksp-app-backfill-desk/frontend/main.html index f78993c..e13b2bb 100644 --- a/crates/ksp-app-backfill-desk/frontend/main.html +++ b/crates/ksp-app-backfill-desk/frontend/main.html @@ -1,5 +1,5 @@ - + @@ -51,11 +51,34 @@ -
- -
-

Backfill runtime non ouvert dans pre.002

-

Aucun Transport, Store ou Job Backfill n'est encore créé par le frontend.

+
+
+
+
Transport HTTP
+
+ + +
Le choix reste logique : aucune URL ni clé provider n'est exposée au frontend.
+
+
Réseau
+
Transport
+
Store
+
Composition
+
+ +
+
+
+
+
+ +
+

Campagne Backfill

+

Les scopes, commitments et le bouton Start seront branchés dans la tranche suivante. Le transport reste HTTP uniquement en v0.3.7.

+
+
diff --git a/crates/ksp-app-backfill-desk/frontend/ts/main.ts b/crates/ksp-app-backfill-desk/frontend/ts/main.ts index 7763899..a3850eb 100644 --- a/crates/ksp-app-backfill-desk/frontend/ts/main.ts +++ b/crates/ksp-app-backfill-desk/frontend/ts/main.ts @@ -1,10 +1,12 @@ // file: crates/ksp-app-backfill-desk/frontend/ts/main.ts -// version: 1 +// version: 2 import "bootstrap"; import ResizeObserver from "resize-observer-polyfill"; import "simplebar"; import { getCurrentWindow } from "@tauri-apps/api/window"; +import type { BackfillDeskOptionsDto } from "./bindings/ksp_app_backfill_desk/dto_common/BackfillDeskOptionsDto.ts"; +import type { BackfillHttpRouteOptionDto } from "./bindings/ksp_app_backfill_desk/dto_common/BackfillHttpRouteOptionDto.ts"; import type { ShellStatusDto } from "./bindings/ksp_app_backfill_desk/dto_common/ShellStatusDto.ts"; import { frontendDebug, frontendInfo, frontendTrace, frontendWarn, installFrontendConsoleBridge } from "./frontend_log"; import { invokeKsp } from "./invoke"; @@ -77,6 +79,74 @@ function bindNavigation(): void { frontendTrace("main", "Backfill Desk navigation handlers installed"); } + +function routeLabel(route: BackfillHttpRouteOptionDto): string { + if (route.pooled) { + return `Pool multi-provider — ${route.providers.join(" + ")}`; + } + return route.providers.length === 1 ? `Provider — ${route.providers[0]}` : route.role; +} + +function renderBackfillOptions(options: BackfillDeskOptionsDto): void { + const select = document.querySelector("#backfillHttpRoute"); + if (select) { + const previous = select.value; + select.replaceChildren(); + for (const route of options.httpRoutes) { + const option = document.createElement("option"); + option.value = route.role; + option.textContent = routeLabel(route); + select.append(option); + } + select.disabled = !options.transportReady || options.httpRoutes.length === 0; + const reusable = options.httpRoutes.some(route => route.role === previous); + if (reusable) { + select.value = previous; + } + } + const values: Record = { + backfillNetwork: options.configuredNetworks.length === 1 ? options.configuredNetworks[0] : options.configuredNetworks.join(", ") || "indisponible", + backfillTransportReady: options.transportReady ? "ready" : "non ready", + backfillStoreReady: options.storeReady ? "ready" : "non ready", + backfillCompositionReady: options.compositionReady ? "ready" : "non ready", + }; + for (const [id, value] of Object.entries(values)) { + const element = document.querySelector(`#${id}`); + if (element) { + element.textContent = value; + } + } + const diagnostic = document.querySelector("#backfillOptionsDiagnostic"); + if (diagnostic) { + const error = options.transportDiagnostic ?? options.storeDiagnostic; + diagnostic.hidden = error === null; + diagnostic.textContent = error === null ? "" : `${error.domain}/${error.code}: ${error.message}`; + } + frontendTrace("main", "Backfill Desk HTTP route options rendered", { + compositionReady: options.compositionReady, + routeCount: options.httpRoutes.length, + transportReady: options.transportReady, + }); +} + +async function loadBackfillOptions(source: "startup" | "user"): Promise { + frontendDebug("main", "Backfill Desk HTTP route options load started", { source }); + const options = await invokeKsp("main", "backfill_options"); + renderBackfillOptions(options); + frontendDebug("main", "Backfill Desk HTTP route options load completed", { source, routeCount: options.httpRoutes.length }); +} + +function bindBackfillRouteSelection(): void { + const select = document.querySelector("#backfillHttpRoute"); + if (!select) { + return; + } + select.addEventListener("change", () => { + frontendDebug("main", "Backfill Desk HTTP route selection changed", { role: select.value || "none" }); + }); + frontendTrace("main", "Backfill Desk HTTP route selection handler installed"); +} + function renderRuntimeStatus(status: ShellStatusDto): void { const values: Record = { runtimeVersion: status.applicationVersion, @@ -131,9 +201,10 @@ async function initializeMain(): Promise { bindFrontendInteractions(); bindNavigation(); bindRuntimeStatusRefresh(); + bindBackfillRouteSelection(); activateView("backfill", "startup"); try { - await loadRuntimeStatus("startup"); + await Promise.all([loadRuntimeStatus("startup"), loadBackfillOptions("startup")]); } catch { frontendWarn("main", "Backfill Desk startup runtime status load failed"); } diff --git a/crates/ksp-app-backfill-desk/src/app_state.rs b/crates/ksp-app-backfill-desk/src/app_state.rs index 1fe0d93..4d356a7 100644 --- a/crates/ksp-app-backfill-desk/src/app_state.rs +++ b/crates/ksp-app-backfill-desk/src/app_state.rs @@ -1,5 +1,5 @@ // file: crates/ksp-app-backfill-desk/src/app_state.rs -// version: 3 +// version: 4 //! Shared backend state owned by the Backfill Desk Tauri application. @@ -121,7 +121,7 @@ impl crate::AppState { application_version: env!("CARGO_PKG_VERSION").to_owned(), config_document_count: document_count, fallback_logging_active: runtime.fallback_active, - shell_phase: "pre.005-store-readiness".to_owned(), + shell_phase: "pre.006-mainnet-http-routing".to_owned(), startup_diagnostic: runtime.startup_diagnostic.clone(), }); } @@ -132,7 +132,7 @@ impl crate::AppState { let options = match runtime { std::option::Option::Some(value) => value.options(), std::option::Option::None => std::result::Result::Ok(crate::BackfillDeskOptionsDto { - compatible_roles: std::vec::Vec::new(), + http_routes: std::vec::Vec::new(), composition_ready: false, configured_networks: std::vec::Vec::new(), network_coherent: false, diff --git a/crates/ksp-app-backfill-desk/src/dto_common.rs b/crates/ksp-app-backfill-desk/src/dto_common.rs index db8b5f9..03d4b47 100644 --- a/crates/ksp-app-backfill-desk/src/dto_common.rs +++ b/crates/ksp-app-backfill-desk/src/dto_common.rs @@ -1,20 +1,35 @@ // file: crates/ksp-app-backfill-desk/src/dto_common.rs -// version: 3 +// version: 4 //! Common Tauri DTOs shared by the Backfill Desk shell. use ts_rs::TS; // rust-rules: trait-import -/// Safe Transport-readiness subset of the Backfill Desk options contract. +/// Safe HTTP route exposed for operator selection before a Backfill run. /// -/// Campaign scopes, commitments and backend-owned bounds are added in the dedicated request/DTO slice. Endpoint URLs, provider identities and credentials -/// are intentionally absent. +/// The route is identified only by the logical Transport role. Provider labels are safe metadata used for operator choice; endpoint URLs and credentials never +/// cross IPC. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_backfill_desk/dto_common/BackfillHttpRouteOptionDto.ts")] +pub(crate) struct BackfillHttpRouteOptionDto { + /// Whether more than one configured provider participates in this logical route. + pub(crate) pooled: bool, + /// Safe provider labels participating in the route, sorted and deduplicated. + pub(crate) providers: std::vec::Vec, + /// Logical Transport role later passed to the Backfill runtime. + pub(crate) role: String, +} + +/// Safe Transport-and-Store readiness subset of the Backfill Desk options contract. +/// +/// Campaign scopes, commitments and backend-owned bounds are added in the dedicated request/DTO slice. Endpoint URLs and credentials are intentionally absent. #[derive(Clone, Debug, serde::Serialize, TS)] #[serde(rename_all = "camelCase")] #[ts(export, export_to = "../frontend/ts/bindings/ksp_app_backfill_desk/dto_common/BackfillDeskOptionsDto.ts")] pub(crate) struct BackfillDeskOptionsDto { - /// Logical roles that can route both RPC methods required by the Backfill runtime. - pub(crate) compatible_roles: std::vec::Vec, + /// Selectable logical HTTP routes that support both RPC methods required by the Backfill runtime. + pub(crate) http_routes: std::vec::Vec, /// Whether the Transport and Store readiness gates jointly permit later Backfill composition. pub(crate) composition_ready: bool, /// Distinct enabled HTTP cluster labels selected by the active Transport profile. diff --git a/crates/ksp-app-backfill-desk/src/lib.rs b/crates/ksp-app-backfill-desk/src/lib.rs index c2521f1..84f76ae 100644 --- a/crates/ksp-app-backfill-desk/src/lib.rs +++ b/crates/ksp-app-backfill-desk/src/lib.rs @@ -1,5 +1,5 @@ // file: crates/ksp-app-backfill-desk/src/lib.rs -// version: 4 +// version: 5 //! Tauri desktop application scaffold for controlling and inspecting KSP RAW backfill jobs. @@ -62,8 +62,10 @@ pub(crate) use self::constants::TRACING_TARGET_FRONTEND; pub(crate) use self::constants::TRACING_TARGET_FRONTEND_MAIN; /// Owning target for splash-window frontend events. pub(crate) use self::constants::TRACING_TARGET_FRONTEND_SPLASH; -/// Safe Transport-readiness subset of the Backfill Desk options contract. +/// Safe Transport-and-Store readiness subset of the Backfill Desk options contract. pub(crate) use self::dto_common::BackfillDeskOptionsDto; +/// Safe logical HTTP route exposed for operator selection. +pub(crate) use self::dto_common::BackfillHttpRouteOptionDto; /// Safe command error projection exposed to Tauri commands. pub(crate) use self::dto_common::CommandErrorDto; /// Safe scaffold/runtime snapshot exposed to the shell. diff --git a/crates/ksp-app-backfill-desk/src/transport_runtime.rs b/crates/ksp-app-backfill-desk/src/transport_runtime.rs index 945ad29..95dd0bd 100644 --- a/crates/ksp-app-backfill-desk/src/transport_runtime.rs +++ b/crates/ksp-app-backfill-desk/src/transport_runtime.rs @@ -1,7 +1,7 @@ // file: crates/ksp-app-backfill-desk/src/transport_runtime.rs -// version: 2 +// version: 3 -//! Composite-selected HTTP Transport readiness owned by Backfill Desk. +//! Composite-selected HTTP Transport readiness and route inventory owned by Backfill Desk. /// Safe and executable Transport runtime retained by the application state. pub(crate) struct TransportRuntime { @@ -30,16 +30,16 @@ impl TransportRuntime { pub(crate) fn options(&self) -> ksp_core_lib::Result { let snapshot = self.pool.snapshot(); let configured_networks = configured_networks(&snapshot); - let compatible_roles = compatible_backfill_roles(&self.pool, &snapshot); - let compatible_roles = match compatible_roles { + let http_routes = compatible_backfill_http_routes(&self.pool, &snapshot); + let http_routes = match http_routes { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; - let transport_ready = configured_networks.len() == 1 && !compatible_roles.is_empty() && snapshot.available_endpoint_count() > 0; + let transport_ready = configured_networks.len() == 1 && !http_routes.is_empty() && snapshot.available_endpoint_count() > 0; return std::result::Result::Ok(crate::BackfillDeskOptionsDto { - compatible_roles, composition_ready: false, configured_networks, + http_routes, network_coherent: false, store_diagnostic: std::option::Option::None, store_network: std::option::Option::None, @@ -89,17 +89,17 @@ pub(crate) fn initialize_transport(management: &ksp_config_lib::ConfigManagement domain = crate::TRACING_DOMAIN_TRANSPORT, transport_profile = runtime.profile_id(), network_count = options.configured_networks.len(), - compatible_role_count = options.compatible_roles.len(), + http_route_count = options.http_routes.len(), transport_ready = options.transport_ready, "initialized Backfill Desk HTTP Transport readiness from composite-managed configuration" ); return std::result::Result::Ok(runtime); } -fn compatible_backfill_roles( +fn compatible_backfill_http_routes( pool: &ksp_onchain_transport_lib::HttpTransportPool, snapshot: &ksp_onchain_transport_lib::HttpTransportPoolSnapshot, -) -> ksp_core_lib::Result> { +) -> ksp_core_lib::Result> { let signatures = required_http_rpc_method("getSignaturesForAddress"); let signatures = match signatures { std::result::Result::Ok(value) => value, @@ -110,22 +110,23 @@ fn compatible_backfill_roles( std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; - let mut candidates = std::collections::BTreeSet::new(); + let mut candidates = std::collections::BTreeMap::>::new(); for endpoint in snapshot.endpoints() { if !endpoint.enabled() { continue; } for role in endpoint.roles() { if role.enabled() { - candidates.insert(role.role().to_owned()); + candidates.entry(role.role().to_owned()).or_default().insert(endpoint.provider().to_owned()); } } } let mut compatible = std::vec::Vec::new(); - for candidate in candidates { + for (candidate, providers) in candidates { let role = ksp_onchain_transport_lib::HttpRoleName::new(candidate.clone()); if pool.select_for_method(&role, signatures).is_ok() && pool.select_for_method(&role, transaction).is_ok() { - compatible.push(candidate); + let providers = providers.into_iter().collect::>(); + compatible.push(crate::BackfillHttpRouteOptionDto { pooled: providers.len() > 1, providers, role: candidate }); } } return std::result::Result::Ok(compatible); diff --git a/crates/ksp-app-backfill-desk/tests/config_composition.rs b/crates/ksp-app-backfill-desk/tests/config_composition.rs index 60bfe53..98e0003 100644 --- a/crates/ksp-app-backfill-desk/tests/config_composition.rs +++ b/crates/ksp-app-backfill-desk/tests/config_composition.rs @@ -1,7 +1,7 @@ // file: crates/ksp-app-backfill-desk/tests/config_composition.rs -// version: 4 +// version: 5 -//! Config composite, Transport readiness and Store-network composition contracts for Backfill Desk through pre.005. +//! Config composite, Transport readiness and Store-network composition contracts for Backfill Desk through pre.006. #![forbid(unsafe_code)] #![deny(unreachable_pub)] @@ -32,7 +32,7 @@ fn assert_component(composite: &ksp_config_lib::ResolvedConfigComposite, compone } #[test] -fn pre_003_default_composite_selects_trace_devnet_logging_transport_and_store() { +fn pre_003_devnet_profile_selects_trace_logging_transport_and_store() { let engine = workspace_engine(); assert!(engine.is_ok(), "workspace Config engine should be constructible: {engine:?}"); let engine = match engine { @@ -45,8 +45,8 @@ fn pre_003_default_composite_selects_trace_devnet_logging_transport_and_store() std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; - let composite = engine.load_resolved_composite(&file_id, std::option::Option::None); - assert!(composite.is_ok(), "committed Backfill Desk composite should resolve: {composite:?}"); + let composite = engine.load_resolved_composite(&file_id, std::option::Option::Some("devnet")); + assert!(composite.is_ok(), "committed Backfill Desk devnet profile should resolve: {composite:?}"); if let std::result::Result::Ok(composite) = composite { assert_eq!(composite.profile_id(), "devnet"); assert_component(&composite, "logging", ksp_config_lib::FILE_ID_STD_LOGGING, "supertrace"); @@ -74,7 +74,7 @@ fn pre_003_named_profiles_keep_transport_and_store_network_selection_paired() { std::result::Result::Err(_) => return, }; for (profile_id, logging_profile, transport_profile, store_profile) in - [("mainnet", "console_info", "mainnet_public", "mainnet"), ("testnet", "console_info", "publicnode_testnet", "testnet")] + [("mainnet", "supertrace", "mainnet_backfill_pool", "mainnet"), ("testnet", "console_info", "publicnode_testnet", "testnet")] { let composite = engine.load_resolved_composite(&file_id, std::option::Option::Some(profile_id)); assert!(composite.is_ok(), "Backfill Desk composite profile {profile_id} should resolve: {composite:?}"); @@ -100,8 +100,8 @@ fn pre_004_default_composite_builds_http_pool_for_both_backfill_rpc_methods() { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; - let composite = engine.load_resolved_composite(&file_id, std::option::Option::None); - assert!(composite.is_ok(), "committed Backfill Desk composite should resolve: {composite:?}"); + let composite = engine.load_resolved_composite(&file_id, std::option::Option::Some("devnet")); + assert!(composite.is_ok(), "committed Backfill Desk devnet profile should resolve: {composite:?}"); let composite = match composite { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, @@ -215,3 +215,52 @@ fn pre_005_all_composite_profiles_keep_declared_http_and_store_networks_identica } } } + +#[test] +fn pre_006_default_mainnet_profile_exposes_pool_and_targeted_http_routes_without_secret_provider_data() { + let engine = workspace_engine(); + assert!(engine.is_ok(), "workspace Config engine should be constructible: {engine:?}"); + let engine = match engine { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let file_id = ksp_config_lib::ConfigFileId::new(ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK); + let file_id = match file_id { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let composite = engine.load_resolved_composite(&file_id, std::option::Option::None); + assert!(composite.is_ok(), "default Backfill Desk composite should resolve: {composite:?}"); + let composite = match composite { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + assert_eq!(composite.profile_id(), "mainnet"); + assert_component(&composite, "logging", ksp_config_lib::FILE_ID_STD_LOGGING, "supertrace"); + assert_component(&composite, "transport", ksp_config_lib::FILE_ID_STD_TRANSPORT, "mainnet_backfill_pool"); + assert_component(&composite, "store", ksp_config_lib::FILE_ID_STD_STORE, "mainnet"); + let transport = match composite.component("transport") { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let profile = transport.resolved().profile(); + let endpoints = profile.get("endpoints").and_then(serde_json::Value::as_array); + assert!(endpoints.is_some(), "mainnet Backfill Transport profile should expose HTTP endpoints"); + let endpoints = match endpoints { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let providers = endpoints + .iter() + .filter_map(|endpoint| endpoint.get("provider").and_then(serde_json::Value::as_str)) + .collect::>(); + assert_eq!(providers, std::collections::BTreeSet::from(["publicnode", "solana-public"])); + let roles = endpoints + .iter() + .flat_map(|endpoint| endpoint.get("roles").and_then(serde_json::Value::as_array).into_iter().flatten()) + .filter_map(|role| role.get("role").and_then(serde_json::Value::as_str)) + .collect::>(); + assert!(roles.contains("backfill_pool")); + assert!(roles.contains("backfill_publicnode")); + assert!(roles.contains("backfill_solana_public")); +} diff --git a/crates/ksp-app-backfill-desk/tests/desktop_contract.rs b/crates/ksp-app-backfill-desk/tests/desktop_contract.rs index 8d71293..d73f558 100644 --- a/crates/ksp-app-backfill-desk/tests/desktop_contract.rs +++ b/crates/ksp-app-backfill-desk/tests/desktop_contract.rs @@ -1,5 +1,5 @@ // file: crates/ksp-app-backfill-desk/tests/desktop_contract.rs -// version: 5 +// version: 6 //! Structural desktop contract checks for the Backfill Desk scaffold. @@ -193,8 +193,9 @@ fn pre_004_transport_runtime_builds_pool_and_validates_both_required_rpc_methods assert!(transport.contains("getTransaction")); assert!(transport.contains("select_for_method")); assert!(transport.contains("configured_networks")); - assert!(transport.contains("compatible_backfill_roles")); - for forbidden in ["provider()", "url()", "Store::open", "BackfillJobRuntime", "BackfillRequest"] { + assert!(transport.contains("compatible_backfill_http_routes")); + assert!(transport.contains("endpoint.provider()")); + for forbidden in ["url()", "Store::open", "BackfillJobRuntime", "BackfillRequest"] { assert!(!transport.contains(forbidden), "pre.004 opens or projects forbidden surface {forbidden}"); } let tauri = read_text(root.join("src/tauri.rs").as_path()); @@ -220,7 +221,6 @@ fn pre_005_store_readiness_proves_network_before_open_and_closes_on_main_window_ assert!(!store.contains("BackfillRequest")); let app_state = read_text(root.join("src/app_state.rs").as_path()); assert!(app_state.contains("initialize_store")); - assert!(app_state.contains("pre.005-store-readiness")); assert!(app_state.contains("composition_ready")); assert!(app_state.contains("close_store")); let tauri = read_text(root.join("src/tauri.rs").as_path()); @@ -228,3 +228,26 @@ fn pre_005_store_readiness_proves_network_before_open_and_closes_on_main_window_ assert!(tauri.contains(required), "missing bounded Store shutdown lifecycle marker {required}"); } } + +#[test] +fn pre_006_mainnet_default_and_http_route_selector_are_explicit_without_start_runtime() { + let root = app_root(); + let workspace = root.join("../.."); + let composite = read_text(workspace.join("config/composite.ksp-app-backfill-desk.json").as_path()); + assert!(composite.contains("\"default_profile\": \"mainnet\"")); + assert!(composite.contains("\"profile_id\": \"mainnet_backfill_pool\"")); + let transport = read_text(workspace.join("config/std.transport.json").as_path()); + for required in ["mainnet_backfill_pool", "backfill_pool", "backfill_publicnode", "backfill_solana_public", "solana-rpc.publicnode.com"] { + assert!(transport.contains(required), "missing Backfill HTTP route marker {required}"); + } + let app_state = read_text(root.join("src/app_state.rs").as_path()); + assert!(app_state.contains("pre.006-mainnet-http-routing")); + let frontend = read_text(root.join("frontend/ts/main.ts").as_path()); + assert!(frontend.contains("Backfill Desk HTTP route selection changed")); + assert!(frontend.contains("backfill_options")); + let html = read_text(root.join("frontend/main.html").as_path()); + assert!(html.contains("backfillHttpRoute")); + for forbidden in ["backfill_start", "BackfillJobRuntime", "grpc", "websocket"] { + assert!(!frontend.contains(forbidden), "pre.006 frontend opens deferred runtime marker {forbidden}"); + } +} diff --git a/crates/ksp-app-backfill-desk/tests/desktop_security.rs b/crates/ksp-app-backfill-desk/tests/desktop_security.rs index 64eb615..1e2d279 100644 --- a/crates/ksp-app-backfill-desk/tests/desktop_security.rs +++ b/crates/ksp-app-backfill-desk/tests/desktop_security.rs @@ -1,5 +1,5 @@ // file: crates/ksp-app-backfill-desk/tests/desktop_security.rs -// version: 5 +// version: 6 //! Security and dependency-boundary checks for the Backfill Desk scaffold. @@ -137,7 +137,23 @@ fn pre_005_opens_store_facade_without_backend_or_job_dependencies() { } let dto = read_text(root.join("src/dto_common.rs").as_path()); assert!(dto.contains("BackfillDeskOptionsDto")); - for forbidden in ["pub(crate) provider", "endpoint_url", "pub(crate) url", "pub(crate) credential", "pub(crate) token", "connection_uri"] { + for forbidden in ["endpoint_url", "pub(crate) url", "pub(crate) credential", "pub(crate) token", "connection_uri"] { assert!(!dto.contains(forbidden), "readiness options DTO source contains forbidden field marker {forbidden}"); } } + +#[test] +fn pre_006_frontend_receives_only_safe_http_route_metadata_and_no_endpoint_material() { + let root = app_root(); + let dto = read_text(root.join("src/dto_common.rs").as_path()); + assert!(dto.contains("BackfillHttpRouteOptionDto")); + assert!(dto.contains("pub(crate) providers")); + assert!(dto.contains("pub(crate) role")); + for forbidden in ["endpoint_url", "pub(crate) url", "pub(crate) credential", "pub(crate) api_key", "pub(crate) authorization"] { + assert!(!dto.contains(forbidden), "route DTO leaks forbidden endpoint material marker {forbidden}"); + } + let frontend = read_text(root.join("frontend/ts/main.ts").as_path()); + for forbidden in ["http://", "https://", "apiKey", "authorization", "token"] { + assert!(!frontend.contains(forbidden), "frontend embeds forbidden provider material marker {forbidden}"); + } +} diff --git a/crates/ksp-app-backfill-desk/unit_tests/dto_common.rs b/crates/ksp-app-backfill-desk/unit_tests/dto_common.rs index ba27cc4..2f4ee9f 100644 --- a/crates/ksp-app-backfill-desk/unit_tests/dto_common.rs +++ b/crates/ksp-app-backfill-desk/unit_tests/dto_common.rs @@ -1,5 +1,5 @@ // file: crates/ksp-app-backfill-desk/unit_tests/dto_common.rs -// version: 3 +// version: 4 #[test] fn command_error_projection_excludes_arbitrary_context_and_source_values() { @@ -16,7 +16,7 @@ fn command_error_projection_excludes_arbitrary_context_and_source_values() { #[test] fn transport_options_projection_contains_only_safe_transport_metadata() { let dto = crate::BackfillDeskOptionsDto { - compatible_roles: vec!["default".to_owned()], + http_routes: vec![crate::BackfillHttpRouteOptionDto { pooled: false, providers: vec!["solana-public".to_owned()], role: "default".to_owned() }], composition_ready: true, configured_networks: vec!["devnet".to_owned()], network_coherent: true, @@ -32,7 +32,7 @@ fn transport_options_projection_contains_only_safe_transport_metadata() { let serialized = value.to_string(); assert!(serialized.contains("default")); assert!(serialized.contains("devnet")); - assert!(!serialized.contains("provider")); + assert!(serialized.contains("solana-public")); assert!(!serialized.contains("url")); assert!(!serialized.contains("endpoint")); assert!(!serialized.contains("postgres")); diff --git a/crates/ksp-app-backfill-desk/unit_tests/transport_runtime.rs b/crates/ksp-app-backfill-desk/unit_tests/transport_runtime.rs index 5213361..ae877bf 100644 --- a/crates/ksp-app-backfill-desk/unit_tests/transport_runtime.rs +++ b/crates/ksp-app-backfill-desk/unit_tests/transport_runtime.rs @@ -46,7 +46,7 @@ fn pool(endpoints: std::vec::Vec return, }; let snapshot = runtime_pool.snapshot(); - let roles = super::compatible_backfill_roles(&runtime_pool, &snapshot); + let roles = super::compatible_backfill_http_routes(&runtime_pool, &snapshot); assert!(roles.is_ok()); if let std::result::Result::Ok(roles) = roles { - assert_eq!(roles, vec!["complete".to_owned()]); + assert_eq!(roles.len(), 1); + if let std::option::Option::Some(route) = roles.first() { + assert_eq!(route.role, "complete"); + assert_eq!(route.providers, vec!["fixture".to_owned()]); + assert!(!route.pooled); + } } } diff --git a/crates/ksp-config-lib/unit_tests/fixtures/composite.ksp-app-backfill-desk.json b/crates/ksp-config-lib/unit_tests/fixtures/composite.ksp-app-backfill-desk.json index a9ad328..50f4e95 100644 --- a/crates/ksp-config-lib/unit_tests/fixtures/composite.ksp-app-backfill-desk.json +++ b/crates/ksp-config-lib/unit_tests/fixtures/composite.ksp-app-backfill-desk.json @@ -1,6 +1,6 @@ { "format_version": 1, - "default_profile": "devnet", + "default_profile": "mainnet", "profiles": [ { "profile_id": "devnet", diff --git a/deltas/0.3.7/pre.006.md b/deltas/0.3.7/pre.006.md new file mode 100644 index 0000000..3e53c32 --- /dev/null +++ b/deltas/0.3.7/pre.006.md @@ -0,0 +1,66 @@ +# Delta `0.3.7-pre.006` + +## Base requise + +`0.3.7-pre.005-fix.001`. + +## Objectif + +Faire de Mainnet le profil Backfill Desk par défaut et introduire une sélection HTTP opérateur entre pool multi-provider et provider ciblé, sans ouvrir encore le runtime Job/Start. + +## Fichiers ajoutés + +- `deltas/0.3.7/pre.006.md` + +## Fichiers modifiés + +- `Cargo.toml` +- `config/composite.ksp-app-backfill-desk.json` +- `config/std.transport.json` +- `crates/ksp-config-lib/unit_tests/fixtures/composite.ksp-app-backfill-desk.json` +- `crates/ksp-app-backfill-desk/src/app_state.rs` +- `crates/ksp-app-backfill-desk/src/dto_common.rs` +- `crates/ksp-app-backfill-desk/src/lib.rs` +- `crates/ksp-app-backfill-desk/src/transport_runtime.rs` +- `crates/ksp-app-backfill-desk/unit_tests/dto_common.rs` +- `crates/ksp-app-backfill-desk/unit_tests/transport_runtime.rs` +- `crates/ksp-app-backfill-desk/tests/config_composition.rs` +- `crates/ksp-app-backfill-desk/tests/desktop_contract.rs` +- `crates/ksp-app-backfill-desk/tests/desktop_security.rs` +- `crates/ksp-app-backfill-desk/frontend/main.html` +- `crates/ksp-app-backfill-desk/frontend/ts/main.ts` +- `docs/plans/028-V0_3_7_BACKFILL_DESK_PLAN.md` +- `docs/validation/024-V0_3_7_BACKFILL_DESK.md` + +## Fichiers supprimés + +Aucun. + +## Décisions + +- Mainnet devient le profil composite par défaut ; Devnet/Testnet restent configurables. +- Backfill reste HTTP-only en `0.3.7`. +- `backfill_pool` route Solana Public + PublicNode ; deux rôles dédiés permettent le ciblage d'un provider. +- Le frontend ne choisit jamais une URL : il choisit un rôle logique Transport. +- Helius HTTP peut être ajouté ultérieurement au même mécanisme de rôles via Config/Transport, sans modifier le contrat Job. +- Yellowstone gRPC est reporté à une version ultérieure (`0.3.12+` indicatif) ; WebSocket reste hors scope. + +## Validations exécutées dans l'environnement d'assemblage + +- audits Python Rust/workspace ; +- audit Markdown ; +- parsing JSON/TOML ; +- validation JSON Schema du Transport/composite ; +- contrôles statiques Mainnet/default/rôles HTTP ; +- contrôle TypeScript syntaxique avec bindings temporaires ; +- contrôle différentiel et reconstruction par delta. + +## Validations non exécutées + +`cargo`, `rustfmt`, Clippy et les tests Rust ne sont pas disponibles dans l'environnement d'assemblage. Ils restent obligatoires côté opérateur avant `pre.007`. + +## Questions ouvertes + +- admission et mapping complet `BackfillRequest` en `pre.007` ; +- Helius HTTP ne doit être activé qu'avec une configuration de secret réellement disponible ; +- smoke Mainnet lecture seule après intégration du Start, pas avant. diff --git a/docs/plans/028-V0_3_7_BACKFILL_DESK_PLAN.md b/docs/plans/028-V0_3_7_BACKFILL_DESK_PLAN.md index a2d59d8..79852cb 100644 --- a/docs/plans/028-V0_3_7_BACKFILL_DESK_PLAN.md +++ b/docs/plans/028-V0_3_7_BACKFILL_DESK_PLAN.md @@ -136,17 +136,17 @@ Aucun `BackfillCheckpoint`, `Store`, `HttpTransportPool`, sender/receiver Tokio Le composite cible est `config/composite.ksp-app-backfill-desk.json` avec trois profils cohérents : -| Profil composite | Logging | Transport | Store | Réseau | -|------------------|----------------|----------------------|-----------|----------------| -| `devnet` | `supertrace` | `devnet_public` | `devnet` | `devnet` | -| `mainnet` | `console_info` | `mainnet_public` | `mainnet` | `mainnet-beta` | -| `testnet` | `console_info` | `publicnode_testnet` | `testnet` | `testnet` | +| Profil composite | Logging | Transport | Store | Réseau | +|------------------|----------------|-------------------------|-----------|----------------| +| `devnet` | `supertrace` | `devnet_public` | `devnet` | `devnet` | +| `mainnet` | `supertrace` | `mainnet_backfill_pool` | `mainnet` | `mainnet-beta` | +| `testnet` | `console_info` | `publicnode_testnet` | `testnet` | `testnet` | -Le default V1 est `devnet`, afin de ne pas lancer implicitement une campagne mainnet. Aucun `std.job_backfill.json` n'est créé : les paramètres de campagne restent opérationnels et backend-validés. +Le default V1 est désormais `mainnet`, conformément à la finalité lecture seule du Backfill Desk. `devnet` et `testnet` restent sélectionnables par reconfiguration du composite. Pendant le développement de `0.3.7`, Mainnet conserve le profil Logging `supertrace`. Aucun `std.job_backfill.json` n'est créé : les paramètres de campagne restent opérationnels et backend-validés. Au bootstrap, l'application charge le composite, résout Logging/Transport/Store via `ksp-config-lib`, construit `HttpTransportPool` et ouvre `ksp_store_lib::Store`. Elle compare le réseau Store au(x) cluster(s) HTTP activés et refuse le readiness si une composition mélange des réseaux. -Les rôles présentés à l'UI sont dérivés des snapshots/settings Transport, dédupliqués, puis validés côté Rust comme capables de sélectionner à la fois `getSignaturesForAddress` et `getTransaction`. Le rôle `default` est actuellement le seul rôle des profils standard, mais il n'est pas codé en dur comme contrat du Desk. +Les routes HTTP présentées à l'UI sont dérivées des snapshots/settings Transport, dédupliquées, puis validées côté Rust comme capables de sélectionner à la fois `getSignaturesForAddress` et `getTransaction`. Sur Mainnet, `backfill_pool` regroupe Solana Public et PublicNode à priorité égale, tandis que `backfill_solana_public` et `backfill_publicnode` ciblent un provider unique. Le frontend ne reçoit que le rôle logique et les labels provider sûrs ; URL, credentials, rate-limit interne et retry restent possédés par Transport. Le packaging Config suit le contrat atomique déjà détenu par `ksp-config-lib::prepare_packaged_runtime()` : il traverse l'intégralité de `ConfigFileRegistry::defaults()`. L'ajout du composite Backfill Desk fait donc passer le registre de 13 à 14 descriptors et impose que les quatre applications Desk qui utilisent ce packaging embarquent les 14 resources Config/schema du registre. Backfill Desk embarque ainsi également les documents Wallet/offchain sans les consommer au runtime ; leur présence satisfait le contrat de packaging partagé et n'ouvre aucune dépendance, permission frontend ou responsabilité métier supplémentaire. @@ -202,7 +202,7 @@ Aucun JSON, fichier ou table checkpoint n'est inventé. La reprise après crash/ | Menace | Surface | Contrôle v0.3.7 | |------------------------------|------------------|----------------------------------------------------------------------------------------------| | fuite URI Store | Config/backend | jamais projetée en DTO/log frontend | -| fuite URL/token provider | Transport/Config | uniquement rôle/cluster/readiness sûrs ; aucune URL | +| fuite URL/token provider | Transport/Config | rôle + labels provider sûrs uniquement ; aucune URL/credential | | fuite RAW transaction | snapshots/IPC | snapshot existant ne contient aucun payload ; aucun Store read général | | fuite liste signatures | request/logging | input transmis au command, non journalisé ; DTO retour n'en contient pas | | fuite adresse | instrumentation | ne pas journaliser la valeur ; seulement scope kind/action code | @@ -243,7 +243,7 @@ Le replay opérateur de `pre.003-fix.001` est ensuite intégralement propre : au ### pre.004 — Transport readiness -Construire `HttpTransportPool`, dériver l'inventaire sûr des réseaux/rôles, vérifier le support simultané de `getSignaturesForAddress` et `getTransaction`, puis exposer `backfill_options()` sans provider, endpoint physique ni URL. `BackfillDeskOptionsDto` apparaît ici comme sous-contrat Transport seulement (`configured_networks`, rôles compatibles, diagnostic sûr, readiness) ; `pre.006` l'étendra avec commitments, scopes et bornes plutôt que d'ouvrir prématurément `ksp-job-backfill-lib`. Une composition Transport avec plusieurs clusters HTTP actifs ou sans rôle compatible reste non-ready. Une erreur de résolution/construction Transport est conservée comme diagnostic sûr afin que le shell puisse rester ouvrable. +Construire `HttpTransportPool`, dériver l'inventaire sûr des réseaux/rôles, vérifier le support simultané de `getSignaturesForAddress` et `getTransaction`, puis exposer `backfill_options()` sans provider, endpoint physique ni URL. `BackfillDeskOptionsDto` apparaît ici comme sous-contrat Transport seulement (`configured_networks`, rôles compatibles, diagnostic sûr, readiness) ; `pre.007` l'étendra avec commitments, scopes et bornes après stabilisation du choix HTTP de `pre.006`. Une composition Transport avec plusieurs clusters HTTP actifs ou sans rôle compatible reste non-ready. Une erreur de résolution/construction Transport est conservée comme diagnostic sûr afin que le shell puisse rester ouvrable. Le replay opérateur de `pre.004` est acquis : audits, `cargo check --workspace`, Clippy, toutes les suites ciblées Backfill Desk et les deux arbres de dépendances passent. Le lancement `cargo tauri dev` confirme également le profil Logging `supertrace`, le profil Transport `devnet_public`, un réseau HTTP unique, un rôle compatible et `transport_ready = true`; les clics de tabs frontend apparaissent bien au niveau `DEBUG`. @@ -253,45 +253,49 @@ Ouvrir uniquement `ksp-store-lib::Store` depuis le profil Store sélectionné pa Le `Store` ouvert reste détenu côté Rust et doit être fermé explicitement via `Store::close()` lors d'une demande de fermeture de la fenêtre principale. La Desk intercepte cette fermeture une seule fois, empêche la destruction immédiate, lance le close borné puis quitte l'application. Aucun `BackfillRequest`, `BackfillJobRuntime`, accès backend PostgreSQL direct ou capability Tauri supplémentaire n'est admis dans cette tranche. -### pre.006 — DTO/request mapping +### pre.006 — Mainnet par défaut + sélection route HTTP -Compléter les DTO TS-RS app-owned commencés par le sous-contrat Transport de `pre.004` : quatre scopes, commitments, min context slot, bornes backend-owned, signatures hostiles et validation de rôle/réseau. +Faire de `mainnet` le profil composite par défaut, conserver `supertrace` pendant le développement et ajouter un profil `mainnet_backfill_pool` HTTP-only. Exposer dans `backfill_options()` des routes sûres dérivées des rôles Transport : `backfill_pool` pour Solana Public + PublicNode, `backfill_solana_public` et `backfill_publicnode` pour un ciblage explicite. Le frontend rend ce choix avant Start et trace les changements ; aucune URL/clé ne traverse IPC et `ksp-job-backfill-lib` n'est pas encore ouvert par la Desk. -### pre.007 — runtime + Start +### pre.007 — DTO/request mapping -Single-active-run state, construction `BackfillJobRuntime`, spawn non bloquant, handle installé avant exécution et snapshot initial/terminal. +Ajouter `ksp-job-backfill-lib` et compléter les DTO TS-RS app-owned : quatre scopes HTTP existants, commitments, min context slot, bornes backend-owned, signatures explicites hostiles et rôle HTTP sélectionné. Mapper strictement vers `BackfillRequest`/`HttpRoleName` côté Rust sans Start effectif et refuser toute route qui n'appartient pas à l'inventaire courant. -### pre.008 — monitoring latest-value +### pre.008 — runtime + Start + +Single-active-run state, construction `BackfillJobRuntime`, spawn non bloquant, handle installé avant exécution, admission atomique et snapshot initial/terminal. + +### pre.009 — monitoring latest-value Bridge de snapshots, status DTO, événements coalescés, compteurs complets, resynchronisation et absence de log parsing. -### pre.009 — Cancel et races terminales +### pre.010 — Cancel et races terminales Cancel, état cancelling, idempotence UX, concurrence Start/Cancel/terminal, sémantique de drain Store et fermeture app. -### pre.010 — checkpoint/frontier et Resume +### pre.011 — checkpoint/frontier et Resume Projection sûre, conservation Rust-only du checkpoint, reprise in-session et invariants par scope. -### pre.011 — frontend fonctionnel et polish +### pre.012 — frontend fonctionnel et polish Formulaire complet, états responsive, instrumentation sûre, summary terminal, erreurs, boutons contextuels, aucun secret/browser storage. -### pre.012 — hardening et complétude +### pre.013 — hardening et complétude Tests desktop/security/dependency/composition/release completeness, canaries DTO et scan des dépendances/interdictions. -### pre.013 — gate technique final +### pre.014 — gate technique final -`cargo fmt`, audits, check/clippy/tests workspace, arbres Cargo utiles, `cargo tauri build`, smoke opt-in si environnement sûr disponible. +`cargo fmt`, audits, check/clippy/tests workspace, arbres Cargo utiles, `cargo tauri build`, smoke Mainnet lecture seule opt-in si environnement sûr disponible. -### pre.014 — réconciliation documentaire +### pre.015 — réconciliation documentaire README/USAGE/plan/validation et architecture seulement si la surface finale l'exige. Aucun nouveau runtime. -### pre.015 — préparation publication +### pre.016 — préparation publication -Prompt 0.3.8, CHANGELOG et ROADMAP uniquement avec le bump prerelease/delta requis. +Prompt suivant + CHANGELOG + ROADMAP uniquement avec le bump prerelease/delta requis. ### rel.001 — publication stable @@ -300,3 +304,7 @@ Mécanique de publication `v0.3.7`, sans rattrapage. ## 14. Hors périmètre Store Desk/navigation RAW, Worker API/service daemon, scheduler/registry générique Job, checkpoint durable, retry Job, sélection provider physique, nouveau protocole Transport, migration Store, decoding/materialization, application globale/control desk. + +## 15. Report transport gRPC + +Le Backfill `0.3.7` reste HTTP-only. Une généralisation de `ksp-job-backfill-lib` et de Backfill Desk vers Yellowstone gRPC est reportée à une version ultérieure, avec `0.3.12+` comme cible indicative à réévaluer. WebSocket reste hors Backfill tant qu'un besoin historique concret ne le justifie pas. diff --git a/docs/validation/024-V0_3_7_BACKFILL_DESK.md b/docs/validation/024-V0_3_7_BACKFILL_DESK.md index e7bab3b..81dd7db 100644 --- a/docs/validation/024-V0_3_7_BACKFILL_DESK.md +++ b/docs/validation/024-V0_3_7_BACKFILL_DESK.md @@ -266,6 +266,25 @@ Le correctif ne modifie aucun runtime, aucun composite Config, aucun profil Tran - [X] aucune modification du composite de production ni des settings Transport/Store ; - [X] aucun secret provider requis par le test déterministe de cohérence réseau ; - [X] audits statiques Rust/Markdown rejoués dans l'environnement d'assemblage ; -- [ ] replay opérateur `cargo fmt/check/clippy/test` de `pre.005-fix.001` à exécuter. +- [X] replay opérateur `cargo fmt/check/clippy/test` de `pre.005-fix.001` propre. La décision utilisateur de faire de Mainnet le profil Backfill par défaut et de tester plusieurs familles de transport Backfill (HTTP standard, HTTP provider tel que Helius, Yellowstone gRPC ; WebSocket exclu de V1 sauf besoin démontré) appartient à des couloirs Config/Transport antérieurs. Conformément à `VER-LIFECYCLE-010`, elle n'est pas introduite dans ce fix et doit ouvrir une prerelease dédiée après validation du correctif. + +## `0.3.7-pre.006` — Mainnet par défaut et routage HTTP opérateur + +La décision post-`pre.005` est appliquée dans une nouvelle tranche dédiée : le composite Backfill Desk utilise `mainnet` par défaut, avec Logging `supertrace` pendant le développement et un profil Transport `mainnet_backfill_pool`. Ce profil expose deux endpoints HTTP sans secret obligatoire au démarrage : Solana Public et PublicNode. + +Trois rôles logiques sont proposés : `backfill_pool` pour le pool multi-provider, `backfill_solana_public` et `backfill_publicnode` pour le ciblage explicite. L'IPC expose uniquement le rôle et les labels provider sûrs ; aucune URL ni credential. Le frontend charge `backfill_options()` au démarrage, rend le sélecteur et trace le changement de route. Aucun Start n'est ajouté dans cette tranche. + +Le support Yellowstone gRPC de `ksp-job-backfill-lib`/Backfill Desk est retiré du périmètre `0.3.7` et reporté à une version ultérieure (`0.3.12+` comme cible indicative). WebSocket reste hors Backfill V1. + +### Gate `pre.006` + +- [X] Mainnet est le profil composite par défaut et conserve `supertrace` pendant le développement ; +- [X] le profil `mainnet_backfill_pool` expose Solana Public + PublicNode en HTTP avec rôles pool/ciblés ; +- [X] aucun endpoint gRPC n'est introduit dans ce profil ; +- [X] l'IPC expose uniquement rôle logique et labels provider sûrs ; +- [X] le frontend charge/rend le sélecteur et trace son changement ; +- [X] aucun Start ni dépendance `ksp-job-backfill-lib` n'est ouvert par la Desk ; +- [X] audits statiques Rust/Markdown, JSON Schema et syntaxe TypeScript propres dans l'environnement d'assemblage ; +- [ ] replay opérateur `cargo fmt/check/clippy/test` de `pre.006` à exécuter.