v0.2.5-pre.002

This commit is contained in:
2026-08-19 09:56:01 +02:00
parent c2ccef3849
commit c97a8739ab
19 changed files with 1037 additions and 27 deletions

View File

@@ -1,12 +1,12 @@
# file: Cargo.toml # file: Cargo.toml
# version: 142 # version: 143
[workspace] [workspace]
resolver = "3" resolver = "3"
members = ["crates/ksp-app-config-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-logging-lib", "crates/ksp-onchain-transport-lib"] members = ["crates/ksp-app-config-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.5-pre.1" version = "0.2.5-pre.2"
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"
@@ -29,6 +29,7 @@ tauri = { version = "^2.11" }
tauri-build = { version = "^2.6" } tauri-build = { version = "^2.6" }
tauri-plugin-tracing = { version = "^0.3" } tauri-plugin-tracing = { version = "^0.3" }
ts-rs = { version = "^12.0" } ts-rs = { version = "^12.0" }
zeroize = { version = "^1.9" }
[workspace.lints.rust] [workspace.lints.rust]
missing_docs = "warn" missing_docs = "warn"

View File

@@ -1,5 +1,5 @@
<!-- file: ROADMAP.md --> <!-- file: ROADMAP.md -->
<!-- version: 46 --> <!-- version: 47 -->
# Roadmap KSP # Roadmap KSP
@@ -49,7 +49,7 @@ Le roadmap décrit les objectifs à atteindre et les grandes étapes prévues. U
- [X] `0.2.2` — HTTP Accounts + Tokens + Cluster : 22 wrappers typés (5 Accounts + 5 Tokens + 12 Cluster), canaries de complétude 52+14, smoke Devnet Transport pur et smoke historique Config -> Transport validés, documentation durable et prompt `0.2.3` publiés stables. - [X] `0.2.2` — HTTP Accounts + Tokens + Cluster : 22 wrappers typés (5 Accounts + 5 Tokens + 12 Cluster), canaries de complétude 52+14, smoke Devnet Transport pur et smoke historique Config -> Transport validés, documentation durable et prompt `0.2.3` publiés stables.
- [X] `0.2.3` — HTTP Transactions stable : 11/11 wrappers typés publiés, classification `8 Read / 2 WriteSubmission / 1 Simulation`, no-resend ambigu prouvé pour les write submissions, `KSP-TRANSPORT-007` réaudité conforme sur les 37 wrappers HTTP courants, graphes Cargo et deux smokes Devnet validés ; `0.2.4` reprend les 15 Blocks/Economics restants. - [X] `0.2.3` — HTTP Transactions stable : 11/11 wrappers typés publiés, classification `8 Read / 2 WriteSubmission / 1 Simulation`, no-resend ambigu prouvé pour les write submissions, `KSP-TRANSPORT-007` réaudité conforme sur les 37 wrappers HTTP courants, graphes Cargo et deux smokes Devnet validés ; `0.2.4` reprend les 15 Blocks/Economics restants.
- [X] `0.2.4` — HTTP Blocks + Economics stable : 15/15 wrappers `V0_2_4` publiés, surface typed complète à 52/52 méthodes courantes, 14/14 historiques conservées, réaudit SIMD/inventaire final et `KSP-TRANSPORT-007` global validés ; deux smokes Devnet passés avant publication. - [X] `0.2.4` — HTTP Blocks + Economics stable : 15/15 wrappers `V0_2_4` publiés, surface typed complète à 52/52 méthodes courantes, 14/14 historiques conservées, réaudit SIMD/inventaire final et `KSP-TRANSPORT-007` global validés ; deux smokes Devnet passés avant publication.
- [/] `0.2.5` — Wallet foundation ouverte par `pre.001` : threat model offline et héritage bot2/bot3 réaudités, `.kspwallet` V1 entièrement autonome sans facteur externe, design content-keys/key-slots VIEW/OWNER indépendant, metadata VIEW read-only niveau B via autorité Ed25519 de format distincte, slot VIEW auto-rotatable uniquement pour son propre password, OWNER capable de rotation OWNER/VIEW et dadministration complète, keypair V1 immuable, spec multi-langages séparée + vecteurs publics exigés, paramètres KDF à benchmarker avant freeze, persistence/signature/rotations/import-export répartis jusquà `pre.010` ; `WalletPolicy` reste exclu. - [/] `0.2.5` — Wallet foundation : `pre.001` fixe le threat model et le format V1 autonome ; `pre.002` crée `ksp-wallet-lib` avec capabilities VIEW/OWNER, projections metadata protégées, wrappers password redacted/zeroized, erreurs Wallet, target `ksp-wallet-lib` via `ksp-logging-lib` et canaries de frontières. `Pubkey` est consommée uniquement via `ksp-core-lib`, sans dépendance directe `solana-pubkey`; Config/Transport/ExecutionPolicy/Store/Tauri restent hors Wallet. Le wire/crypto/persistence/signature/rotations/import-export restent répartis jusquà `pre.010` ; `WalletPolicy` reste exclu.
- [ ] `0.2.6` — Introduire `ksp-app-wallet-desk` utilisant Config composite + Wallet + transport HTTP, notamment pour afficher l'identité et le solde d'un wallet. - [ ] `0.2.6` — Introduire `ksp-app-wallet-desk` utilisant Config composite + Wallet + transport HTTP, notamment pour afficher l'identité et le solde d'un wallet.
- [ ] `0.2.7` — Étendre `ksp-onchain-transport-lib` au WebSocket Solana standard complet ; permettre plusieurs sessions sur une même URL sans imposer encore un pool automatique complexe. - [ ] `0.2.7` — Étendre `ksp-onchain-transport-lib` au WebSocket Solana standard complet ; permettre plusieurs sessions sur une même URL sans imposer encore un pool automatique complexe.
- [ ] `0.2.8` — Ajouter Helius LaserStream WebSocket comme extension du moteur WebSocket standard, sans duplication de client. - [ ] `0.2.8` — Ajouter Helius LaserStream WebSocket comme extension du moteur WebSocket standard, sans duplication de client.

View File

@@ -0,0 +1,16 @@
# file: crates/ksp-wallet-lib/Cargo.toml
# version: 1
[package]
name = "ksp-wallet-lib"
version.workspace = true
edition.workspace = true
repository.workspace = true
[dependencies]
ksp-core-lib = { path = "../ksp-core-lib" }
ksp-logging-lib = { path = "../ksp-logging-lib" }
zeroize.workspace = true
[lints]
workspace = true

View File

@@ -0,0 +1,15 @@
// file: crates/ksp-wallet-lib/src/capability.rs
// version: 1
/// Authorized capability represented by an unlocked Wallet handle.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum WalletCapability {
/// Metadata access plus rotation of the current VIEW password only.
View,
/// Full Wallet ownership including signing and administration.
Owner,
}
#[cfg(test)]
#[path = "../unit_tests/capability.rs"]
mod tests;

View File

@@ -0,0 +1,7 @@
// file: crates/ksp-wallet-lib/src/constants.rs
// version: 1
//! Wallet-owned constants.
/// Owning tracing target for events emitted by the Wallet crate.
pub(crate) const TRACING_TARGET: &str = "ksp-wallet-lib";

View File

