v0.2.6-pre.013
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
# file: Cargo.toml
|
# file: Cargo.toml
|
||||||
# version: 182
|
# version: 183
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = ["crates/ksp-app-config-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-logging-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-wallet-lib"]
|
members = ["crates/ksp-app-config-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-logging-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-wallet-lib"]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.2.6-pre.12"
|
version = "0.2.6-pre.13"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"
|
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# file: crates/ksp-app-wallet-desk/Cargo.toml
|
# file: crates/ksp-app-wallet-desk/Cargo.toml
|
||||||
# version: 4
|
# version: 5
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "ksp-app-wallet-desk"
|
name = "ksp-app-wallet-desk"
|
||||||
@@ -33,7 +33,7 @@ serde = { workspace = true, features = ["derive"] }
|
|||||||
tauri.workspace = true
|
tauri.workspace = true
|
||||||
tauri-plugin-dialog.workspace = true
|
tauri-plugin-dialog.workspace = true
|
||||||
tauri-plugin-tracing.workspace = true
|
tauri-plugin-tracing.workspace = true
|
||||||
tokio = { workspace = true, features = ["fs", "io-util", "rt", "time"] }
|
tokio = { workspace = true, features = ["fs", "io-util", "macros", "rt", "time"] }
|
||||||
ts-rs.workspace = true
|
ts-rs.workspace = true
|
||||||
zeroize.workspace = true
|
zeroize.workspace = true
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
<h1 id="viewTitle" class="h3 mb-1">Dashboard</h1>
|
<h1 id="viewTitle" class="h3 mb-1">Dashboard</h1>
|
||||||
<p class="text-body-secondary mb-0">Identité autorisée et balance SOL via le Transport HTTP composite sont actives.</p>
|
<p class="text-body-secondary mb-0">Identité autorisée et balance SOL via le Transport HTTP composite sont actives.</p>
|
||||||
</div>
|
</div>
|
||||||
<span class="badge text-bg-secondary">0.2.6-pre.012</span>
|
<span class="badge text-bg-secondary">0.2.6-pre.013</span>
|
||||||
</div>
|
</div>
|
||||||
<section data-view-panel="dashboard">
|
<section data-view-panel="dashboard">
|
||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
@@ -480,7 +480,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<footer class="app-footer bg-dark text-white-50 d-flex align-items-center px-4">
|
<footer class="app-footer bg-dark text-white-50 d-flex align-items-center px-4">
|
||||||
<span>KSP Wallet Desk · OWNER keypair export pre.012</span>
|
<span>KSP Wallet Desk · integration / compliance pre.013</span>
|
||||||
</footer>
|
</footer>
|
||||||
<script type="module" src="./ts/main.ts"></script>
|
<script type="module" src="./ts/main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-app-wallet-desk/frontend/ts/main.ts
|
// file: crates/ksp-app-wallet-desk/frontend/ts/main.ts
|
||||||
// version: 13
|
// version: 14
|
||||||
|
|
||||||
import { Modal } from "bootstrap";
|
import { Modal } from "bootstrap";
|
||||||
import DataTable from "datatables.net-bs5";
|
import DataTable from "datatables.net-bs5";
|
||||||
@@ -190,7 +190,7 @@ function initializeWalletTable(): void {
|
|||||||
zeroRecords: "Aucun wallet correspondant.",
|
zeroRecords: "Aucun wallet correspondant.",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
frontendDebug("main", "Wallet inventory DataTable initialized", { phase: "pre.012-owner-transfer-export" });
|
frontendDebug("main", "Wallet inventory DataTable initialized", { phase: "pre.013-integration-compliance" });
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderWalletInventory(entries: WalletInventoryEntryDto[]): void {
|
function renderWalletInventory(entries: WalletInventoryEntryDto[]): void {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "ksp-app-wallet-desk",
|
"name": "ksp-app-wallet-desk",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.2.6-pre.12",
|
"version": "0.2.6-pre.13",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-app-wallet-desk/src/app_state.rs
|
// file: crates/ksp-app-wallet-desk/src/app_state.rs
|
||||||
// version: 16
|
// version: 17
|
||||||
|
|
||||||
//! Shared backend state owned by the Wallet Desk Tauri application.
|
//! Shared backend state owned by the Wallet Desk Tauri application.
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ impl AppState {
|
|||||||
effective_wallets_directory_created_on_startup: self.wallet_config_startup.effective_directory_created_on_startup(),
|
effective_wallets_directory_created_on_startup: self.wallet_config_startup.effective_directory_created_on_startup(),
|
||||||
fallback_logging_active: runtime.fallback_active,
|
fallback_logging_active: runtime.fallback_active,
|
||||||
root_wallets_directory_created_on_startup: self.wallet_config_startup.root_directory_created_on_startup(),
|
root_wallets_directory_created_on_startup: self.wallet_config_startup.root_directory_created_on_startup(),
|
||||||
shell_phase: "pre.012-owner-transfer-export".to_owned(),
|
shell_phase: "pre.013-integration-compliance".to_owned(),
|
||||||
startup_diagnostic: runtime.startup_diagnostic.clone(),
|
startup_diagnostic: runtime.startup_diagnostic.clone(),
|
||||||
transport_available_endpoint_count,
|
transport_available_endpoint_count,
|
||||||
transport_clusters: self.transport_runtime.clusters(),
|
transport_clusters: self.transport_runtime.clusters(),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "KSP Wallet Desk",
|
"productName": "KSP Wallet Desk",
|
||||||
"version": "0.2.6-pre.12",
|
"version": "0.2.6-pre.13",
|
||||||
"identifier": "com.sasedev.ksp-app-wallet-desk",
|
"identifier": "com.sasedev.ksp-app-wallet-desk",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "npm run dev",
|
"beforeDevCommand": "npm run dev",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-app-wallet-desk/tests/desktop_contract.rs
|
// file: crates/ksp-app-wallet-desk/tests/desktop_contract.rs
|
||||||
// version: 13
|
// version: 14
|
||||||
|
|
||||||
//! Desktop build, shell and Config-status contract audits for Wallet Desk.
|
//! Desktop build, shell and Config-status contract audits for Wallet Desk.
|
||||||
|
|
||||||
@@ -329,5 +329,21 @@ fn pre_012_owner_export_uses_rust_native_save_picker_and_wallet_file_adapter() {
|
|||||||
assert!(html.contains("id=\"exportWalletKeypairModal\""));
|
assert!(html.contains("id=\"exportWalletKeypairModal\""));
|
||||||
assert!(main.contains("bindWalletExportActions"));
|
assert!(main.contains("bindWalletExportActions"));
|
||||||
assert!(main.contains("export_wallet_transfer"));
|
assert!(main.contains("export_wallet_transfer"));
|
||||||
assert!(main.contains("pre.012-owner-transfer-export"));
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pre_013_integration_compliance_and_devnet_smoke_are_durable_release_gates() {
|
||||||
|
let root = app_root();
|
||||||
|
let state = read_text(root.join("src/app_state.rs").as_path());
|
||||||
|
let main = read_text(root.join("frontend/ts/main.ts").as_path());
|
||||||
|
let compliance = read_text(root.join("tests/release_compliance.rs").as_path());
|
||||||
|
let smoke = read_text(root.join("tests/wallet_desk_devnet_smoke.rs").as_path());
|
||||||
|
let validation = read_text(root.join("../../docs/validation/009-V0_2_6_WALLET_DESK_COMPLIANCE.md").as_path());
|
||||||
|
assert!(state.contains("pre.013-integration-compliance"));
|
||||||
|
assert!(main.contains("pre.013-integration-compliance"));
|
||||||
|
assert!(compliance.contains("wallet_desk_dependency_firewall_and_tauri_capabilities_are_minimal"));
|
||||||
|
assert!(compliance.contains("frontend_has_no_direct_filesystem_network_or_secret_persistence_surface"));
|
||||||
|
assert!(smoke.contains("committed_wallet_desk_composite_creates_wallet_and_reaches_devnet_balance"));
|
||||||
|
assert!(smoke.contains("#[ignore"));
|
||||||
|
assert!(validation.contains("Config -> Wallet -> Transport -> Devnet getBalance"));
|
||||||
}
|
}
|
||||||
|
|||||||
181
crates/ksp-app-wallet-desk/tests/release_compliance.rs
Normal file
181
crates/ksp-app-wallet-desk/tests/release_compliance.rs
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
// file: crates/ksp-app-wallet-desk/tests/release_compliance.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
//! Release-wide deterministic compliance canaries for Wallet Desk.
|
||||||
|
|
||||||
|
fn app_root() -> std::path::PathBuf {
|
||||||
|
return std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_text(path: &std::path::Path) -> String {
|
||||||
|
let source = std::fs::read_to_string(path);
|
||||||
|
assert!(source.is_ok(), "unable to read {}", path.display());
|
||||||
|
return match source {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => String::new(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_rust_source_tree(root: &std::path::Path) -> String {
|
||||||
|
let entries = std::fs::read_dir(root);
|
||||||
|
assert!(entries.is_ok(), "unable to enumerate {}", root.display());
|
||||||
|
let entries = match entries {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => return String::new(),
|
||||||
|
};
|
||||||
|
let mut paths = std::vec::Vec::new();
|
||||||
|
for entry in entries {
|
||||||
|
let entry = match entry {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => continue,
|
||||||
|
};
|
||||||
|
let path = entry.path();
|
||||||
|
if path.extension().and_then(std::ffi::OsStr::to_str) == std::option::Option::Some("rs") {
|
||||||
|
paths.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
paths.sort();
|
||||||
|
let mut combined = String::new();
|
||||||
|
for path in paths {
|
||||||
|
combined.push_str(read_text(path.as_path()).as_str());
|
||||||
|
combined.push('\n');
|
||||||
|
}
|
||||||
|
return combined;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn struct_field_block<'a>(source: &'a str, name: &str) -> &'a str {
|
||||||
|
let marker = std::format!("struct {name}");
|
||||||
|
let start = source.find(marker.as_str());
|
||||||
|
assert!(start.is_some(), "missing struct {name}");
|
||||||
|
let start = match start {
|
||||||
|
std::option::Option::Some(value) => value,
|
||||||
|
std::option::Option::None => return "",
|
||||||
|
};
|
||||||
|
let tail = &source[start..];
|
||||||
|
return tail.split("}\n").next().unwrap_or_default();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn backend_keeps_config_wallet_transport_and_logging_ownership_boundaries() {
|
||||||
|
let root = app_root();
|
||||||
|
let source = read_rust_source_tree(root.join("src").as_path());
|
||||||
|
for forbidden in [
|
||||||
|
"std::env::var(",
|
||||||
|
"std::env::var_os(",
|
||||||
|
"std::env::vars(",
|
||||||
|
"reqwest::",
|
||||||
|
"solana_keypair::",
|
||||||
|
"solana_pubkey::",
|
||||||
|
"solana_signer::",
|
||||||
|
"solana_signature::",
|
||||||
|
] {
|
||||||
|
assert!(!source.contains(forbidden), "Wallet Desk backend bypasses an owned boundary through {forbidden}");
|
||||||
|
}
|
||||||
|
for line in source.lines() {
|
||||||
|
let trimmed = line.trim_start();
|
||||||
|
assert!(!trimmed.starts_with("use tracing::"), "Wallet Desk backend must not import tracing directly: {trimmed}");
|
||||||
|
assert!(!trimmed.starts_with("tracing::"), "Wallet Desk backend must not emit through tracing directly: {trimmed}");
|
||||||
|
}
|
||||||
|
assert!(source.contains("ksp_config_lib::ConfigEnvironment::load"));
|
||||||
|
assert!(source.contains("ksp_wallet_lib::"));
|
||||||
|
assert!(source.contains("ksp_onchain_transport_lib::"));
|
||||||
|
assert!(source.contains("ksp_logging_lib::"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn frontend_has_no_direct_filesystem_network_or_secret_persistence_surface() {
|
||||||
|
let root = app_root();
|
||||||
|
let package = read_text(root.join("package.json").as_path());
|
||||||
|
let main = read_text(root.join("frontend/ts/main.ts").as_path());
|
||||||
|
let invoke = read_text(root.join("frontend/ts/invoke.ts").as_path());
|
||||||
|
let frontend_log = read_text(root.join("frontend/ts/frontend_log.ts").as_path());
|
||||||
|
let splash = read_text(root.join("frontend/ts/splash.ts").as_path());
|
||||||
|
let html = read_text(root.join("frontend/main.html").as_path());
|
||||||
|
let combined = std::format!("{main}\n{invoke}\n{frontend_log}\n{splash}\n{html}");
|
||||||
|
for forbidden in [
|
||||||
|
"@tauri-apps/plugin-dialog",
|
||||||
|
"@tauri-apps/plugin-fs",
|
||||||
|
"navigator.clipboard",
|
||||||
|
"localStorage",
|
||||||
|
"sessionStorage",
|
||||||
|
"window.alert",
|
||||||
|
"window.confirm",
|
||||||
|
"window.prompt",
|
||||||
|
"XMLHttpRequest",
|
||||||
|
"new WebSocket",
|
||||||
|
"EventSource",
|
||||||
|
"fetch(",
|
||||||
|
"KSP_SECRET_WALLET_PASS_",
|
||||||
|
"sourcePath",
|
||||||
|
"source_path",
|
||||||
|
"destinationPath",
|
||||||
|
"destination_path",
|
||||||
|
] {
|
||||||
|
assert!(!combined.contains(forbidden), "frontend contains forbidden direct surface {forbidden}");
|
||||||
|
}
|
||||||
|
assert!(!package.contains("@tauri-apps/plugin-dialog"));
|
||||||
|
assert!(!package.contains("@tauri-apps/plugin-fs"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn response_dtos_keep_secret_key_material_out_of_ipc() {
|
||||||
|
let root = app_root();
|
||||||
|
let session = read_text(root.join("src/wallet_session.rs").as_path());
|
||||||
|
let balance = read_text(root.join("src/wallet_balance.rs").as_path());
|
||||||
|
let import = read_text(root.join("src/wallet_import.rs").as_path());
|
||||||
|
let export = read_text(root.join("src/wallet_export.rs").as_path());
|
||||||
|
let security = read_text(root.join("src/wallet_security.rs").as_path());
|
||||||
|
let common = read_text(root.join("src/dto_common.rs").as_path());
|
||||||
|
for (source, name) in [
|
||||||
|
(session.as_str(), "WalletAuthorizedDto"),
|
||||||
|
(session.as_str(), "WalletSessionDto"),
|
||||||
|
(balance.as_str(), "WalletBalanceDto"),
|
||||||
|
(import.as_str(), "WalletTransferInspectionDto"),
|
||||||
|
(export.as_str(), "WalletExportResultDto"),
|
||||||
|
(security.as_str(), "WalletViewSecurityStatusDto"),
|
||||||
|
(common.as_str(), "RuntimeStatusDto"),
|
||||||
|
] {
|
||||||
|
let fields = struct_field_block(source, name);
|
||||||
|
for forbidden in ["password", "secret_key", "private_key", "keypair_bytes", "ciphertext"] {
|
||||||
|
assert!(!fields.contains(forbidden), "response DTO {name} contains forbidden field material {forbidden}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let inspection = struct_field_block(import.as_str(), "WalletTransferInspectionDto");
|
||||||
|
let export_result = struct_field_block(export.as_str(), "WalletExportResultDto");
|
||||||
|
assert!(!inspection.contains("path"));
|
||||||
|
assert!(!export_result.contains("path"));
|
||||||
|
assert!(!export_result.contains("content"));
|
||||||
|
assert!(!export_result.contains("bytes"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wallet_desk_dependency_firewall_and_tauri_capabilities_are_minimal() {
|
||||||
|
let root = app_root();
|
||||||
|
let manifest = read_text(root.join("Cargo.toml").as_path());
|
||||||
|
for line in manifest.lines() {
|
||||||
|
let trimmed = line.trim_start();
|
||||||
|
assert!(!trimmed.starts_with("solana-"), "Wallet Desk must consume Solana behavior through KSP crates: {trimmed}");
|
||||||
|
assert!(!trimmed.starts_with("reqwest "), "Wallet Desk must consume HTTP through Transport: {trimmed}");
|
||||||
|
assert!(!trimmed.starts_with("tracing "), "Wallet Desk must consume logging through the KSP facade/Tauri adapter: {trimmed}");
|
||||||
|
}
|
||||||
|
let capability_source = read_text(root.join("capabilities/default.json").as_path());
|
||||||
|
let capability = serde_json::from_str::<serde_json::Value>(capability_source.as_str());
|
||||||
|
assert!(capability.is_ok(), "Wallet Desk capability JSON should parse");
|
||||||
|
let capability = match capability {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => return,
|
||||||
|
};
|
||||||
|
let permissions = capability.get("permissions").and_then(serde_json::Value::as_array);
|
||||||
|
assert!(permissions.is_some());
|
||||||
|
let permissions = match permissions {
|
||||||
|
std::option::Option::Some(value) => value,
|
||||||
|
std::option::Option::None => return,
|
||||||
|
};
|
||||||
|
assert_eq!(permissions.len(), 2);
|
||||||
|
assert_eq!(permissions[0].as_str(), std::option::Option::Some("core:default"));
|
||||||
|
assert_eq!(permissions[1].as_str(), std::option::Option::Some("tracing:default"));
|
||||||
|
let tauri = read_text(root.join("tauri.conf.json").as_path());
|
||||||
|
assert!(tauri.contains("\"csp\": null"));
|
||||||
|
assert!(!capability_source.contains("dialog:"));
|
||||||
|
assert!(!capability_source.contains("fs:"));
|
||||||
|
}
|
||||||
143
crates/ksp-app-wallet-desk/tests/wallet_desk_devnet_smoke.rs
Normal file
143
crates/ksp-app-wallet-desk/tests/wallet_desk_devnet_smoke.rs
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
// file: crates/ksp-app-wallet-desk/tests/wallet_desk_devnet_smoke.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
//! Opt-in live composition smoke owned by the Wallet Desk orchestration surface.
|
||||||
|
|
||||||
|
fn workspace_engine() -> ksp_core_lib::Result<ksp_config_lib::ConfigDocumentEngine> {
|
||||||
|
let workspace = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../..");
|
||||||
|
let bootstrap = ksp_config_lib::ConfigBootstrapOptions::from_paths(workspace.join("config"), workspace.join("config/schemas"));
|
||||||
|
let bootstrap = match bootstrap {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
|
};
|
||||||
|
let registry = ksp_config_lib::ConfigFileRegistry::defaults();
|
||||||
|
return match registry {
|
||||||
|
std::result::Result::Ok(value) => std::result::Result::Ok(ksp_config_lib::ConfigDocumentEngine::new(bootstrap, value)),
|
||||||
|
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "current_thread")]
|
||||||
|
#[ignore = "opt-in live Wallet Desk Devnet smoke; creates one temporary .kspwallet under the Config-selected root and performs an external getBalance request"]
|
||||||
|
async fn committed_wallet_desk_composite_creates_wallet_and_reaches_devnet_balance() {
|
||||||
|
let engine = workspace_engine();
|
||||||
|
assert!(engine.is_ok(), "workspace Config engine should be constructible");
|
||||||
|
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_WALLET_DESK);
|
||||||
|
assert!(file_id.is_ok(), "Wallet Desk composite file_id should be valid");
|
||||||
|
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(), "committed Wallet Desk composite should resolve");
|
||||||
|
let composite = match composite {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => return,
|
||||||
|
};
|
||||||
|
assert_eq!(composite.profile_id(), "devnet");
|
||||||
|
let environment = ksp_config_lib::ConfigEnvironment::load();
|
||||||
|
assert!(environment.is_ok(), "Config-owned environment snapshot should load");
|
||||||
|
let environment = match environment {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => return,
|
||||||
|
};
|
||||||
|
let wallet_component = composite.component("wallet");
|
||||||
|
assert!(wallet_component.is_some(), "Wallet Desk composite should contain Wallet");
|
||||||
|
let wallet_component = match wallet_component {
|
||||||
|
std::option::Option::Some(value) => value,
|
||||||
|
std::option::Option::None => return,
|
||||||
|
};
|
||||||
|
let wallet_config = engine.resolve_wallet_config_profile(wallet_component.resolved(), &environment);
|
||||||
|
assert!(wallet_config.is_ok(), "composite-selected Wallet profile should map");
|
||||||
|
let wallet_config = match wallet_config {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => return,
|
||||||
|
};
|
||||||
|
assert_eq!(wallet_config.selection_source(), ksp_config_lib::ConfigProfileSelectionSource::Composite);
|
||||||
|
let wallet_root = wallet_config.effective_wallets_directory().to_path_buf();
|
||||||
|
assert!(!wallet_root.as_os_str().is_empty());
|
||||||
|
let directory = tokio::fs::create_dir_all(wallet_root.as_path()).await;
|
||||||
|
assert!(directory.is_ok(), "Config-selected Wallet smoke directory should be creatable");
|
||||||
|
if directory.is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let stamp = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH);
|
||||||
|
assert!(stamp.is_ok(), "system clock should be after Unix epoch");
|
||||||
|
let stamp = match stamp {
|
||||||
|
std::result::Result::Ok(value) => value.as_nanos(),
|
||||||
|
std::result::Result::Err(_) => return,
|
||||||
|
};
|
||||||
|
let wallet_path = wallet_root.join(std::format!("wallet-desk-pre013-smoke-{}-{stamp}.kspwallet", std::process::id()));
|
||||||
|
let owner = ksp_wallet_lib::create_wallet_file_v1(
|
||||||
|
wallet_path.as_path(),
|
||||||
|
ksp_wallet_lib::OwnerPassword::new("ksp-pre013-devnet-owner".to_owned()),
|
||||||
|
std::option::Option::Some(ksp_wallet_lib::ViewPassword::new("ksp-pre013-devnet-view".to_owned())),
|
||||||
|
ksp_wallet_lib::WalletCreateMetadataV1::new(std::option::Option::Some("pre013-devnet-smoke".to_owned()), std::vec::Vec::new()),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(owner.is_ok(), "Wallet smoke file should be created through the public Wallet API");
|
||||||
|
let owner = match owner {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => return,
|
||||||
|
};
|
||||||
|
let locked = ksp_wallet_lib::inspect_locked_wallet_file_v1(wallet_path.as_path()).await;
|
||||||
|
assert!(locked.is_ok(), "created smoke wallet should inspect while locked");
|
||||||
|
if let std::result::Result::Ok(locked) = locked {
|
||||||
|
assert_eq!(locked.format_version(), ksp_wallet_lib::KSPWALLET_FORMAT_VERSION_V1);
|
||||||
|
assert!(locked.view_enabled());
|
||||||
|
}
|
||||||
|
let view = ksp_wallet_lib::open_wallet_view_file_v1(wallet_path.as_path(), ksp_wallet_lib::ViewPassword::new("ksp-pre013-devnet-view".to_owned())).await;
|
||||||
|
assert!(view.is_ok(), "created smoke wallet should open with VIEW");
|
||||||
|
let view = match view {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => {
|
||||||
|
let _ = tokio::fs::remove_file(wallet_path.as_path()).await;
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert_eq!(owner.pubkey(), view.pubkey());
|
||||||
|
let transport_component = composite.component("transport");
|
||||||
|
assert!(transport_component.is_some(), "Wallet Desk composite should contain Transport");
|
||||||
|
let transport_component = match transport_component {
|
||||||
|
std::option::Option::Some(value) => value,
|
||||||
|
std::option::Option::None => {
|
||||||
|
let _ = tokio::fs::remove_file(wallet_path.as_path()).await;
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let transport = engine.resolve_transport_config_profile(transport_component.resolved(), &environment);
|
||||||
|
assert!(transport.is_ok(), "composite-selected Transport profile should map");
|
||||||
|
let transport = match transport {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => {
|
||||||
|
let _ = tokio::fs::remove_file(wallet_path.as_path()).await;
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert_eq!(transport.selection_source(), ksp_config_lib::ConfigProfileSelectionSource::Composite);
|
||||||
|
let pool = ksp_onchain_transport_lib::HttpTransportPool::new(transport.into_settings());
|
||||||
|
assert!(pool.is_ok(), "composite-selected Transport should construct the HTTP pool");
|
||||||
|
let pool = match pool {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => {
|
||||||
|
let _ = tokio::fs::remove_file(wallet_path.as_path()).await;
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let role = ksp_onchain_transport_lib::HttpRoleName::new("default");
|
||||||
|
let config = ksp_onchain_transport_lib::GetBalanceConfig::new(
|
||||||
|
std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Confirmed),
|
||||||
|
std::option::Option::None,
|
||||||
|
);
|
||||||
|
let balance = pool.get_balance(&role, view.pubkey(), std::option::Option::Some(&config)).await;
|
||||||
|
let cleanup = tokio::fs::remove_file(wallet_path.as_path()).await;
|
||||||
|
assert!(cleanup.is_ok(), "Wallet Desk Devnet smoke wallet should be removed after the request");
|
||||||
|
assert!(balance.is_ok(), "Wallet Desk Config -> Wallet -> Transport getBalance smoke should succeed");
|
||||||
|
if let std::result::Result::Ok(balance) = balance {
|
||||||
|
assert!(balance.context().slot() > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-app-wallet-desk/unit_tests/wallet_inventory.rs
|
// file: crates/ksp-app-wallet-desk/unit_tests/wallet_inventory.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
const VALID_WALLET: &[u8] = include_bytes!("../../ksp-wallet-lib/tests/fixtures/kspwallet_v1_full_vector.json");
|
const VALID_WALLET: &[u8] = include_bytes!("../../ksp-wallet-lib/tests/fixtures/kspwallet_v1_full_vector.json");
|
||||||
|
|
||||||
@@ -13,7 +13,11 @@ fn inventory_lists_regular_wallets_reports_invalid_candidates_and_ignores_other_
|
|||||||
assert!(std::fs::write(root.join("beta.kspwallet"), VALID_WALLET).is_ok());
|
assert!(std::fs::write(root.join("beta.kspwallet"), VALID_WALLET).is_ok());
|
||||||
assert!(std::fs::write(root.join("alpha.kspwallet"), b"not-json").is_ok());
|
assert!(std::fs::write(root.join("alpha.kspwallet"), b"not-json").is_ok());
|
||||||
assert!(std::fs::write(root.join("ignored.txt"), VALID_WALLET).is_ok());
|
assert!(std::fs::write(root.join("ignored.txt"), VALID_WALLET).is_ok());
|
||||||
|
assert!(std::fs::write(root.join("upper.KSPWALLET"), VALID_WALLET).is_ok());
|
||||||
|
assert!(std::fs::write(root.join("backup.kspwallet.bak"), VALID_WALLET).is_ok());
|
||||||
assert!(std::fs::create_dir(root.join("directory.kspwallet")).is_ok());
|
assert!(std::fs::create_dir(root.join("directory.kspwallet")).is_ok());
|
||||||
|
assert!(std::fs::create_dir(root.join("nested")).is_ok());
|
||||||
|
assert!(std::fs::write(root.join("nested/hidden.kspwallet"), VALID_WALLET).is_ok());
|
||||||
let runtime = tokio::runtime::Builder::new_current_thread().build();
|
let runtime = tokio::runtime::Builder::new_current_thread().build();
|
||||||
assert!(runtime.is_ok(), "test runtime should be constructible: {runtime:?}");
|
assert!(runtime.is_ok(), "test runtime should be constructible: {runtime:?}");
|
||||||
if let std::result::Result::Ok(runtime) = runtime {
|
if let std::result::Result::Ok(runtime) = runtime {
|
||||||
@@ -60,6 +64,14 @@ fn selection_rejects_traversal_and_reinspects_one_valid_locked_wallet() {
|
|||||||
if let std::result::Result::Err(error) = rejected_backslash {
|
if let std::result::Result::Err(error) = rejected_backslash {
|
||||||
assert_eq!(error.code(), crate::ERROR_CODE_WALLET_SELECTION_INVALID);
|
assert_eq!(error.code(), crate::ERROR_CODE_WALLET_SELECTION_INVALID);
|
||||||
}
|
}
|
||||||
|
for wallet_id in ["", "/selected.kspwallet", "nested/selected.kspwallet", "selected.kspwallet.bak"] {
|
||||||
|
let rejected =
|
||||||
|
runtime.block_on(crate::select_locked_wallet(root.as_path(), crate::WalletSelectionRequestDto { wallet_id: wallet_id.to_owned() }));
|
||||||
|
assert!(rejected.is_err(), "invalid wallet selection should be rejected: {wallet_id}");
|
||||||
|
if let std::result::Result::Err(error) = rejected {
|
||||||
|
assert_eq!(error.code(), crate::ERROR_CODE_WALLET_SELECTION_INVALID);
|
||||||
|
}
|
||||||
|
}
|
||||||
let selected =
|
let selected =
|
||||||
runtime.block_on(crate::select_locked_wallet(root.as_path(), crate::WalletSelectionRequestDto { wallet_id: "selected.kspwallet".to_owned() }));
|
runtime.block_on(crate::select_locked_wallet(root.as_path(), crate::WalletSelectionRequestDto { wallet_id: "selected.kspwallet".to_owned() }));
|
||||||
assert!(selected.is_ok(), "valid locked Wallet should be selectable: {selected:?}");
|
assert!(selected.is_ok(), "valid locked Wallet should be selectable: {selected:?}");
|
||||||
|
|||||||
65
deltas/0.2.6/pre.013.md
Normal file
65
deltas/0.2.6/pre.013.md
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<!-- file: deltas/0.2.6/pre.013.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta `0.2.6-pre.013` — intégration / compliance / sécurité / smoke
|
||||||
|
|
||||||
|
## Objet
|
||||||
|
|
||||||
|
Consolider les preuves transversales de Wallet Desk avant le polish visuel et la documentation finale : frontières Config/Wallet/Transport, inventory/path adversarial, DTO/IPC secrets, capabilities Tauri, dependency firewall et smoke Devnet de composition réel.
|
||||||
|
|
||||||
|
## Changements
|
||||||
|
|
||||||
|
- `workspace.package.version` passe à `0.2.6-pre.13` ;
|
||||||
|
- ajout de `tests/release_compliance.rs` :
|
||||||
|
- interdiction des bypass `std::env`, `reqwest`, Solana crates et `tracing` direct côté backend ;
|
||||||
|
- audit frontend sans filesystem/dialog guest, réseau direct, storage/clipboard secret ou noms `KSP_SECRET_WALLET_PASS_*` ;
|
||||||
|
- audit global des DTO de réponse contre password/key material/ciphertext ;
|
||||||
|
- capability Tauri limitée à `core:default` + `tracing:default` ;
|
||||||
|
- firewall du manifest Wallet Desk contre dépendances `solana-*`, `reqwest` et `tracing` directes ;
|
||||||
|
- renforcement des tests inventory/path :
|
||||||
|
- inventory non récursif ;
|
||||||
|
- suffixe `.kspwallet` exact ;
|
||||||
|
- casse/backup/nested/directory ignorés ;
|
||||||
|
- traversal, absolute, nested, suffixe invalide et identifiant vide rejetés à la sélection ;
|
||||||
|
- ajout du smoke `tests/wallet_desk_devnet_smoke.rs`, opt-in/ignored :
|
||||||
|
- charge le composite Wallet Desk et `ConfigEnvironment` ;
|
||||||
|
- résout Wallet et Transport en provenance `Composite` ;
|
||||||
|
- crée un `.kspwallet` réel VIEW+OWNER sous le répertoire Config effectif ;
|
||||||
|
- inspecte locked puis ouvre VIEW ;
|
||||||
|
- exécute `getBalance(confirmed)` via `HttpTransportPool` ;
|
||||||
|
- supprime le wallet canari après la requête ;
|
||||||
|
- ajout de `tokio/macros` aux features locales Wallet Desk pour le test async opt-in ;
|
||||||
|
- création de `docs/validation/009-V0_2_6_WALLET_DESK_COMPLIANCE.md` et indexation dans `docs/validation/000-README.md` ;
|
||||||
|
- correction des index `docs/000-README.md` et `docs/plans/000-README.md` pour refléter la séquence désormais réelle `pre.014` polish puis `pre.015` clôture ;
|
||||||
|
- `shell_phase`, badge et footer réalignés sur `pre.013-integration-compliance` ;
|
||||||
|
- `ROADMAP.md`, `CHANGELOG.md` et `docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md` restent inchangés.
|
||||||
|
|
||||||
|
## Sécurité / architecture
|
||||||
|
|
||||||
|
Le smoke cross-crates vit dans Wallet Desk, surface d'orchestration légitime, et non dans Config ou Transport. Le test ne lit aucune variable KSP directement : `KSP_WALLETS_DIRECTORY` est consommée par `ConfigEnvironment` conformément à l'ownership Config.
|
||||||
|
|
||||||
|
Aucun secret supplémentaire ne traverse IPC. Aucune permission Tauri Dialog/FS n'est accordée au frontend. Aucun accès RPC n'est ajouté au frontend.
|
||||||
|
|
||||||
|
## Validation opérateur
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo fmt --all
|
||||||
|
python3 scripts/audit_rust_workspace_rules.py
|
||||||
|
cargo check --workspace
|
||||||
|
cargo clippy --workspace --all-targets
|
||||||
|
cargo test -p ksp-app-wallet-desk
|
||||||
|
cargo test --workspace
|
||||||
|
|
||||||
|
cargo tree -p ksp-app-wallet-desk --depth 1
|
||||||
|
cargo tree -p ksp-app-wallet-desk -d
|
||||||
|
cargo tree -i ksp-wallet-lib
|
||||||
|
cargo tree -i ksp-onchain-transport-lib
|
||||||
|
cargo tree -i tauri-plugin-dialog
|
||||||
|
|
||||||
|
KSP_WALLETS_DIRECTORY=var/wallet-desk-pre013-smoke \
|
||||||
|
cargo test -p ksp-app-wallet-desk \
|
||||||
|
--test wallet_desk_devnet_smoke \
|
||||||
|
-- --ignored --nocapture
|
||||||
|
```
|
||||||
|
|
||||||
|
`cargo tauri dev` n'est pas obligatoire pour cette tranche si le runtime `pre.012` déjà communiqué reste la dernière preuve UI, car `pre.013` ne change aucune interaction métier ; il reste acceptable comme contrôle supplémentaire. Aucun `cargo tauri build` avant `pre.015`.
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/plans/000-README.md -->
|
<!-- file: docs/plans/000-README.md -->
|
||||||
<!-- version: 48 -->
|
<!-- version: 49 -->
|
||||||
|
|
||||||
# Plans KSP
|
# Plans KSP
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ Un plan décrit le périmètre, les décisions déjà acquises, les questions ou
|
|||||||
- [`010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md`](010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md) — plan historique clôturé de la release stable `0.2.3 — HTTP Transactions`, ouvert par `pre.001`, exécuté jusqu'à `pre.009` puis publié par `rel.001`; il couvre les 11 méthodes, la classification `8 Read / 2 WriteSubmission / 1 Simulation`, `KSP-TRANSPORT-007`, le no-resend et la préparation de `0.2.4`.
|
- [`010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md`](010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md) — plan historique clôturé de la release stable `0.2.3 — HTTP Transactions`, ouvert par `pre.001`, exécuté jusqu'à `pre.009` puis publié par `rel.001`; il couvre les 11 méthodes, la classification `8 Read / 2 WriteSubmission / 1 Simulation`, `KSP-TRANSPORT-007`, le no-resend et la préparation de `0.2.4`.
|
||||||
- [`011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md`](011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md) — plan historique clôturé de la release stable `0.2.4`, ouvert par `pre.001`, exécuté jusqu’à `pre.009`, complété par le fix documentaire Wallet `pre.009-fix.001` puis publié par `rel.001`; il couvre les 10 Blocks + 5 Economics et la compliance finale `52/52 + 14/14` sous `KSP-TRANSPORT-007`.
|
- [`011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md`](011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md) — plan historique clôturé de la release stable `0.2.4`, ouvert par `pre.001`, exécuté jusqu’à `pre.009`, complété par le fix documentaire Wallet `pre.009-fix.001` puis publié par `rel.001`; il couvre les 10 Blocks + 5 Economics et la compliance finale `52/52 + 14/14` sous `KSP-TRANSPORT-007`.
|
||||||
- [`012-V0_2_5_WALLET_FOUNDATION_PLAN.md`](012-V0_2_5_WALLET_FOUNDATION_PLAN.md) — plan historique clôturé de la release stable `0.2.5 — Wallet foundation`, ouvert par `pre.001`, livré jusqu’à `pre.010`, renforcé par `pre.010-fix.001`–`fix.003` pour Dalek 3 et la normalisation Rust/audit structurel, puis publié par `rel.001`; il couvre `.kspwallet` V1, VIEW/OWNER, crypto, persistence, administration, transfer et compliance.
|
- [`012-V0_2_5_WALLET_FOUNDATION_PLAN.md`](012-V0_2_5_WALLET_FOUNDATION_PLAN.md) — plan historique clôturé de la release stable `0.2.5 — Wallet foundation`, ouvert par `pre.001`, livré jusqu’à `pre.010`, renforcé par `pre.010-fix.001`–`fix.003` pour Dalek 3 et la normalisation Rust/audit structurel, puis publié par `rel.001`; il couvre `.kspwallet` V1, VIEW/OWNER, crypto, persistence, administration, transfer et compliance.
|
||||||
- [`013-V0_2_6_WALLET_DESK_PLAN.md`](013-V0_2_6_WALLET_DESK_PLAN.md) — plan actif de `0.2.6 — Wallet Desk`, ouvert par `pre.001`; `pre.002` matérialise le shell Tauri et `pre.003` la composition `std.wallet`/composite ainsi que la préparation des répertoires. Le plan fixe aussi secrets `KSP_SECRET_WALLET_PASS_*`, inventory DataTables/path safety, lifecycle VIEW/OWNER, screen/command/DTO maps, balance HTTP, administration retenue et forecast souple détaillé jusqu’à `pre.014`, incluant la documentation finale et le prompt `0.2.7`.
|
- [`013-V0_2_6_WALLET_DESK_PLAN.md`](013-V0_2_6_WALLET_DESK_PLAN.md) — plan actif de `0.2.6 — Wallet Desk`, ouvert par `pre.001`; `pre.002` matérialise le shell Tauri et `pre.003` la composition `std.wallet`/composite ainsi que la préparation des répertoires. Le plan fixe aussi secrets `KSP_SECRET_WALLET_PASS_*`, inventory DataTables/path safety, lifecycle VIEW/OWNER, screen/command/DTO maps, balance HTTP, administration retenue et forecast souple détaillé jusqu’à `pre.015`, avec `pre.014` réservé au polish Bootstrap/splashscreen puis `pre.015` à la documentation finale, au prompt `0.2.7` et au build Tauri final.
|
||||||
|
|
||||||
Le `pre.001` de chaque release fonctionnelle peut introduire son propre plan détaillé lorsque la release s'ouvre.
|
Le `pre.001` de chaque release fonctionnelle peut introduire son propre plan détaillé lorsque la release s'ouvre.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/plans/013-V0_2_6_WALLET_DESK_PLAN.md -->
|
<!-- file: docs/plans/013-V0_2_6_WALLET_DESK_PLAN.md -->
|
||||||
<!-- version: 27 -->
|
<!-- version: 28 -->
|
||||||
|
|
||||||
# Plan `0.2.6` — Wallet Desk
|
# Plan `0.2.6` — Wallet Desk
|
||||||
|
|
||||||
@@ -1651,21 +1651,24 @@ Le nom de destination et le contenu secret ne sont pas journalisés. Les logs pe
|
|||||||
|
|
||||||
### `pre.013` — intégration, compliance, sécurité et smoke
|
### `pre.013` — intégration, compliance, sécurité et smoke
|
||||||
|
|
||||||
Objectifs :
|
Cette tranche consolide les preuves transversales sans ajouter de nouvelle capacité Wallet Desk métier. Elle matérialise :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
tests déterministes de composition Config -> Wallet -> Transport
|
tests déterministes de composition et de frontières release-wide
|
||||||
inventory/path adversarial
|
inventory/path adversarial renforcé : non récursif, suffixe exact, traversal/absolute/nested rejetés
|
||||||
dto/debug/log secret audit
|
audit DTO/IPC : aucun secret, keypair, endpoint URL ou path arbitraire projeté vers le frontend
|
||||||
frontend state purge/security checks
|
audit frontend : aucun filesystem/dialog plugin guest, aucun fetch/WebSocket, aucun storage/clipboard secret
|
||||||
capabilities audit
|
audit capabilities : core:default + tracing:default uniquement
|
||||||
cargo tree / inverse tree pertinents
|
audit dependency firewall Wallet Desk : pas de dépendance Solana/protocole directe hors crates KSP
|
||||||
Devnet smoke opt-in
|
smoke Devnet opt-in Config -> Wallet -> Transport -> getBalance depuis la surface Wallet Desk
|
||||||
validation/009 ou numéro courant dédié à 0.2.6
|
matrice docs/validation/009-V0_2_6_WALLET_DESK_COMPLIANCE.md
|
||||||
|
cargo tree / inverse tree opérateur
|
||||||
workspace checkpoint complet
|
workspace checkpoint complet
|
||||||
```
|
```
|
||||||
|
|
||||||
Cette tranche doit produire la preuve que les surfaces ajoutées fonctionnent ensemble avant la documentation de clôture.
|
Le smoke live est volontairement `ignored` dans `cargo test --workspace`. Il doit être déclenché explicitement avec un `KSP_WALLETS_DIRECTORY` dédié ; le test charge ce paramètre uniquement via `ConfigEnvironment`, crée un `.kspwallet` réel dans le répertoire effectif du profil composite, ouvre VIEW, exécute `getBalance` via le Transport composite puis supprime le wallet canari. Ainsi le smoke cross-crates vit dans la surface d'orchestration Wallet Desk et non dans Config ou Transport.
|
||||||
|
|
||||||
|
Cette tranche doit produire la preuve que les surfaces ajoutées fonctionnent ensemble avant le polish visuel `pre.014` et la documentation finale `pre.015`.
|
||||||
|
|
||||||
### `pre.014` — polish gabarit Bootstrap et splashscreen
|
### `pre.014` — polish gabarit Bootstrap et splashscreen
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/validation/000-README.md -->
|
<!-- file: docs/validation/000-README.md -->
|
||||||
<!-- version: 14 -->
|
<!-- version: 15 -->
|
||||||
|
|
||||||
# Validations KSP
|
# Validations KSP
|
||||||
|
|
||||||
@@ -18,3 +18,5 @@ Documents :
|
|||||||
- [`006-V0_2_3_HTTP_TRANSACTIONS.md`](006-V0_2_3_HTTP_TRANSACTIONS.md) — matrice finale validée de `0.2.3`, 11 wrappers Transactions, sécurité write/simulation, réaudit 52+14, `KSP-TRANSPORT-007` 37/37, graphes Cargo et deux smokes Devnet passés avant publication stable.
|
- [`006-V0_2_3_HTTP_TRANSACTIONS.md`](006-V0_2_3_HTTP_TRANSACTIONS.md) — matrice finale validée de `0.2.3`, 11 wrappers Transactions, sécurité write/simulation, réaudit 52+14, `KSP-TRANSPORT-007` 37/37, graphes Cargo et deux smokes Devnet passés avant publication stable.
|
||||||
- [`007-V0_2_4_HTTP_FINAL_COMPLIANCE.md`](007-V0_2_4_HTTP_FINAL_COMPLIANCE.md) — matrice finale validée de `0.2.4`, inventaire exact 52 current + 14 Deprecated, preuve typed 52/52, audit SIMD final, `KSP-TRANSPORT-007`, workspace complet et deux smokes Devnet passés avant publication stable.
|
- [`007-V0_2_4_HTTP_FINAL_COMPLIANCE.md`](007-V0_2_4_HTTP_FINAL_COMPLIANCE.md) — matrice finale validée de `0.2.4`, inventaire exact 52 current + 14 Deprecated, preuve typed 52/52, audit SIMD final, `KSP-TRANSPORT-007`, workspace complet et deux smokes Devnet passés avant publication stable.
|
||||||
- [`008-V0_2_5_WALLET_SECURITY_COMPLIANCE.md`](008-V0_2_5_WALLET_SECURITY_COMPLIANCE.md) — matrice finale validée de la release stable `0.2.5`, threat model V1, canaris adversariaux, reproduction externe des vecteurs, audit de frontières, normalisation Rust/audit structurel, graphes Cargo et checkpoint final `pre.010-fix.003` vert.
|
- [`008-V0_2_5_WALLET_SECURITY_COMPLIANCE.md`](008-V0_2_5_WALLET_SECURITY_COMPLIANCE.md) — matrice finale validée de la release stable `0.2.5`, threat model V1, canaris adversariaux, reproduction externe des vecteurs, audit de frontières, normalisation Rust/audit structurel, graphes Cargo et checkpoint final `pre.010-fix.003` vert.
|
||||||
|
|
||||||
|
- [`009-V0_2_6_WALLET_DESK_COMPLIANCE.md`](009-V0_2_6_WALLET_DESK_COMPLIANCE.md) — matrice de compliance ouverte en `0.2.6-pre.013`, couvrant composition Config/Wallet/Transport, frontières DTO/IPC/frontend, path/inventory adversarial, capabilities Tauri, dependency firewall, graphes Cargo et smoke Devnet Wallet Desk opt-in.
|
||||||
|
|||||||
234
docs/validation/009-V0_2_6_WALLET_DESK_COMPLIANCE.md
Normal file
234
docs/validation/009-V0_2_6_WALLET_DESK_COMPLIANCE.md
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
<!-- file: docs/validation/009-V0_2_6_WALLET_DESK_COMPLIANCE.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Validation `0.2.6` — Wallet Desk integration / compliance / security
|
||||||
|
|
||||||
|
## 1. Objet
|
||||||
|
|
||||||
|
Cette matrice est ouverte par `0.2.6-pre.013` comme preuve durable de composition et de conformité de `ksp-app-wallet-desk`. Elle sera complétée en `pre.015` avec les résultats finaux, le polish visuel de `pre.014`, la documentation de clôture et le dernier build Tauri.
|
||||||
|
|
||||||
|
Elle ne remplace ni le plan [`../plans/013-V0_2_6_WALLET_DESK_PLAN.md`](../plans/013-V0_2_6_WALLET_DESK_PLAN.md), ni la matrice Wallet V1 [`008-V0_2_5_WALLET_SECURITY_COMPLIANCE.md`](008-V0_2_5_WALLET_SECURITY_COMPLIANCE.md), ni les deltas `0.2.6`.
|
||||||
|
|
||||||
|
Le gate `pre.013` porte sur :
|
||||||
|
|
||||||
|
```text
|
||||||
|
composition Config -> Wallet -> Transport
|
||||||
|
inventory/path adversarial
|
||||||
|
DTO / IPC / frontend secret boundaries
|
||||||
|
capabilities Tauri
|
||||||
|
KSP dependency ownership
|
||||||
|
smoke Devnet opt-in Wallet Desk
|
||||||
|
Cargo tree / inverse tree
|
||||||
|
workspace checkpoint complet
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Surface intégrée auditée
|
||||||
|
|
||||||
|
Au démarrage de `pre.013`, Wallet Desk a déjà validé en runtime :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Config composite logging + wallet + transport
|
||||||
|
répertoire Wallet effectif Config-owned
|
||||||
|
inventory locked .kspwallet
|
||||||
|
create
|
||||||
|
import Solana CLI JSON / Base58
|
||||||
|
unlock manuel et Config-owned VIEW / OWNER
|
||||||
|
getBalance via Transport
|
||||||
|
alias / notes OWNER
|
||||||
|
rotation OWNER
|
||||||
|
rotation VIEW par OWNER
|
||||||
|
self-rotation VIEW
|
||||||
|
strong disable/recreate VIEW
|
||||||
|
export OWNER Solana CLI JSON / Base58
|
||||||
|
no-clobber export
|
||||||
|
```
|
||||||
|
|
||||||
|
Le checkpoint opérateur `pre.012` du 21 août 2026 est vert : `cargo fmt`, audit Python, `cargo check`, Clippy sans warning, tests Wallet Desk, tests workspace complets et parcours `cargo tauri dev`. Le runtime confirme aussi l'export des deux formats, le refus `wallet.destination_exists` sans invalidation de la session OWNER et l'annulation du save picker sans effet.
|
||||||
|
|
||||||
|
## 3. Matrice de frontières
|
||||||
|
|
||||||
|
| Frontière | Contrat `0.2.6` | Preuve durable |
|
||||||
|
|---|---|---|
|
||||||
|
| Frontend -> Config | aucun accès direct env/.env | `release_compliance::backend_keeps_config_wallet_transport_and_logging_ownership_boundaries` + ownership Config |
|
||||||
|
| Frontend -> Wallet secret | passwords uniquement request-only ; jamais de keypair en IPC | `desktop_security` + `release_compliance::response_dtos_keep_secret_key_material_out_of_ipc` |
|
||||||
|
| Frontend -> filesystem | aucun path arbitraire ; picker natif Rust | tests import/export + capability audit |
|
||||||
|
| Frontend -> réseau Solana | aucun `fetch`/WebSocket/RPC direct | `release_compliance::frontend_has_no_direct_filesystem_network_or_secret_persistence_surface` |
|
||||||
|
| Wallet Desk -> Solana crates | aucune dépendance protocole directe | `release_compliance::wallet_desk_dependency_firewall_and_tauri_capabilities_are_minimal` |
|
||||||
|
| Wallet Desk -> HTTP | `ksp-onchain-transport-lib` uniquement | manifest + smoke Devnet |
|
||||||
|
| Wallet Desk -> Wallet crypto | `ksp-wallet-lib` uniquement | manifest + feature canaries |
|
||||||
|
| Wallet Desk -> logging | façade `ksp-logging-lib` + adapter `tauri-plugin-tracing` | source audit + workspace logging canary |
|
||||||
|
| locked -> frontend | format/version/VIEW enabled seulement | `desktop_security` |
|
||||||
|
| VIEW/OWNER handles | Rust-only `AppState` | session/security tests |
|
||||||
|
| export | OWNER-only, file direct, no-clobber | `desktop_security` + runtime `pre.012` |
|
||||||
|
|
||||||
|
## 4. Inventory / path adversarial
|
||||||
|
|
||||||
|
`pre.013` renforce le canari déterministe d'inventory :
|
||||||
|
|
||||||
|
```text
|
||||||
|
seuls les fichiers réguliers directs *.kspwallet sont candidats
|
||||||
|
suffixe exact et sensible à la casse
|
||||||
|
backup.kspwallet.bak ignoré
|
||||||
|
upper.KSPWALLET ignoré
|
||||||
|
nested/hidden.kspwallet ignoré
|
||||||
|
répertoire nommé *.kspwallet ignoré
|
||||||
|
symlink *.kspwallet ignoré
|
||||||
|
sélection ../... rejetée
|
||||||
|
sélection ..\... rejetée
|
||||||
|
sélection absolue rejetée
|
||||||
|
sélection nested/... rejetée
|
||||||
|
sélection suffixe non exact rejetée
|
||||||
|
sélection vide rejetée
|
||||||
|
```
|
||||||
|
|
||||||
|
La sélection continue de réinspecter le fichier verrouillé au moment de l'ouverture de session ; l'inventory n'est jamais utilisé comme autorité de sécurité durable.
|
||||||
|
|
||||||
|
## 5. DTO / IPC / frontend audit
|
||||||
|
|
||||||
|
Les réponses IPC auditées ne doivent jamais contenir :
|
||||||
|
|
||||||
|
```text
|
||||||
|
password
|
||||||
|
secret_key
|
||||||
|
private_key
|
||||||
|
keypair_bytes
|
||||||
|
ciphertext
|
||||||
|
```
|
||||||
|
|
||||||
|
Les projections import/export ne contiennent aucun path complet. `WalletAuthorizedDto` peut exposer Pubkey/alias/notes uniquement après VIEW/OWNER ; le locked inventory ne peut jamais les exposer.
|
||||||
|
|
||||||
|
Le frontend doit rester sans :
|
||||||
|
|
||||||
|
```text
|
||||||
|
@tauri-apps/plugin-dialog
|
||||||
|
@tauri-apps/plugin-fs
|
||||||
|
window.alert / confirm / prompt
|
||||||
|
localStorage / sessionStorage
|
||||||
|
navigator.clipboard pour secret
|
||||||
|
fetch / XMLHttpRequest / WebSocket / EventSource
|
||||||
|
KSP_SECRET_WALLET_PASS_* concret
|
||||||
|
sourcePath / destinationPath
|
||||||
|
```
|
||||||
|
|
||||||
|
Les opérations natives import/export utilisent le plugin Dialog côté Rust, sans permission Dialog/FS accordée au guest frontend.
|
||||||
|
|
||||||
|
## 6. Capabilities Tauri
|
||||||
|
|
||||||
|
La capability Wallet Desk doit rester exactement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
core:default
|
||||||
|
tracing:default
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucune permission `dialog:*` ou `fs:*` n'est accordée au frontend. La CSP reste `null` tant qu'un besoin concret n'impose pas une politique spécifique ; `pre.013` n'ajoute aucune ressource réseau frontend.
|
||||||
|
|
||||||
|
## 7. Dependency firewall
|
||||||
|
|
||||||
|
Dépendances KSP directes attendues de Wallet Desk :
|
||||||
|
|
||||||
|
```text
|
||||||
|
ksp-config-lib
|
||||||
|
ksp-core-lib
|
||||||
|
ksp-logging-lib
|
||||||
|
ksp-onchain-transport-lib
|
||||||
|
ksp-wallet-lib
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucune dépendance directe `solana-*`, `reqwest` ou `tracing` n'est admise dans le manifest Wallet Desk. `tauri-plugin-tracing` reste l'adapter Tauri justifié ; l'émission applicative reste via `ksp-logging-lib`.
|
||||||
|
|
||||||
|
Commandes opérateur `pre.013` :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo tree -p ksp-app-wallet-desk --depth 1
|
||||||
|
cargo tree -p ksp-app-wallet-desk -d
|
||||||
|
cargo tree -i ksp-wallet-lib
|
||||||
|
cargo tree -i ksp-onchain-transport-lib
|
||||||
|
cargo tree -i tauri-plugin-dialog
|
||||||
|
```
|
||||||
|
|
||||||
|
Le résultat attendu est qu'aucune nouvelle dépendance Solana/protocole directe ne soit introduite par `pre.013`; le seul changement de feature est `tokio/macros` requis pour le smoke opt-in de la crate application.
|
||||||
|
|
||||||
|
## 8. Smoke Devnet Wallet Desk
|
||||||
|
|
||||||
|
Le smoke live est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Config composite devnet
|
||||||
|
-> resolve Wallet profile Composite
|
||||||
|
-> ConfigEnvironment
|
||||||
|
-> répertoire Wallet effectif
|
||||||
|
-> create .kspwallet réel VIEW+OWNER
|
||||||
|
-> inspect locked
|
||||||
|
-> open VIEW
|
||||||
|
-> resolve Transport profile Composite
|
||||||
|
-> HttpTransportPool
|
||||||
|
-> getBalance(confirmed) sur la Pubkey du wallet créé
|
||||||
|
-> suppression du .kspwallet canari
|
||||||
|
```
|
||||||
|
|
||||||
|
Il est volontairement `ignored` dans la suite déterministe afin d'éviter une dépendance réseau lors de `cargo test --workspace`.
|
||||||
|
|
||||||
|
Commande recommandée :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
KSP_WALLETS_DIRECTORY=var/wallet-desk-pre013-smoke \
|
||||||
|
cargo test -p ksp-app-wallet-desk \
|
||||||
|
--test wallet_desk_devnet_smoke \
|
||||||
|
-- --ignored --nocapture
|
||||||
|
```
|
||||||
|
|
||||||
|
Ce placement est intentionnel : le smoke cross-crates `Config -> Wallet -> Transport -> Devnet getBalance` appartient à la surface d'orchestration Wallet Desk. Il ne doit pas migrer dans `ksp-config-lib` ou `ksp-onchain-transport-lib`.
|
||||||
|
|
||||||
|
Un solde `0` est un succès valide ; le gate vérifie l'exécution RPC et un contexte de slot non nul, pas un montant particulier.
|
||||||
|
|
||||||
|
## 9. Gate déterministe `pre.013`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo fmt --all
|
||||||
|
python3 scripts/audit_rust_workspace_rules.py
|
||||||
|
cargo check --workspace
|
||||||
|
cargo clippy --workspace --all-targets
|
||||||
|
cargo test -p ksp-app-wallet-desk
|
||||||
|
cargo test --workspace
|
||||||
|
```
|
||||||
|
|
||||||
|
Attendus :
|
||||||
|
|
||||||
|
```text
|
||||||
|
audit structurel clean
|
||||||
|
aucun warning check/Clippy
|
||||||
|
release_compliance vert
|
||||||
|
wallet_desk_devnet_smoke présent mais ignored dans workspace
|
||||||
|
inventory adversarial renforcé vert
|
||||||
|
aucune régression Config/Core/Logging/Transport/Wallet
|
||||||
|
```
|
||||||
|
|
||||||
|
## 10. Checkpoint runtime `pre.012` déjà acquis
|
||||||
|
|
||||||
|
Le runtime communiqué avant ouverture de `pre.013` confirme notamment :
|
||||||
|
|
||||||
|
```text
|
||||||
|
export Base58 OWNER : OK
|
||||||
|
export Solana CLI JSON OWNER : OK
|
||||||
|
destination existante : wallet.destination_exists, session OWNER conservée
|
||||||
|
annulation save picker : aucun export, session conservée
|
||||||
|
VIEW : export indisponible
|
||||||
|
aucun secret/path complet observé dans les logs frontend/backend fournis
|
||||||
|
```
|
||||||
|
|
||||||
|
Ce checkpoint n'est pas rejoué automatiquement par `pre.013`; la tranche ajoute les canaries transversaux qui rendent ces frontières durables.
|
||||||
|
|
||||||
|
## 11. Résultats `pre.013`
|
||||||
|
|
||||||
|
À compléter après exécution opérateur :
|
||||||
|
|
||||||
|
```text
|
||||||
|
fmt / audit / check / clippy : PENDING
|
||||||
|
cargo test -p ksp-app-wallet-desk : PENDING
|
||||||
|
cargo test --workspace : PENDING
|
||||||
|
cargo tree / inverse tree : PENDING
|
||||||
|
smoke Devnet Wallet Desk opt-in : PENDING
|
||||||
|
```
|
||||||
|
|
||||||
|
La matrice reste ouverte jusqu'à `pre.015`, où seront ajoutés le résultat du polish `pre.014`, le parcours fonctionnel final, les docs README/USAGE et le build Tauri exécuté en toute dernière opération.
|
||||||
Reference in New Issue
Block a user