From 02919e959e0e90ea81f1300b1da5619797351269 Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Tue, 8 Sep 2026 17:28:37 +0200 Subject: [PATCH] v0.3.11-pre.010 --- Cargo.toml | 4 +- .../tests/hardening.rs | 277 ++++++++++++++++++ .../tests/release_completeness.rs | 113 +++++++ deltas/0.3.11/pre.010.md | 126 ++++++++ ...ANSACTION_INGEST_WORKER_FOUNDATION_PLAN.md | 6 +- ...AW_TRANSACTION_INGEST_WORKER_FOUNDATION.md | 113 ++++++- 6 files changed, 634 insertions(+), 5 deletions(-) create mode 100644 crates/ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs create mode 100644 crates/ksp-worker-raw-transaction-ingest-lib/tests/release_completeness.rs create mode 100644 deltas/0.3.11/pre.010.md diff --git a/Cargo.toml b/Cargo.toml index 1681d2d..a3b2927 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 512 +# version: 513 [workspace] resolver = "3" members = ["crates/ksp-app-backfill-desk", "crates/ksp-app-config-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.11-pre.9.fix.3" +version = "0.3.11-pre.10" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/crates/ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs b/crates/ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs new file mode 100644 index 0000000..3050841 --- /dev/null +++ b/crates/ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs @@ -0,0 +1,277 @@ +// file: crates/ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs +// version: 1 + +//! External public, security, redaction and release-boundary hardening canaries for `pre.010`. + +fn network(value: &'static str) -> std::option::Option { + let result = ksp_store_lib::RawNetworkId::new(value); + return match result { + std::result::Result::Ok(value) => std::option::Option::Some(value), + std::result::Result::Err(error) => { + assert_eq!(error.code(), ksp_store_lib::ERROR_CODE_RAW_MODEL_INVALID); + std::option::Option::None + }, + }; +} + +fn worker_id(value: &'static str) -> std::option::Option { + let result = ksp_worker_api::WorkerId::new(value); + return match result { + std::result::Result::Ok(value) => std::option::Option::Some(value), + std::result::Result::Err(error) => { + assert_eq!(error.code(), ksp_worker_api::ERROR_CODE_WORKER_ID_INVALID); + std::option::Option::None + }, + }; +} + +#[test] +fn pre_010_external_error_codes_are_stable_unique_and_domain_scoped() { + let codes = [ + (ksp_worker_raw_transaction_ingest_lib::ERROR_CODE_RAW_TRANSACTION_INGEST_CONTENT_CONFLICT, "content_conflict"), + (ksp_worker_raw_transaction_ingest_lib::ERROR_CODE_RAW_TRANSACTION_INGEST_COUNTER_EXHAUSTED, "counter_exhausted"), + (ksp_worker_raw_transaction_ingest_lib::ERROR_CODE_RAW_TRANSACTION_INGEST_DRAIN_TIMEOUT, "drain_timeout"), + (ksp_worker_raw_transaction_ingest_lib::ERROR_CODE_RAW_TRANSACTION_INGEST_RUNTIME_INVALID, "runtime_invalid"), + (ksp_worker_raw_transaction_ingest_lib::ERROR_CODE_RAW_TRANSACTION_INGEST_SETTINGS_INVALID, "settings_invalid"), + (ksp_worker_raw_transaction_ingest_lib::ERROR_CODE_RAW_TRANSACTION_INGEST_SOURCE_FAILED, "source_failed"), + (ksp_worker_raw_transaction_ingest_lib::ERROR_CODE_RAW_TRANSACTION_INGEST_STORE_FAILED, "store_failed"), + ]; + for (index, (code, expected)) in codes.iter().enumerate() { + assert_eq!(code.domain(), "worker_raw_transaction_ingest"); + assert_eq!(code.code(), *expected); + for (other_index, (other, _)) in codes.iter().enumerate() { + if index != other_index { + assert_ne!(code, other); + } + } + } + return; +} + +#[test] +fn pre_010_debug_and_settings_errors_redact_worker_identity_and_invalid_values() { + let raw_worker_id = "raw-ingest-secret-marker-001"; + let network = match network("mainnet") { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let worker_id = match worker_id(raw_worker_id) { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + assert_eq!(std::format!("{worker_id:?}"), "WorkerId(..)"); + let settings = + match ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestSettings::new(network.clone(), worker_id, 1, 1, std::time::Duration::from_millis(100)) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let settings_debug = std::format!("{settings:?}"); + assert!(!settings_debug.contains(raw_worker_id)); + assert!(settings_debug.contains("WorkerId(..)")); + let invalid_worker_id = match worker_id("raw-ingest-secret-marker-002") { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let invalid = + ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestSettings::new(network, invalid_worker_id, 65_537, 1, std::time::Duration::from_millis(100)); + let error = match invalid { + std::result::Result::Ok(_) => return, + std::result::Result::Err(error) => error, + }; + let error_debug = std::format!("{error:?}"); + assert_eq!(error.code(), ksp_worker_raw_transaction_ingest_lib::ERROR_CODE_RAW_TRANSACTION_INGEST_SETTINGS_INVALID); + assert!(!error_debug.contains("raw-ingest-secret-marker-002")); + assert!(!error_debug.contains("65537")); + assert!(error.context().iter().any(|context| return context.key() == "field" && context.value() == "admission_queue_capacity")); + return; +} + +#[test] +fn pre_010_manifest_dependency_surface_remains_exact_source_neutral_and_backend_neutral() { + let manifest = include_str!("../Cargo.toml"); + let mut section = ""; + let mut normal = std::collections::BTreeSet::new(); + let mut dev = std::collections::BTreeSet::new(); + let mut build = std::collections::BTreeSet::new(); + for line in manifest.lines() { + let trimmed = line.trim(); + if trimmed.starts_with('[') && trimmed.ends_with(']') { + section = trimmed; + continue; + } + if trimmed.is_empty() || trimmed.starts_with('#') { + continue; + } + let name = match trimmed.split_once('=') { + std::option::Option::Some((name, _)) => name.trim(), + std::option::Option::None => continue, + }; + if section == "[dependencies]" { + normal.insert(name); + } else if section == "[dev-dependencies]" { + dev.insert(name); + } else if section == "[build-dependencies]" { + build.insert(name); + } + } + assert_eq!( + normal, + std::collections::BTreeSet::from(["ksp-core-lib", "ksp-logging-lib", "ksp-raw-transaction-lib", "ksp-store-lib", "ksp-worker-api", "sha2", "tokio",]) + ); + assert!(dev.is_empty()); + assert!(build.is_empty()); + assert!(manifest.contains("ksp-store-lib = { path = \"../ksp-store-lib\", default-features = false }")); + assert!(manifest.contains("tokio = { workspace = true, features = [\"macros\", \"rt\", \"sync\", \"time\"] }")); + for forbidden in [ + "ksp-config-lib", + "ksp-job-api", + "ksp-job-backfill-lib", + "ksp-onchain-transport-lib", + "ksp-store-api", + "ksp-store-postgres-lib", + "reqwest", + "tokio-tungstenite", + "tonic", + "yellowstone-grpc-proto", + ] { + assert!(!manifest.contains(forbidden), "forbidden Worker manifest dependency present: {forbidden}"); + } + return; +} + +#[test] +fn pre_010_source_visibility_contract_uses_crate_root_for_shared_items() { + let source_contracts: [(&str, &[&str]); 4] = [ + (include_str!("../src/settings.rs"), &["RawTransactionIngestSettings"]), + (include_str!("../src/runtime.rs"), &["RawTransactionIngestHandle", "RawTransactionIngestWorker"]), + (include_str!("../src/snapshot.rs"), &["RawTransactionIngestSnapshot", "RawTransactionIngestSnapshotSource"]), + (include_str!("../src/persistence.rs"), &["RawTransactionIngestPersistenceOutcome", "RawTransactionIngestPersistencePort"]), + ]; + for (source, symbols) in source_contracts { + for symbol in symbols { + let required = std::format!("impl crate::{symbol}"); + assert!(source.contains(required.as_str()), "shared item must use crate-root impl path: {symbol}"); + let forbidden = std::format!("impl {symbol}"); + assert!(!source.contains(forbidden.as_str()), "shared item must not use bare impl path: {symbol}"); + } + } + for (module, source) in [ + ("admission", include_str!("../src/admission.rs")), + ("error", include_str!("../src/error.rs")), + ("identity", include_str!("../src/identity.rs")), + ("persistence", include_str!("../src/persistence.rs")), + ("runtime", include_str!("../src/runtime.rs")), + ("settings", include_str!("../src/settings.rs")), + ("snapshot", include_str!("../src/snapshot.rs")), + ] { + let forbidden = std::format!("crate::{module}::"); + assert!(!source.contains(forbidden.as_str()), "internal module path bypasses crate-root façade: {forbidden}"); + } + return; +} + +#[test] +fn pre_010_production_surface_has_no_historical_backfill_or_retriever_contract() { + let sources = [ + include_str!("../src/admission.rs"), + include_str!("../src/error.rs"), + include_str!("../src/identity.rs"), + include_str!("../src/lib.rs"), + include_str!("../src/persistence.rs"), + include_str!("../src/runtime.rs"), + include_str!("../src/settings.rs"), + include_str!("../src/snapshot.rs"), + ]; + for source in sources { + for forbidden in [ + "ksp-worker-raw-retriever", + "raw_transaction_retriever", + "RawTransactionRetriever", + "Backfill", + "backfill", + "Checkpoint", + "checkpoint", + "Discovery", + "discovery", + "Hydration", + "hydration", + "historical", + ] { + assert!(!source.contains(forbidden), "historical/retriever surface leaked into Worker production source: {forbidden}"); + } + } + return; +} + +#[test] +fn pre_010_production_sources_scan_clean_for_config_secrets_backend_and_transport() { + let sources = [ + include_str!("../src/admission.rs"), + include_str!("../src/error.rs"), + include_str!("../src/identity.rs"), + include_str!("../src/lib.rs"), + include_str!("../src/persistence.rs"), + include_str!("../src/runtime.rs"), + include_str!("../src/settings.rs"), + include_str!("../src/snapshot.rs"), + ]; + for source in sources { + let lower = source.to_ascii_lowercase(); + for forbidden in ["api_key", "api-key", "authorization", "bearer ", "password", "credential", "secret"] { + assert!(!lower.contains(forbidden), "secret-like material leaked into Worker production source: {forbidden}"); + } + for forbidden in [ + "ksp_config_lib::", + "ksp_store_postgres_lib::", + "ksp_onchain_transport_lib::", + "ksp_offchain_transport_lib::", + "reqwest::", + "tokio_tungstenite::", + "tonic::", + "yellowstone_grpc_proto::", + "postgresql://", + "postgres://", + ] { + assert!(!source.contains(forbidden), "Config/backend/Transport implementation leaked into Worker production source: {forbidden}"); + } + } + return; +} + +#[test] +fn pre_010_lower_layers_have_no_dependency_return_to_concrete_worker() { + for manifest in [ + include_str!("../../ksp-core-lib/Cargo.toml"), + include_str!("../../ksp-logging-lib/Cargo.toml"), + include_str!("../../ksp-raw-transaction-lib/Cargo.toml"), + include_str!("../../ksp-store-api/Cargo.toml"), + include_str!("../../ksp-store-lib/Cargo.toml"), + include_str!("../../ksp-store-postgres-lib/Cargo.toml"), + include_str!("../../ksp-worker-api/Cargo.toml"), + ] { + assert!(!manifest.contains("ksp-worker-raw-transaction-ingest-lib")); + } + return; +} + +#[test] +fn pre_010_public_root_exposes_no_runtime_backend_or_live_source_implementation_types() { + let root = include_str!("../src/lib.rs"); + for forbidden in [ + "pub mod ", + "tokio::", + "JoinSet", + "watch::Receiver", + "watch::Sender", + "mpsc::Sender", + "ksp_store_postgres_lib::", + "ksp_onchain_transport_lib::", + "ksp_config_lib::", + "reqwest::", + "tonic::", + ] { + assert!(!root.contains(forbidden), "implementation/backend/live-source detail leaked into public root: {forbidden}"); + } + return; +} diff --git a/crates/ksp-worker-raw-transaction-ingest-lib/tests/release_completeness.rs b/crates/ksp-worker-raw-transaction-ingest-lib/tests/release_completeness.rs new file mode 100644 index 0000000..f9e622f --- /dev/null +++ b/crates/ksp-worker-raw-transaction-ingest-lib/tests/release_completeness.rs @@ -0,0 +1,113 @@ +// file: crates/ksp-worker-raw-transaction-ingest-lib/tests/release_completeness.rs +// version: 1 + +//! Release-completeness canaries through the `pre.010` public/release/security hardening tranche. + +#[test] +fn pre_010_production_module_inventory_is_exact() -> std::io::Result<()> { + let source_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src"); + let entries = match std::fs::read_dir(source_root) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut names = std::vec::Vec::new(); + for entry in entries { + let entry = match entry { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let file_type = match entry.file_type() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if !file_type.is_file() { + continue; + } + let name = match entry.file_name().into_string() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => continue, + }; + if name.ends_with(".rs") { + names.push(name); + } + } + names.sort_unstable(); + assert_eq!(names, std::vec!["admission.rs", "error.rs", "identity.rs", "lib.rs", "persistence.rs", "runtime.rs", "settings.rs", "snapshot.rs",]); + return std::result::Result::Ok(()); +} + +#[test] +fn pre_010_public_root_export_inventory_is_exact() { + let root = include_str!("../src/lib.rs"); + let mut exports = std::vec::Vec::new(); + for line in root.lines() { + let line = line.trim(); + if !line.starts_with("pub use ") { + continue; + } + let path = line.trim_start_matches("pub use ").trim_end_matches(';'); + let name = match path.rsplit("::").next() { + std::option::Option::Some(value) => value, + std::option::Option::None => continue, + }; + exports.push(name); + } + exports.sort_unstable(); + assert_eq!( + exports, + std::vec![ + "DEFAULT_RAW_TRANSACTION_INGEST_ADMISSION_QUEUE_CAPACITY", + "DEFAULT_RAW_TRANSACTION_INGEST_PERSISTENCE_CONCURRENCY", + "DEFAULT_RAW_TRANSACTION_INGEST_SHUTDOWN_DRAIN_TIMEOUT", + "ERROR_CODE_RAW_TRANSACTION_INGEST_CONTENT_CONFLICT", + "ERROR_CODE_RAW_TRANSACTION_INGEST_COUNTER_EXHAUSTED", + "ERROR_CODE_RAW_TRANSACTION_INGEST_DRAIN_TIMEOUT", + "ERROR_CODE_RAW_TRANSACTION_INGEST_RUNTIME_INVALID", + "ERROR_CODE_RAW_TRANSACTION_INGEST_SETTINGS_INVALID", + "ERROR_CODE_RAW_TRANSACTION_INGEST_SOURCE_FAILED", + "ERROR_CODE_RAW_TRANSACTION_INGEST_STORE_FAILED", + "MAX_RAW_TRANSACTION_INGEST_ADMISSION_QUEUE_CAPACITY", + "MAX_RAW_TRANSACTION_INGEST_PERSISTENCE_CONCURRENCY", + "MAX_RAW_TRANSACTION_INGEST_SHUTDOWN_DRAIN_TIMEOUT", + "MIN_RAW_TRANSACTION_INGEST_ADMISSION_QUEUE_CAPACITY", + "MIN_RAW_TRANSACTION_INGEST_PERSISTENCE_CONCURRENCY", + "MIN_RAW_TRANSACTION_INGEST_SHUTDOWN_DRAIN_TIMEOUT", + "RAW_TRANSACTION_INGEST_WORKER_KIND_CODE", + "RawTransactionIngestHandle", + "RawTransactionIngestSettings", + "RawTransactionIngestSnapshot", + "RawTransactionIngestSnapshotFuture", + "RawTransactionIngestSnapshotSource", + "RawTransactionIngestTerminalFuture", + "RawTransactionIngestWorker", + ] + ); + assert!(!root.contains("pub mod ")); + return; +} + +#[test] +fn pre_010_external_hardening_suite_is_present_and_scoped() { + let hardening = include_str!("hardening.rs"); + for required in [ + "pre_010_external_error_codes_are_stable_unique_and_domain_scoped", + "pre_010_debug_and_settings_errors_redact_worker_identity_and_invalid_values", + "pre_010_manifest_dependency_surface_remains_exact_source_neutral_and_backend_neutral", + "pre_010_source_visibility_contract_uses_crate_root_for_shared_items", + "pre_010_production_surface_has_no_historical_backfill_or_retriever_contract", + "pre_010_production_sources_scan_clean_for_config_secrets_backend_and_transport", + "pre_010_lower_layers_have_no_dependency_return_to_concrete_worker", + "pre_010_public_root_exposes_no_runtime_backend_or_live_source_implementation_types", + ] { + assert!(hardening.contains(required), "required pre.010 hardening canary missing: {required}"); + } + let dependency_boundary = include_str!("dependency_boundary.rs"); + assert!(dependency_boundary.contains("pre_002_manifest_dependency_surface_is_exact")); + assert!(dependency_boundary.contains("pre_009_source_surface_hardens_shutdown_and_faults_without_backend_or_live_source")); + let public_api = include_str!("public_api.rs"); + assert!(public_api.contains("pre_003_kind_code_and_settings_are_consumable_from_crate_root")); + assert!(public_api.contains("pre_004_start_handle_and_terminal_future_are_consumable_without_public_join_handle")); + assert!(public_api.contains("pre_008_snapshot_surface_and_common_projection_are_public_and_stable")); + assert!(public_api.contains("pre_009_source_and_drain_timeout_error_codes_are_public_and_stable")); + return; +} diff --git a/deltas/0.3.11/pre.010.md b/deltas/0.3.11/pre.010.md new file mode 100644 index 0000000..f87efc2 --- /dev/null +++ b/deltas/0.3.11/pre.010.md @@ -0,0 +1,126 @@ + + + +# Delta `0.3.11-pre.010` — hardening public, release et sécurité + +## Base requise + +```text +0.3.11-pre.009-fix.003 +workspace.package.version = 0.3.11-pre.9.fix.3 +``` + +Le gate opérateur du 8 septembre 2026 est vert sur `fmt`, audits Rust/Markdown, `cargo check --workspace`, Clippy strict, 45 tests de la crate Worker et les doc-tests. Aucune dépendance/feature n'ayant changé, aucun `cargo tree` intermédiaire n'était requis. + +## Objectif + +Fermer uniquement le hardening externe `pre.010` du plan `032` : surface publique crate-root exacte, dépendances exactes, absence de surface historique, redaction, codes d'erreur, inventaire des modules et scans Config/secret/backend/Transport. Aucun comportement runtime n'est ajouté. + +## Version + +```text +identifiant de livraison : 0.3.11-pre.010 +workspace.package.version : 0.3.11-pre.10 +``` + +## Hardening externe + +Nouvelle suite `tests/hardening.rs` : + +```text +7 ErrorCode publics uniques et domain-scoped +WorkerId redacted dans Debug +settings invalides sans echo de valeur ni WorkerId +manifest productif exact +aucun dev/build dependency +crate::Item pour les impl partagées +aucune surface Backfill/historical/checkpoint/discovery/hydration/retriever +aucun Config productif +aucun secret-like material productif +aucun backend Store direct +aucun Transport productif +aucune dépendance retour depuis les couches basses +aucun type runtime/backend/live-source exposé par la crate-root +``` + +## Release completeness + +Nouvelle suite `tests/release_completeness.rs` : + +```text +8 modules de production exacts +24 exports publics crate-root exacts +aucun pub mod +présence des canaries dependency/public/hardening requises +``` + +Le corpus attendu après cette tranche est : + +```text +35 unit tests +3 dependency-boundary tests +7 public API tests +8 hardening tests +3 release-completeness tests +56 tests Worker au total +``` + +## Fichiers ajoutés + +```text +crates/ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs +crates/ksp-worker-raw-transaction-ingest-lib/tests/release_completeness.rs +deltas/0.3.11/pre.010.md +``` + +## Fichiers modifiés + +```text +Cargo.toml +docs/plans/032-V0_3_11_RAW_TRANSACTION_INGEST_WORKER_FOUNDATION_PLAN.md +docs/validation/028-V0_3_11_RAW_TRANSACTION_INGEST_WORKER_FOUNDATION.md +``` + +## Fichiers supprimés + +Aucun. + +## Code de production + +Aucun fichier `crates/ksp-worker-raw-transaction-ingest-lib/src/*.rs` n'est modifié. + +## Dépendances + +Aucune dépendance ni feature n'est ajoutée, supprimée ou modifiée. Le `Cargo.toml` de la crate Worker reste identique à la base. + +## Validations exécutées dans l'environnement d'assemblage + +```text +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 +scans statiques API/dependency/historical/Config/secret/backend/Transport +comparaison byte-for-byte des sources productives et du manifest Worker +``` + +L'environnement d'assemblage ne fournit ni `cargo`, ni `rustc`, ni `rustfmt`. Aucun gate Cargo de `pre.010` n'est déclaré PASS localement. + +## Gate opérateur demandé + +Aucune dépendance ni feature n'ayant changé, aucun `cargo tree` intermédiaire n'est requis : + +```bash +cargo fmt --all +python3 scripts/audit_rust_workspace_rules.py +python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas +cargo check --workspace +cargo clippy --workspace --all-targets --all-features -- -D warnings +cargo test -p ksp-worker-raw-transaction-ingest-lib +``` + +## Décision + +`pre.011` reste bloquée jusqu'à validation opérateur verte des canaries public/release/security. + +## Questions ouvertes + +Aucune nouvelle question architecturale. Les sources live restent hors scope de `0.3.11`. diff --git a/docs/plans/032-V0_3_11_RAW_TRANSACTION_INGEST_WORKER_FOUNDATION_PLAN.md b/docs/plans/032-V0_3_11_RAW_TRANSACTION_INGEST_WORKER_FOUNDATION_PLAN.md index 1ccb8bc..c9720dd 100644 --- a/docs/plans/032-V0_3_11_RAW_TRANSACTION_INGEST_WORKER_FOUNDATION_PLAN.md +++ b/docs/plans/032-V0_3_11_RAW_TRANSACTION_INGEST_WORKER_FOUNDATION_PLAN.md @@ -1,5 +1,5 @@ - + # Plan v0.3.11 — fondation runtime du Worker RawTransaction ingest @@ -744,12 +744,14 @@ Budget cible : **15–20 min**. `watch` latest-value, compteurs checked, common Budget cible : **15–20 min**. Drain deadline, stop/fault ordering, saturation, source failure, Store slow/failure, abort+join au timeout, no orphan tasks. Scinder immédiatement si le gate réel dépasse le budget. -État après matérialisation : **implémenté, gate opérateur requis**. Les tâches source privées retournent désormais `ksp_core_lib::Result<()>` dans le `JoinSet` du supervisor ; une erreur source ou un `JoinError` devient `worker_raw_transaction_ingest.source_failed` et incrémente `source_failure_total` sans exposer de texte provider. Le shutdown possède une deadline stricte `shutdown_drain_timeout` ; au timeout, admission, persistences et sources sont fermées/abortées puis toutes rejointes avant `Faulted(worker_raw_transaction_ingest.drain_timeout)`. Le stop reste prioritaire dans le `select!`, mais un Store fault découvert pendant le drain n'est pas masqué par un stop antérieur ; un fault terminal déjà décidé n'est pas remplacé par un stop tardif. La saturation est instrumentée de façon source-neutral au dequeue d'une queue `mpsc` pleine via `backpressure_wait_total`, sans drop ni queue non bornée. Les tests déterministes couvrent source failure, stop/Store fault, saturation et timeout avec absence de tâches orphelines. Aucune source live, aucun Transport, aucun backend direct et aucune nouvelle dépendance/feature ne sont introduits. +État après matérialisation : **implémenté et gate opérateur validé après `pre.009-fix.003`**. Les tâches source privées retournent désormais `ksp_core_lib::Result<()>` dans le `JoinSet` du supervisor ; une erreur source ou un `JoinError` devient `worker_raw_transaction_ingest.source_failed` et incrémente `source_failure_total` sans exposer de texte provider. Le shutdown possède une deadline stricte `shutdown_drain_timeout` ; au timeout, admission, persistences et sources sont fermées/abortées puis toutes rejointes avant `Faulted(worker_raw_transaction_ingest.drain_timeout)`. Le stop reste prioritaire dans le `select!`, mais un Store fault découvert pendant le drain n'est pas masqué par un stop antérieur ; un fault terminal déjà décidé n'est pas remplacé par un stop tardif. La saturation est instrumentée de façon source-neutral au dequeue d'une queue `mpsc` pleine via `backpressure_wait_total`, sans drop ni queue non bornée. Les correctifs `pre.009-fix.001` à `.003` ont uniquement réparé les nouveaux tests (signature de `receive`, annotation du `JoinSet`, `store_guard` et `implicit_return`) sans modifier le code de production ni les dépendances. Le gate communiqué le 8 septembre 2026 est vert sur `fmt`, audits, `check`, Clippy strict, 45 tests de crate et doc-tests. Aucun arbre Cargo n'a été requis car aucune dépendance/feature n'avait changé. ### `pre.010` — hardening public/release/security Budget cible : **10–15 min**. Tests externes exacts : API root, dependencies, historical-surface absence, redaction, error codes, module inventory et scans Config/secret/backend. +État après matérialisation : **implémenté, gate opérateur requis**. Deux suites externes nouvelles ferment la tranche sans modifier le runtime : `hardening.rs` vérifie les sept codes d'erreur publics, la redaction du `WorkerId` et des valeurs invalides, la surface de dépendances exacte, l'usage `crate::Item`, l'absence de surface historique/Backfill/retriever, les scans Config/secret/backend/Transport et l'absence de dépendance retour depuis les couches basses ; `release_completeness.rs` verrouille l'inventaire exact des huit modules de production, les vingt-quatre exports publics crate-root et la présence des canaries externes attendues. Aucun fichier `src/`, aucune dépendance et aucune feature ne changent. + ### `pre.011` — gate technique final Budget cible : **10–15 min**. Workspace tests/all-features, Clippy strict, suites ciblées, arbres normal/features, duplicate tree. Aucun nouveau scope fonctionnel. diff --git a/docs/validation/028-V0_3_11_RAW_TRANSACTION_INGEST_WORKER_FOUNDATION.md b/docs/validation/028-V0_3_11_RAW_TRANSACTION_INGEST_WORKER_FOUNDATION.md index 78c6f21..a35fa3e 100644 --- a/docs/validation/028-V0_3_11_RAW_TRANSACTION_INGEST_WORKER_FOUNDATION.md +++ b/docs/validation/028-V0_3_11_RAW_TRANSACTION_INGEST_WORKER_FOUNDATION.md @@ -1,5 +1,5 @@ - + # Validation v0.3.11 — fondation runtime du Worker RawTransaction ingest @@ -1404,3 +1404,114 @@ cargo test -p ksp-worker-raw-transaction-ingest-lib ``` Critère de passage : races stop/fault déterministes, timeout borné sans tâche orpheline, saturation observable sans drop et frontières de dépendances inchangées. + +## 21. `pre.010` — hardening public/release/security + +### 21.1 Clôture opérateur de `pre.009-fix.003` + +Le gate communiqué le 8 septembre 2026 est vert : + +```text +General Rust rule audit: clean +Rust export completeness audit: 0 candidate(s) +KSP workspace Rust rule audit: clean +Markdown table audit: clean (340 table(s), 789 file(s)) +cargo check --workspace: PASS +cargo clippy --workspace --all-targets --all-features -- -D warnings: PASS +35 unit tests: PASS +3 dependency-boundary tests: PASS +7 public API tests: PASS +doc-tests: PASS +``` + +Les trois fixes `pre.009-fix.001` à `.003` ont uniquement corrigé les nouveaux tests ; aucun code de production, edge de dépendance ou feature n'a changé. Aucun `cargo tree` intermédiaire n'était requis. + +### 21.2 Surface publique exacte + +`pre.010` n'ajoute aucun symbole productif. La suite `release_completeness.rs` verrouille exactement les vingt-quatre exports publics crate-root déjà ouverts par `pre.003` à `pre.009` et interdit `pub mod`. + +Inventaire productif exact verrouillé : + +```text +admission.rs +error.rs +identity.rs +lib.rs +persistence.rs +runtime.rs +settings.rs +snapshot.rs +``` + +Aucun nouveau module de production n'est introduit. + +### 21.3 Hardening externe et redaction + +La suite `hardening.rs` ajoute des preuves externes pour : + +```text +7 ErrorCode publics uniques et domain-scoped +WorkerId redacted dans Debug +settings invalides sans echo de valeur ni WorkerId +manifest exact et backend-neutral +crate::Item pour les impl partagées +aucune surface historical/Backfill/checkpoint/discovery/hydration/retriever +aucun Config productif +aucun secret-like material productif +aucun backend Store direct +aucun Transport productif +aucune dépendance retour depuis les couches basses vers le Worker concret +aucun type Tokio/backend/live-source exposé par la crate-root +``` + +Les scans secret ciblent uniquement des marqueurs à valeur de credential (`api_key`, `authorization`, `bearer`, `password`, `credential`, `secret`) et ne confondent pas les tokens de contrôle runtime avec des secrets. + +### 21.4 Dépendances + +Le manifest productif reste exactement : + +```text +ksp-core-lib +ksp-logging-lib +ksp-raw-transaction-lib +ksp-store-lib (default-features = false) +ksp-worker-api +sha2 +tokio (macros, rt, sync, time) +``` + +Aucun `[dev-dependencies]` ni `[build-dependencies]` n'est ajouté. Aucun arbre Cargo intermédiaire n'est requis puisque le manifest de la crate Worker est inchangé. + +### 21.5 Frontière volontaire de `pre.010` + +La tranche reste purement tests/release/security et n'introduit aucun : + +```text +source live +Transport +Config +backend Store direct +nouvelle API publique +nouveau runtime behavior +nouvelle dépendance/feature +réconciliation README/USAGE +publication metadata +``` + +`pre.011` reste propriétaire du gate technique final workspace et des arbres Cargo. + +### 21.6 Gate opérateur demandé pour `pre.010` + +Aucune dépendance ni feature n'est modifiée ; le gate ciblé reste : + +```bash +cargo fmt --all +python3 scripts/audit_rust_workspace_rules.py +python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas +cargo check --workspace +cargo clippy --workspace --all-targets --all-features -- -D warnings +cargo test -p ksp-worker-raw-transaction-ingest-lib +``` + +Critère de passage : toutes les canaries public/release/security sont vertes sans modification de la surface productive. +