@@ -0,0 +1,29 @@
// file: crates/ksp-wallet-lib/src/error.rs
// version: 1
/// Error code used when a native Wallet structure is invalid.
pub const ERROR_CODE_FORMAT_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "format_invalid");
/// Error code used when a native Wallet format version is unsupported.
pub const ERROR_CODE_FORMAT_VERSION_UNSUPPORTED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "format_version_unsupported");
/// Error code used when serialized cryptographic parameters are invalid or unsupported.
pub const ERROR_CODE_CRYPTO_PARAMETERS_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "crypto_parameters_invalid");
/// Error code used when an authenticated Wallet structure cannot be verified.
pub const ERROR_CODE_AUTHENTICATION_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "authentication_failed");
/// Error code used when a VIEW unlock attempt fails without exposing a finer cryptographic oracle.
pub const ERROR_CODE_VIEW_UNLOCK_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "view_unlock_failed");
/// Error code used when an OWNER unlock attempt fails without exposing a finer cryptographic oracle.
pub const ERROR_CODE_OWNER_UNLOCK_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "owner_unlock_failed");
/// Error code used when an operation requires a capability that the caller does not own.
pub const ERROR_CODE_CAPABILITY_INSUFFICIENT: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "capability_insufficient");
/// Error code used when Wallet filesystem I/O fails.
pub const ERROR_CODE_IO_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "io_failed");
/// Error code used when a no-clobber create or import destination already exists.
pub const ERROR_CODE_DESTINATION_EXISTS: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "destination_exists");
/// Error code used when an atomic Wallet persistence operation cannot publish a valid replacement.
pub const ERROR_CODE_ATOMIC_PERSISTENCE_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "atomic_persistence_failed");
/// Error code used when an import/export transfer format is unsupported.
pub const ERROR_CODE_TRANSFER_FORMAT_UNSUPPORTED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "transfer_format_unsupported");
/// Error code used when imported or decoded key material is invalid.
pub const ERROR_CODE_KEY_MATERIAL_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "key_material_invalid");
/// Error code used when a Wallet signing operation fails.
pub const ERROR_CODE_SIGNATURE_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "signature_failed");

View File

@@ -0,0 +1,66 @@
// file: crates/ksp-wallet-lib/src/lib.rs
// version: 1
#![warn(missing_docs)]
#![deny(unreachable_pub)]
#![forbid(unsafe_code)]
//! Autonomous KSP Wallet foundation.
//!
//! `ksp-wallet-lib` owns the native `.kspwallet` domain, VIEW/OWNER capability model, protected metadata projection, password-secret wrappers and Wallet
//! error contract. The `0.2.5-pre.002` foundation deliberately contains no file codec, KDF/AEAD implementation, Solana secret material, persistence,
//! network access, Config integration or execution policy. Public keys are consumed exclusively through the [`ksp_core_lib::Pubkey`] re-export owned by
//! KSP Core, and behavioral observability uses only `ksp-logging-lib` with the explicit crate target defined in `src/constants.rs`.
mod capability;
mod constants;
mod error;
mod metadata;
mod owner;
mod password;
mod view;
/// Authorized capability represented by an unlocked Wallet handle.
pub use self::capability::WalletCapability;
/// Error code used when an atomic Wallet persistence operation cannot publish a valid replacement.
pub use self::error::ERROR_CODE_ATOMIC_PERSISTENCE_FAILED;
/// Error code used when an authenticated Wallet structure cannot be verified.
pub use self::error::ERROR_CODE_AUTHENTICATION_FAILED;
/// Error code used when an operation requires a capability that the caller does not own.
pub use self::error::ERROR_CODE_CAPABILITY_INSUFFICIENT;
/// Error code used when serialized cryptographic parameters are invalid or unsupported.
pub use self::error::ERROR_CODE_CRYPTO_PARAMETERS_INVALID;
/// Error code used when a no-clobber create or import destination already exists.
pub use self::error::ERROR_CODE_DESTINATION_EXISTS;
/// Error code used when a native Wallet structure is invalid.
pub use self::error::ERROR_CODE_FORMAT_INVALID;
/// Error code used when a native Wallet format version is unsupported.
pub use self::error::ERROR_CODE_FORMAT_VERSION_UNSUPPORTED;
/// Error code used when Wallet filesystem I/O fails.
pub use self::error::ERROR_CODE_IO_FAILED;
/// Error code used when imported or decoded key material is invalid.
pub use self::error::ERROR_CODE_KEY_MATERIAL_INVALID;
/// Error code used when an OWNER unlock attempt fails without exposing a finer cryptographic oracle.
pub use self::error::ERROR_CODE_OWNER_UNLOCK_FAILED;
/// Error code used when a Wallet signing operation fails.
pub use self::error::ERROR_CODE_SIGNATURE_FAILED;
/// Error code used when an import/export transfer format is unsupported.
pub use self::error::ERROR_CODE_TRANSFER_FORMAT_UNSUPPORTED;
/// Error code used when a VIEW unlock attempt fails without exposing a finer cryptographic oracle.
pub use self::error::ERROR_CODE_VIEW_UNLOCK_FAILED;
/// Minimal non-secret information available while a native Wallet remains locked.
pub use self::metadata::LockedWalletInfo;
/// Safe metadata projection produced after VIEW or OWNER authorization.
pub use self::metadata::WalletInfo;
/// One protected Wallet note exposed only after authorization.
pub use self::metadata::WalletNote;
/// Authorized OWNER capability handle.
pub use self::owner::WalletOwner;
/// Owned OWNER password material with redacted diagnostics and drop-time zeroization.
pub use self::password::OwnerPassword;
/// Owned VIEW password material with redacted diagnostics and drop-time zeroization.
pub use self::password::ViewPassword;
/// Authorized VIEW capability handle.
pub use self::view::WalletView;
/// Wallet-owned tracing target used by the KSP logging facade.
pub(crate) use self::constants::TRACING_TARGET;

View File

@@ -0,0 +1,109 @@
// file: crates/ksp-wallet-lib/src/metadata.rs
// version: 1
/// One protected Wallet note exposed only after VIEW or OWNER authorization.
#[derive(Clone, Eq, PartialEq)]
pub struct WalletNote {
id: std::string::String,
text: std::string::String,
}
impl WalletNote {
/// Returns the stable note identifier used by Wallet administration operations.
#[must_use]
pub fn id(&self) -> &str {
return self.id.as_str();
}
/// Returns the protected note text after authorization.
#[must_use]
pub fn text(&self) -> &str {
return self.text.as_str();
}
}
impl std::fmt::Debug for WalletNote {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter.debug_struct("WalletNote").field("id", &"<redacted>").field("text", &"<redacted>").finish();
}
}
/// Safe metadata projection produced after VIEW or OWNER authorization.
#[derive(Clone, Eq, PartialEq)]
pub struct WalletInfo {
format_version: u32,
capability: crate::WalletCapability,
pubkey: ksp_core_lib::Pubkey,
alias: std::option::Option<std::string::String>,
notes: std::vec::Vec<crate::WalletNote>,
}
impl WalletInfo {
/// Returns the native Wallet format version parsed for this projection.
#[must_use]
pub const fn format_version(&self) -> u32 {
return self.format_version;
}
/// Returns the authorization capability that produced this projection.
#[must_use]
pub const fn capability(&self) -> crate::WalletCapability {
return self.capability;
}
/// Returns the authorized Solana public key using the KSP Core re-export.
#[must_use]
pub const fn pubkey(&self) -> &ksp_core_lib::Pubkey {
return &self.pubkey;
}
/// Returns the protected internal alias after authorization, when present.
#[must_use]
pub fn alias(&self) -> std::option::Option<&str> {
return self.alias.as_deref();
}
/// Returns the protected Wallet notes after authorization.
#[must_use]
pub fn notes(&self) -> &[crate::WalletNote] {
return self.notes.as_slice();
}
}
impl std::fmt::Debug for WalletInfo {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter
.debug_struct("WalletInfo")
.field("format_version", &self.format_version)
.field("capability", &self.capability)
.field("pubkey", &self.pubkey)
.field("alias", &self.alias.as_ref().map(|_| "<redacted>"))
.field("note_count", &self.notes.len())
.finish();
}
}
/// Minimal non-secret information available while a native Wallet remains locked.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct LockedWalletInfo {
format_version: u32,
view_enabled: bool,
}
impl LockedWalletInfo {
/// Returns the native Wallet format version.
#[must_use]
pub const fn format_version(&self) -> u32 {
return self.format_version;
}
/// Reports whether a VIEW capability slot exists without exposing its protected contents.
#[must_use]
pub const fn view_enabled(&self) -> bool {
return self.view_enabled;
}
}
#[cfg(test)]
#[path = "../unit_tests/metadata.rs"]
mod tests;

View File

@@ -0,0 +1,54 @@
// file: crates/ksp-wallet-lib/src/owner.rs
// version: 1
/// Authorized OWNER capability handle.
///
/// OWNER exposes all authorized metadata and will receive signing plus Wallet administration operations in later `0.2.5` tranches without exposing a
/// general-purpose secret-key getter.
pub struct WalletOwner {
info: crate::WalletInfo,
}
impl WalletOwner {
/// Returns the authorization capability represented by this handle.
#[must_use]
pub const fn capability(&self) -> crate::WalletCapability {
return crate::WalletCapability::Owner;
}
/// Returns the authorized Solana public key.
#[must_use]
pub const fn pubkey(&self) -> &ksp_core_lib::Pubkey {
return self.info.pubkey();
}
/// Returns the protected internal alias, when present.
#[must_use]
pub fn alias(&self) -> std::option::Option<&str> {
return self.info.alias();
}
/// Returns the protected notes.
#[must_use]
pub fn notes(&self) -> &[crate::WalletNote] {
return self.info.notes();
}
/// Returns the complete safe metadata projection for this OWNER capability.
#[must_use]
pub fn info(&self) -> &crate::WalletInfo {
ksp_logging_lib::trace!(
target: crate::TRACING_TARGET,
operation = "wallet_info_projection",
capability = "owner",
"wallet metadata projection requested"
);
return &self.info;
}
}
impl std::fmt::Debug for WalletOwner {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter.debug_struct("WalletOwner").field("info", &self.info).finish();
}
}

View File

@@ -0,0 +1,74 @@
// file: crates/ksp-wallet-lib/src/password.rs
// version: 1
/// Owned VIEW password material.
///
/// The value is never exposed through `Debug` or `Display`, is intentionally non-`Clone`, and is zeroized on drop. Consumers should move an existing
/// `String` into this type instead of keeping unnecessary clear-text copies.
///
/// ```compile_fail
/// let password = ksp_wallet_lib::ViewPassword::new(std::string::String::from("test-only"));
/// let duplicated = password.clone();
/// let _ = duplicated;
/// ```
pub struct ViewPassword {
value: std::string::String,
}
impl ViewPassword {
/// Takes ownership of VIEW password material.
#[must_use]
pub fn new(value: std::string::String) -> Self {
return Self { value };
}
}
impl std::fmt::Debug for ViewPassword {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter.write_str("ViewPassword(<redacted>)");
}
}
impl std::ops::Drop for ViewPassword {
fn drop(&mut self) {
zeroize::Zeroize::zeroize(&mut self.value);
}
}
/// Owned OWNER password material.
///
/// The value is never exposed through `Debug` or `Display`, is intentionally non-`Clone`, and is zeroized on drop. Consumers should move an existing
/// `String` into this type instead of keeping unnecessary clear-text copies.
///
/// ```compile_fail
/// let password = ksp_wallet_lib::OwnerPassword::new(std::string::String::from("test-only"));
/// let duplicated = password.clone();
/// let _ = duplicated;
/// ```
pub struct OwnerPassword {
value: std::string::String,
}
impl OwnerPassword {
/// Takes ownership of OWNER password material.
#[must_use]
pub fn new(value: std::string::String) -> Self {
return Self { value };
}
}
impl std::fmt::Debug for OwnerPassword {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter.write_str("OwnerPassword(<redacted>)");
}
}
impl std::ops::Drop for OwnerPassword {
fn drop(&mut self) {
zeroize::Zeroize::zeroize(&mut self.value);
}
}
#[cfg(test)]
#[path = "../unit_tests/password.rs"]
mod tests;

View File

@@ -0,0 +1,54 @@
// file: crates/ksp-wallet-lib/src/view.rs
// version: 1
/// Authorized VIEW capability handle.
///
/// VIEW exposes protected metadata and, in later `0.2.5` tranches, will expose only self-rotation of its VIEW password. It never owns the Solana secret or
/// OWNER administration material.
pub struct WalletView {
info: crate::WalletInfo,
}
impl WalletView {
/// Returns the authorization capability represented by this handle.
#[must_use]
pub const fn capability(&self) -> crate::WalletCapability {
return crate::WalletCapability::View;
}
/// Returns the authorized Solana public key.
#[must_use]
pub const fn pubkey(&self) -> &ksp_core_lib::Pubkey {
return self.info.pubkey();
}
/// Returns the protected internal alias, when present.
#[must_use]
pub fn alias(&self) -> std::option::Option<&str> {
return self.info.alias();
}
/// Returns the protected notes.
#[must_use]
pub fn notes(&self) -> &[crate::WalletNote] {
return self.info.notes();
}
/// Returns the complete safe metadata projection for this VIEW capability.
#[must_use]
pub fn info(&self) -> &crate::WalletInfo {
ksp_logging_lib::trace!(
target: crate::TRACING_TARGET,
operation = "wallet_info_projection",
capability = "view",
"wallet metadata projection requested"
);
return &self.info;
}
}
impl std::fmt::Debug for WalletView {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter.debug_struct("WalletView").field("info", &self.info).finish();
}
}

View File

@@ -0,0 +1,59 @@
// file: crates/ksp-wallet-lib/tests/dependency_boundary.rs
// version: 1
//! Wallet-specific dependency and ownership canaries.
fn crate_root() -> std::path::PathBuf {
return std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
}
fn rust_source_files(directory: &std::path::Path) -> std::vec::Vec<std::path::PathBuf> {
let entries = std::fs::read_dir(directory).expect("Wallet source directory must be readable during integration tests");
let mut files = std::vec::Vec::new();
for entry in entries {
let entry = entry.expect("Wallet source directory entry must be readable");
let path = entry.path();
if path.is_dir() {
files.extend(rust_source_files(path.as_path()));
} else if path.extension() == std::option::Option::Some(std::ffi::OsStr::new("rs")) {
files.push(path);
}
}
return files;
}
#[test]
fn wallet_manifest_preserves_dependency_firewall() {
let manifest = std::fs::read_to_string(crate_root().join("Cargo.toml")).expect("Wallet manifest must be readable during integration tests");
assert!(manifest.contains("ksp-core-lib"));
assert!(manifest.contains("ksp-logging-lib"));
assert!(manifest.contains("zeroize.workspace = true"));
for forbidden in [
"ksp-config-lib",
"ksp-onchain-transport-lib",
"ksp-execution-policy-api",
"ksp-store-api",
"ksp-store-lib",
"tauri",
"tracing =",
"solana-pubkey",
] {
assert!(!manifest.contains(forbidden), "forbidden direct Wallet dependency detected: {forbidden}");
}
}
#[test]
fn wallet_sources_use_core_pubkey_logging_facade_and_no_environment() {
let source_files = rust_source_files(crate_root().join("src").as_path());
let mut all_source = std::string::String::new();
for source_file in source_files {
let source = std::fs::read_to_string(source_file.as_path()).expect("Wallet Rust source must be readable during integration tests");
all_source.push_str(source.as_str());
}
assert!(all_source.contains("ksp_core_lib::Pubkey"));
assert!(all_source.contains("ksp_logging_lib::trace!"));
assert!(all_source.contains("pub(crate) const TRACING_TARGET: &str = \"ksp-wallet-lib\";"));
assert!(!all_source.contains("solana_pubkey::"));
assert!(!all_source.contains("std::env::"));
assert!(!all_source.contains("tracing::"));
}

View File

@@ -0,0 +1,61 @@
// file: crates/ksp-wallet-lib/tests/public_api.rs
// version: 1
//! Public API canaries for the Wallet foundation.
fn accepts_view_handle(_: std::option::Option<&ksp_wallet_lib::WalletView>) {}
fn accepts_owner_handle(_: std::option::Option<&ksp_wallet_lib::WalletOwner>) {}
#[test]
fn capability_and_handle_types_are_available_from_crate_root() {
assert_eq!(ksp_wallet_lib::WalletCapability::View, ksp_wallet_lib::WalletCapability::View);
assert_ne!(ksp_wallet_lib::WalletCapability::View, ksp_wallet_lib::WalletCapability::Owner);
accepts_view_handle(std::option::Option::None);
accepts_owner_handle(std::option::Option::None);
}
#[test]
fn password_types_redact_public_debug_output() {
let view = ksp_wallet_lib::ViewPassword::new(std::string::String::from("PUBLIC-VIEW-CANARY"));
let owner = ksp_wallet_lib::OwnerPassword::new(std::string::String::from("PUBLIC-OWNER-CANARY"));
let view_debug = format!("{view:?}");
let owner_debug = format!("{owner:?}");
assert!(!view_debug.contains("PUBLIC-VIEW-CANARY"));
assert!(!owner_debug.contains("PUBLIC-OWNER-CANARY"));
assert!(std::mem::needs_drop::<ksp_wallet_lib::ViewPassword>());
assert!(std::mem::needs_drop::<ksp_wallet_lib::OwnerPassword>());
}
#[test]
fn metadata_projection_methods_use_core_pubkey_contract() {
let pubkey_method: fn(&ksp_wallet_lib::WalletInfo) -> &ksp_core_lib::Pubkey = ksp_wallet_lib::WalletInfo::pubkey;
let view_pubkey_method: fn(&ksp_wallet_lib::WalletView) -> &ksp_core_lib::Pubkey = ksp_wallet_lib::WalletView::pubkey;
let owner_pubkey_method: fn(&ksp_wallet_lib::WalletOwner) -> &ksp_core_lib::Pubkey = ksp_wallet_lib::WalletOwner::pubkey;
let _ = pubkey_method;
let _ = view_pubkey_method;
let _ = owner_pubkey_method;
}
#[test]
fn wallet_error_codes_are_available_from_crate_root() {
let codes = [
ksp_wallet_lib::ERROR_CODE_FORMAT_INVALID,
ksp_wallet_lib::ERROR_CODE_FORMAT_VERSION_UNSUPPORTED,
ksp_wallet_lib::ERROR_CODE_CRYPTO_PARAMETERS_INVALID,
ksp_wallet_lib::ERROR_CODE_AUTHENTICATION_FAILED,
ksp_wallet_lib::ERROR_CODE_VIEW_UNLOCK_FAILED,
ksp_wallet_lib::ERROR_CODE_OWNER_UNLOCK_FAILED,
ksp_wallet_lib::ERROR_CODE_CAPABILITY_INSUFFICIENT,
ksp_wallet_lib::ERROR_CODE_IO_FAILED,
ksp_wallet_lib::ERROR_CODE_DESTINATION_EXISTS,
ksp_wallet_lib::ERROR_CODE_ATOMIC_PERSISTENCE_FAILED,
ksp_wallet_lib::ERROR_CODE_TRANSFER_FORMAT_UNSUPPORTED,
ksp_wallet_lib::ERROR_CODE_KEY_MATERIAL_INVALID,
ksp_wallet_lib::ERROR_CODE_SIGNATURE_FAILED,
];
assert_eq!(codes.len(), 13);
for code in codes {
assert_eq!(code.domain(), "wallet");
}
}

View File

@@ -0,0 +1,9 @@
// file: crates/ksp-wallet-lib/unit_tests/capability.rs
// version: 1
#[test]
fn capability_variants_are_distinct_and_stable() {
assert_ne!(super::WalletCapability::View, super::WalletCapability::Owner);
assert_eq!(format!("{:?}", super::WalletCapability::View), "View");
assert_eq!(format!("{:?}", super::WalletCapability::Owner), "Owner");
}

View File

@@ -0,0 +1,56 @@
// file: crates/ksp-wallet-lib/unit_tests/metadata.rs
// version: 1
fn test_pubkey() -> ksp_core_lib::Pubkey {
return ksp_core_lib::PRGIDPK_SOLANA_SYSTEM;
}
#[test]
fn authorized_info_exposes_metadata_after_authorization() {
let note = super::WalletNote { id: std::string::String::from("purpose"), text: std::string::String::from("devnet test wallet") };
let info = super::WalletInfo {
format_version: 1,
capability: crate::WalletCapability::View,
pubkey: test_pubkey(),
alias: std::option::Option::Some(std::string::String::from("devnet-owner")),
notes: std::vec![note],
};
assert_eq!(info.format_version(), 1);
assert_eq!(info.capability(), crate::WalletCapability::View);
assert_eq!(info.pubkey(), &test_pubkey());
assert_eq!(info.alias(), std::option::Option::Some("devnet-owner"));
assert_eq!(info.notes()[0].id(), "purpose");
assert_eq!(info.notes()[0].text(), "devnet test wallet");
}
#[test]
fn authorized_info_debug_redacts_alias_and_note_text() {
let note = super::WalletNote {
id: std::string::String::from("NOTE-ID-CANARY"),
text: std::string::String::from("NOTE-SECRET-CANARY"),
};
let info = super::WalletInfo {
format_version: 1,
capability: crate::WalletCapability::Owner,
pubkey: test_pubkey(),
alias: std::option::Option::Some(std::string::String::from("ALIAS-SECRET-CANARY")),
notes: std::vec![note],
};
let rendered = format!("{info:?}");
assert!(rendered.contains("<redacted>"));
assert!(rendered.contains("note_count"));
assert!(!rendered.contains("ALIAS-SECRET-CANARY"));
assert!(!rendered.contains("NOTE-ID-CANARY"));
assert!(!rendered.contains("NOTE-SECRET-CANARY"));
}
#[test]
fn locked_info_does_not_carry_authorized_identity_or_metadata() {
let info = super::LockedWalletInfo { format_version: 1, view_enabled: true };
assert_eq!(info.format_version(), 1);
assert!(info.view_enabled());
let rendered = format!("{info:?}");
assert!(!rendered.contains("Pubkey"));
assert!(!rendered.contains("alias"));
assert!(!rendered.contains("notes"));
}

View File

@@ -0,0 +1,24 @@
// file: crates/ksp-wallet-lib/unit_tests/password.rs
// version: 1
#[test]
fn view_password_debug_is_redacted() {
let password = super::ViewPassword::new(std::string::String::from("VIEW-SECRET-CANARY"));
let rendered = format!("{password:?}");
assert_eq!(rendered, "ViewPassword(<redacted>)");
assert!(!rendered.contains("VIEW-SECRET-CANARY"));
}
#[test]
fn owner_password_debug_is_redacted() {
let password = super::OwnerPassword::new(std::string::String::from("OWNER-SECRET-CANARY"));
let rendered = format!("{password:?}");
assert_eq!(rendered, "OwnerPassword(<redacted>)");
assert!(!rendered.contains("OWNER-SECRET-CANARY"));
}
#[test]
fn password_wrappers_require_drop_for_zeroization() {
assert!(std::mem::needs_drop::<super::ViewPassword>());
assert!(std::mem::needs_drop::<super::OwnerPassword>());
}

365
deltas/0.2.5/pre.002.md Normal file
View File

@@ -0,0 +1,365 @@
<!-- file: deltas/0.2.5/pre.002.md -->
<!-- version: 1 -->
# Delta `0.2.5-pre.002` — foundation crate/capabilities/passwords/errors/logging
## Base requise
```text
release : 0.2.5
prerelease : pre.002
identifiant de commit attendu : v0.2.5-pre.002
workspace.package.version : 0.2.5-pre.2
base : v0.2.5-pre.001-fix.001
```
Le delta `pre.001-fix.001` est appliqué avant cette tranche. Les deltas historiques `pre.001.md` et `pre.001-fix.001.md` restent inchangés.
## Objectif
Créer la foundation Rust de `ksp-wallet-lib` sans commencer encore le codec `.kspwallet`, le KDF/AEAD, la persistence, la keypair Solana ou la signature.
Cette tranche matérialise uniquement :
```text
crate + frontières Cargo
WalletCapability
WalletView / WalletOwner opaques
LockedWalletInfo / WalletInfo / WalletNote
ViewPassword / OwnerPassword
codes d'erreur Wallet
contrat de logging KSP
canaries public API / dependency firewall
```
Les opérations `create/open/rotate/sign/import/export` restent volontairement absentes tant que leurs invariants cryptographiques/persistence ne sont pas implémentés dans les tranches prévues.
## Version Cargo
Conformément à `VER-ID-009` :
```text
0.2.5-pre.1 -> 0.2.5-pre.2
```
Toutes les crates membres continuent d'hériter `version.workspace = true`.
## Workspace et dépendances
Nouveau membre :
```text
crates/ksp-wallet-lib
```
Dépendances directes :
```text
ksp-core-lib
ksp-logging-lib
zeroize.workspace = true
```
`zeroize` est centralisé dans `[workspace.dependencies]` :
```toml
zeroize = { version = "^1.9" }
```
La génération courante `zeroize 1.9.0` a été réauditée avant insertion. La crate est utilisée immédiatement pour nettoyer au `Drop` les buffers `String` possédés par les wrappers de passwords. Aucun `secrecy`, Argon2, AEAD, CSPRNG, Solana keypair/signer/signature ou codec supplémentaire n'est ajouté par anticipation.
Interdictions confirmées dans le manifest Wallet :
```text
ksp-config-lib
ksp-onchain-transport-lib
ksp-execution-policy-api
ksp-store-api / ksp-store-lib
Tauri
tracing direct
solana-pubkey direct
```
### Ownership de `Pubkey`
`ksp-wallet-lib` ne dépend pas directement de `solana-pubkey`.
Toute surface Wallet utilise :
```text
ksp_core_lib::Pubkey
```
qui est le re-export possédé par `ksp-core-lib`. Les futures crates `solana-keypair`/`solana-signer` ne seront ajoutées que lorsqu'un chemin de compilation les consommera réellement ; leur compatibilité avec la génération de `Pubkey` possédée par Core sera alors contrôlée par compilation et `cargo tree`.
### Aucun ownership Config
Wallet n'a aucune configuration runtime propre et ne lit ni document Config, ni `.env`, ni environnement processus.
Le futur chemin/répertoire de destination d'une création/import sera fourni explicitement par le caller. Une application pourra faire résoudre son propre défaut par Config puis transmettre ce chemin à Wallet sans créer de dépendance inverse Wallet -> Config.
## Capabilities publiques
### `WalletCapability`
Deux rôles uniquement :
```text
View
Owner
```
`View` signifie metadata autorisées plus future rotation de **son propre password VIEW uniquement**.
`Owner` signifie metadata autorisées plus futures capacités de signature/export explicite/administration.
Aucun rôle recovery/hardware/automation n'est ajouté en V1.
### `WalletView`
Handle opaque exposant actuellement uniquement la projection metadata :
```text
capability() -> View
pubkey()
alias()
notes()
info()
```
Il n'expose aucun constructeur public, aucune signature, aucun export secret, aucune mutation alias/notes et aucune administration OWNER.
La future méthode de rotation de son propre password sera ajoutée lorsque le slot VIEW réel et sa persistence existent ; `pre.002` ne simule pas cette opération.
### `WalletOwner`
Handle opaque exposant actuellement la même lecture metadata autorisée :
```text
capability() -> Owner
pubkey()
alias()
notes()
info()
```
Il n'expose encore ni secret ni signature ni méthode d'administration. Les opérations OWNER réelles sont ajoutées avec les matériaux cryptographiques/persistence correspondants dans les tranches suivantes.
### Projections metadata
`LockedWalletInfo` contient uniquement :
```text
format_version
view_enabled
```
Il ne contient aucune Pubkey, alias ou note.
`WalletInfo` contient après autorisation :
```text
format_version
capability
ksp_core_lib::Pubkey
alias optionnel
notes
```
`WalletNote` expose un identifiant stable et un texte protégé après autorisation.
Les constructors de ces projections ne sont pas publics : elles sont destinées à être produites par Wallet, pas à fabriquer une capability.
Les `Debug` de `WalletInfo`/`WalletNote` ne rendent pas alias, identifiant de note ou contenu de note. La Pubkey peut apparaître dans `WalletInfo::Debug` car cette projection n'existe qu'après autorisation et la Pubkey n'est pas un secret blockchain ; Wallet verrouillé ne possède pas cette projection.
## Password wrappers
Deux types distincts :
```text
ViewPassword
OwnerPassword
```
Ils :
- prennent ownership d'un `String` ;
- n'implémentent ni `Clone` ni `Copy` ;
- n'implémentent pas `Display` ;
- exposent un `Debug` strictement redacted ;
- implémentent un `Drop` appelant `zeroize::Zeroize` sur le buffer possédé ;
- n'exposent aucun getter public du password en clair.
Deux doctests `compile_fail` canaris interdisent la régression vers `Clone`.
Cette zeroization reste une hygiène best-effort sur le buffer possédé et ne prétend pas effacer des copies antérieures conservées par le caller, l'allocateur, les registres ou d'autres couches du runtime.
## Erreurs
Le domaine stable est :
```text
wallet
```
Les 13 codes réservés/stabilisés dans cette foundation sont :
```text
format_invalid
format_version_unsupported
crypto_parameters_invalid
authentication_failed
view_unlock_failed
owner_unlock_failed
capability_insufficient
io_failed
destination_exists
atomic_persistence_failed
transfer_format_unsupported
key_material_invalid
signature_failed
```
Ils utilisent exclusivement `ksp_core_lib::ErrorCode`. Aucun type d'erreur parallèle n'est créé.
Les codes de déverrouillage restent volontairement orientés rôle/opération afin que l'implémentation crypto future puisse éviter de distinguer publiquement password incorrect, unwrap incorrect et tag AEAD incorrect lorsqu'une telle distinction créerait un oracle inutile.
## Logging
Le target Wallet est possédé explicitement par :
```text
crates/ksp-wallet-lib/src/constants.rs
```
avec :
```text
pub(crate) const TRACING_TARGET: &str = "ksp-wallet-lib";
```
Les émissions utilisent exclusivement `ksp_logging_lib::*` et jamais `tracing` directement ni `env!("CARGO_PKG_NAME")` comme target.
La foundation instrumente seulement la demande explicite de projection `info()` au niveau `trace`, avec :
```text
operation=wallet_info_projection
capability=view|owner
```
Aucun alias, note, password, secret, payload arbitraire ou path n'est loggé.
## Tests ajoutés
La tranche définit **13 tests Rust déterministes** plus **2 doctests `compile_fail`** :
- variants `WalletCapability` ;
- redaction `Debug` des deux password wrappers ;
- présence d'un `Drop` pour les wrappers secrets ;
- projection metadata autorisée ;
- redaction alias/note dans `Debug` ;
- projection locked sans identité/metadata ;
- canary crate-root pour `WalletView`/`WalletOwner`/`WalletCapability` ;
- canary crate-root pour les wrappers de password ;
- signatures de méthodes Pubkey typées en `ksp_core_lib::Pubkey` ;
- disponibilité des 13 codes d'erreur depuis le crate root ;
- manifest firewall Wallet ;
- source ownership canary Core Pubkey / Logging facade / absence env direct.
Les tests d'intégration de firewall restent spécifiques à `ksp-wallet-lib`; aucun nouveau canary général du workspace n'est déplacé arbitrairement dans cette crate.
## Documentation synchronisée
Le plan Wallet est passé en version 5 pour :
- figer `ksp_core_lib::Pubkey` comme seule frontière Pubkey de Wallet ;
- corriger le target de logging vers `ksp-wallet-lib` dans `src/constants.rs` conformément à `DEP-LOG-010` ;
- enregistrer `zeroize ^1.9` comme seul ajout tiers de `pre.002` ;
- repousser les crates Solana directes à leur premier usage réel ;
- confirmer qu'aucun répertoire par défaut n'appartient à Wallet et que le path sera fourni par le caller ;
- pointer la suite immédiate sur `pre.003`.
`ROADMAP.md` et `docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md` enregistrent la matérialisation de la foundation sans recopier le détail des futures prereleases.
## Fichiers ajoutés
```text
crates/ksp-wallet-lib/Cargo.toml
crates/ksp-wallet-lib/src/capability.rs
crates/ksp-wallet-lib/src/constants.rs
crates/ksp-wallet-lib/src/error.rs
crates/ksp-wallet-lib/src/lib.rs
crates/ksp-wallet-lib/src/metadata.rs
crates/ksp-wallet-lib/src/owner.rs
crates/ksp-wallet-lib/src/password.rs
crates/ksp-wallet-lib/src/view.rs
crates/ksp-wallet-lib/unit_tests/capability.rs
crates/ksp-wallet-lib/unit_tests/metadata.rs
crates/ksp-wallet-lib/unit_tests/password.rs
crates/ksp-wallet-lib/tests/dependency_boundary.rs
crates/ksp-wallet-lib/tests/public_api.rs
deltas/0.2.5/pre.002.md
```
## Fichiers modifiés
```text
Cargo.toml
ROADMAP.md
docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md
docs/plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md
```
## Fichiers supprimés
Aucun.
## Validations exécutées dans l'environnement de préparation
Contrôles statiques réellement exécutés :
- parsing TOML du `Cargo.toml` racine et du manifest Wallet ;
- comparaison de l'arbre avec la base `pre.001-fix.001` ;
- inventaire exact des fichiers ajoutés/modifiés ;
- scan des sources Wallet : aucun `use` statement ;
- scan production : aucun `unwrap`, `expect`, `panic`, opérateur `?` ou bloc `unsafe` ajouté ;
- manifest firewall : absence Config/Transport/ExecutionPolicy/Store/Tauri/tracing/solana-pubkey directs ;
- source ownership : `ksp_core_lib::Pubkey`, `ksp_logging_lib::trace!`, target explicite `ksp-wallet-lib`, aucun `solana_pubkey::`, `tracing::` ou `std::env::` ;
- contrôle des headers `file:` / `version:` des fichiers ajoutés/modifiés ;
- contrôle de l'archive d'échange contre `VER-ARCHIVE-004` avant livraison.
## Validations non exécutées
L'environnement de préparation ne fournit pas la toolchain Rust/Cargo. Les validations suivantes doivent donc être exécutées par l'opérateur après application du delta :
```bash
cargo fmt --all
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-wallet-lib
```
Un `cargo tree` complet des primitives Solana/crypto n'est pas encore pertinent : aucune primitive Solana/keypair/KDF/AEAD nouvelle n'est ajoutée en `pre.002`. `zeroize` doit néanmoins apparaître sans duplication injustifiée lors du contrôle Cargo opérateur.
## Décisions prises
- `Pubkey` Wallet est exclusivement `ksp_core_lib::Pubkey`; aucune dépendance directe `solana-pubkey` dans Wallet.
- Logging Wallet utilise exclusivement `ksp-logging-lib` et `TRACING_TARGET = "ksp-wallet-lib"` dans `src/constants.rs`.
- Wallet ne dépend pas de Config et ne choisira pas lui-même un répertoire par défaut ; le caller fournit le path.
- `zeroize ^1.9` est le seul nouveau tiers de `pre.002`.
- Les password wrappers sont distincts OWNER/VIEW, owned, redacted, non-Clone et zeroized au drop.
- Les handles VIEW/OWNER restent opaques et ne simulent aucune opération cryptographique non encore implémentée.
- VIEW conserve comme contrat futur la seule mutation de son propre password ; OWNER conserve les futures rotations OWNER/VIEW et l'administration complète.
## Questions ouvertes
Aucune question bloquante pour `pre.003`.
Restent volontairement à décider/figer dans leurs tranches prévues :
- wire exact JSON/key slots/transcript/AAD (`pre.003`) ;
- paramètres Argon2 benchmarkés et crypto effective (`pre.004`) ;
- matériaux owner-control/metadata/secret et state signature (`pre.005`) ;
- persistence async/atomique/no-clobber (`pre.006`) ;
- keypair/signature et rotations réelles (`pre.007`) ;
- adapters import/export (`pre.008`).

View File

@@ -1,5 +1,5 @@
<!-- file: docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md --> <!-- file: docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md -->
<!-- version: 46 --> <!-- version: 47 -->
# Séquence des releases fonctionnelles KSP # Séquence des releases fonctionnelles KSP
@@ -408,6 +408,8 @@ Le format V1 est cadré comme JSON UTF-8 strict avec binary Base64url sans paddi
La release doit fournir `docs/formats/KSPWALLET_V1.md` comme spécification séparée et indépendante de Rust, accompagnée de vecteurs publics auto-contenus permettant une réimplémentation dans un autre langage. La prévision est étendue jusqu'à `pre.010` afin de séparer codec, crypto, capabilities, persistence, administration, import/export, security audit et documentation interopérable. Le plan actif est `docs/plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md`. La release doit fournir `docs/formats/KSPWALLET_V1.md` comme spécification séparée et indépendante de Rust, accompagnée de vecteurs publics auto-contenus permettant une réimplémentation dans un autre langage. La prévision est étendue jusqu'à `pre.010` afin de séparer codec, crypto, capabilities, persistence, administration, import/export, security audit et documentation interopérable. Le plan actif est `docs/plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md`.
`0.2.5-pre.002` matérialise la crate sans ouvrir encore le codec ou la cryptographie du fichier : `WalletView`/`WalletOwner`, `WalletCapability`, projections `LockedWalletInfo`/`WalletInfo`/`WalletNote`, wrappers `ViewPassword`/`OwnerPassword`, codes derreur Wallet et target de logging explicite `ksp-wallet-lib`. La crate dépend seulement de Core, Logging et `zeroize`; elle consomme la Pubkey exclusivement via `ksp_core_lib::Pubkey` et ne dépend directement ni de `solana-pubkey`, ni de Config, Transport, ExecutionPolicy, Store ou Tauri. Les primitives Solana keypair/signature ne seront ajoutées que lorsquelles seront réellement consommées.
## `0.2.6` — Wallet Desk ## `0.2.6` — Wallet Desk
Mission : valider Config composite + `.kspwallet` + transport HTTP dans une application Tauri mince. Mission : valider Config composite + `.kspwallet` + transport HTTP dans une application Tauri mince.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md --> <!-- file: docs/plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md -->
<!-- version: 4 --> <!-- version: 5 -->
# Plan `0.2.5` — Wallet foundation # Plan `0.2.5` — Wallet foundation
@@ -77,7 +77,8 @@ Clarifications confirmées pendant la revue du gate :
27. **OWNER peut changer son propre password OWNER et le password VIEW**, sans connaître l'ancien password VIEW ; OWNER reste seul capable de l'administration metadata et des key slots au-delà du self-service de rotation VIEW ; 27. **OWNER peut changer son propre password OWNER et le password VIEW**, sans connaître l'ancien password VIEW ; OWNER reste seul capable de l'administration metadata et des key slots au-delà du self-service de rotation VIEW ;
28. la keypair Solana est **immuable dans un wallet V1 après création/import**. V1 ne fournit pas d'opération de remplacement de keypair ; une autre keypair crée un autre wallet ; 28. la keypair Solana est **immuable dans un wallet V1 après création/import**. V1 ne fournit pas d'opération de remplacement de keypair ; une autre keypair crée un autre wallet ;
29. les permissions/ACL du système de fichiers ne constituent **ni une garantie cryptographique ni une responsabilité de sécurité de `ksp-wallet-lib`**. Le contrat Wallet porte sur les capacités : sans OWNER, aucune signature Solana, aucun export secret, aucune écriture acceptée de Pubkey/alias/notes et aucune mutation OWNER-controlled ; la seule mutation volontairement accordée à VIEW est la rotation de son propre password ; 29. les permissions/ACL du système de fichiers ne constituent **ni une garantie cryptographique ni une responsabilité de sécurité de `ksp-wallet-lib`**. Le contrat Wallet porte sur les capacités : sans OWNER, aucune signature Solana, aucun export secret, aucune écriture acceptée de Pubkey/alias/notes et aucune mutation OWNER-controlled ; la seule mutation volontairement accordée à VIEW est la rotation de son propre password ;
30. tout import produit un **nouveau `.kspwallet`** selon la sémantique no-clobber de `create`. Un import ne remplace, n'écrase et ne transforme jamais un `.kspwallet` existant. 30. tout import produit un **nouveau `.kspwallet`** selon la sémantique no-clobber de `create`. Un import ne remplace, n'écrase et ne transforme jamais un `.kspwallet` existant ;
31. `ksp-wallet-lib` ne possède aucun « répertoire Wallet par défaut » ni configuration correspondante. Les opérations de persistence reçoivent un chemin/répertoire explicite du caller ; une future application peut faire résoudre son propre défaut par Config puis transmettre le chemin à Wallet, sans créer de dépendance Wallet -> Config.
## 4. Réaudit de l'héritage bot2/bot3 ## 4. Réaudit de l'héritage bot2/bot3
@@ -368,16 +369,16 @@ Aucune dépendance ci-dessous n'est ajoutée dans `pre.001`. Ce sont les candida
### 8.1 Solana low-level ### 8.1 Solana low-level
| Besoin | Crate publiée auditée | Décision candidate | | Besoin | Crate publiée auditée | Décision candidate |
|------------------|--------------------------|--------------------------------------------------------| |------------------|--------------------------|---------------------------------------------------------------------------------------------|
| Pubkey | `solana-pubkey 4.3.0` | déjà workspace, conserver | | Pubkey | `solana-pubkey 4.3.0` | propriété Core ; Wallet consomme uniquement `ksp_core_lib::Pubkey`, sans dépendance directe |
| keypair concret | `solana-keypair 3.1.2` | retenir, features minimales | | keypair concret | `solana-keypair 3.1.2` | retenir, features minimales |
| interface signer | `solana-signer 3.0.1` | retenir si contrat public/impl l'exige | | interface signer | `solana-signer 3.0.1` | retenir si contrat public/impl l'exige |
| signature | `solana-signature 3.5.2` | dépendance directe seulement si le type public l'exige | | signature | `solana-signature 3.5.2` | dépendance directe seulement si le type public l'exige |
`solana-keypair` fournit le keypair Ed25519, la conversion stricte depuis 64 octets, la signature, le format JSON de 64 entiers et une représentation Base58 complète. Aucun client RPC Solana n'est nécessaire. `solana-keypair` fournit le keypair Ed25519, la conversion stricte depuis 64 octets, la signature, le format JSON de 64 entiers et une représentation Base58 complète. Aucun client RPC Solana n'est nécessaire.
La génération SDK récente définit `Pubkey` comme alias du type `Address`, mais l'écosystème a déjà connu des incompatibilités lorsque plusieurs générations de `solana-address` coexistent. `pre.002` doit donc prouver par compilation/cargo-tree que le `Pubkey` possédé par `ksp-core-lib` et le `Address/Pubkey` exposé par `solana-keypair` sont de la même génération avant de figer les signatures publiques Wallet. Aucun second type d'adresse KSP n'est introduit pour contourner un mismatch. La génération SDK récente définit `Pubkey` comme alias du type `Address`, mais l'écosystème a déjà connu des incompatibilités lorsque plusieurs générations de `solana-address` coexistent. `pre.002` verrouille donc la surface publique Wallet sur **`ksp_core_lib::Pubkey` exclusivement** et n'ajoute aucune dépendance directe `solana-pubkey`. Lorsque `solana-keypair` sera réellement introduit, la tranche concernée devra prouver par compilation/cargo-tree que son `Address/Pubkey` est compatible avec la génération possédée par Core avant toute conversion interne. Aucun second type d'adresse KSP n'est introduit pour contourner un mismatch.
Un `cargo tree` est obligatoire lors de l'introduction réelle afin de contrôler les versions `ed25519-dalek`, `solana-signature`, `rand/getrandom` et éviter des duplications évitables. Un `cargo tree` est obligatoire lors de l'introduction réelle afin de contrôler les versions `ed25519-dalek`, `solana-signature`, `rand/getrandom` et éviter des duplications évitables.
@@ -412,7 +413,7 @@ AES-GCM-SIV apporte une meilleure tolérance à la réutilisation accidentelle d
### 8.5 Secret memory ### 8.5 Secret memory
`zeroize 1.9.0` est retenu. `secrecy` n'est pas ajouté tant qu'un besoin ergonomique concret n'est pas démontré ; des types KSP simples peuvent imposer eux-mêmes redaction/non-Clone et utiliser `Zeroize`/`Zeroizing`. `zeroize 1.9.0` est retenu et devient la seule nouvelle dépendance tierce de `pre.002`, car les wrappers `ViewPassword` / `OwnerPassword` lutilisent immédiatement pour leur nettoyage au `Drop`. `secrecy` n'est pas ajouté tant qu'un besoin ergonomique concret n'est pas démontré ; des types KSP simples imposent eux-mêmes redaction/non-Clone et utilisent `zeroize`.
Pour la clé admin Ed25519 distincte, `ed25519-dalek 3.0.0` est une candidate standard, mais son ajout direct est **conditionné au cargo-tree** de la tranche qui implémente l'authentification afin d'éviter une génération concurrente inutile avec celle déjà tirée par `solana-keypair`. Pour la clé admin Ed25519 distincte, `ed25519-dalek 3.0.0` est une candidate standard, mais son ajout direct est **conditionné au cargo-tree** de la tranche qui implémente l'authentification afin d'éviter une génération concurrente inutile avec celle déjà tirée par `solana-keypair`.
@@ -435,7 +436,7 @@ Candidates :
```text ```text
base64 0.23.1 pour Base64url sans padding des champs binaires JSON base64 0.23.1 pour Base64url sans padding des champs binaires JSON
tempfile 3.27.0 pour temp files privés same-directory + persist/persist_noclobber tempfile 3.27.0 pour temp files same-directory + persist/persist_noclobber
``` ```
Elles ne sont ajoutées que lorsqu'elles sont réellement consommées. Elles ne sont ajoutées que lorsqu'elles sont réellement consommées.
@@ -766,10 +767,12 @@ La crate peut donc être utilisée depuis Tauri plus tard sans dépendre de Taur
### 14.1 Baseline portable ### 14.1 Baseline portable
Le chemin de destination est toujours fourni explicitement par le caller ; Wallet ne lit aucune Config ni variable d'environnement pour choisir un répertoire par défaut.
Pour créer/remplacer un `.kspwallet` : Pour créer/remplacer un `.kspwallet` :
1. sérialiser complètement le nouvel état en mémoire bornée ; 1. sérialiser complètement le nouvel état en mémoire bornée ;
2. créer un temp file privé **dans le même répertoire** que la destination ; 2. créer un temp file unique **dans le même répertoire** que la destination ;
3. écrire tout le contenu ; 3. écrire tout le contenu ;
4. `flush`/`sync_all` le temp file ; 4. `flush`/`sync_all` le temp file ;
5. publier avec une primitive no-clobber pour `create` ou replace pour administration ; 5. publier avec une primitive no-clobber pour `create` ou replace pour administration ;
@@ -836,13 +839,13 @@ notes/alias par défaut
## 16. Logging ## 16. Logging
Cible KSP future proposée : Le target principal Wallet est explicitement possédé par `src/constants.rs` conformément à `DEP-LOG-010` :
```text ```text
ksp.wallet pub(crate) const TRACING_TARGET: &str = "ksp-wallet-lib";
``` ```
Via `ksp-logging-lib` uniquement. Toutes les émissions passent par `ksp-logging-lib` uniquement. Wallet ne dépend jamais directement de `tracing` et nutilise pas `env!("CARGO_PKG_NAME")` comme target.
Événements sûrs : Événements sûrs :
@@ -1065,9 +1068,15 @@ Une `fix` ou tranche supplémentaire est préférable à la suppression d'une ga
## 22. Dépendances candidates par tranche ## 22. Dépendances candidates par tranche
Aucune ajoutée en `pre.001`. `pre.001` najoutait aucune dépendance. `pre.002` ajoute uniquement :
Liste de travail, à réauditer juste avant insertion sous `[workspace.dependencies]` : ```text
zeroize ^1.9
```
La dépendance est centralisée sous `[workspace.dependencies]` puis consommée avec `zeroize.workspace = true`. Elle est utilisée immédiatement par les wrappers de password ; aucune crate crypto/KDF/Solana supplémentaire nest ajoutée par anticipation. La génération courante `zeroize 1.9.0` a été réauditée avant insertion.
Liste de travail restante, à réauditer juste avant insertion sous `[workspace.dependencies]` :
```text ```text
argon2 ^0.5 argon2 ^0.5
@@ -1079,7 +1088,6 @@ solana-keypair ^3.1
solana-signer ^3.0 # si réellement nécessaire directement solana-signer ^3.0 # si réellement nécessaire directement
solana-signature ^3.5 # seulement si le type public l'exige solana-signature ^3.5 # seulement si le type public l'exige
tempfile ^3.27 tempfile ^3.27
zeroize ^1.9
``` ```
Déjà présents et réutilisables : Déjà présents et réutilisables :
@@ -1088,11 +1096,12 @@ Déjà présents et réutilisables :
serde serde
serde_json serde_json
tokio tokio
solana-pubkey ksp-core-lib # propriétaire/réexport de Pubkey pour Wallet
ksp-core-lib
ksp-logging-lib ksp-logging-lib
``` ```
`ksp-wallet-lib` ne déclare pas `solana-pubkey` : toute Pubkey publique ou interne du domaine Wallet passe par `ksp_core_lib::Pubkey`.
Non retenus nativement en V1 : Non retenus nativement en V1 :
```text ```text
@@ -1184,4 +1193,4 @@ Une future `format_version >= 2` pourra réétudier des facteurs/ancrages extern
## 26. Suite immédiate ## 26. Suite immédiate
`0.2.5-pre.002` crée seulement la foundation de `ksp-wallet-lib` : boundaries Cargo, types de capability/password/info, erreurs, façade logging et canaries architecturales. Le codec et la cryptographie du fichier restent à `pre.003+`. `0.2.5-pre.003` est la suite immédiate : codec JSON strict, limites, DTOs denveloppe/key slots, transcript/AAD et première spécification `docs/formats/KSPWALLET_V1.md`. La cryptographie effective KDF/AEAD reste à `pre.004+`.