v0.2.6-pre.006

This commit is contained in:
2026-08-21 10:01:11 +02:00
parent 63e711629f
commit a207cf7161
21 changed files with 1136 additions and 63 deletions

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-wallet-desk/src/wallet_session.rs
// version: 2
// version: 3
//! Durable root-scoped Wallet session lifecycle for Wallet Desk.
@@ -14,6 +14,10 @@ pub(crate) enum WalletSessionStateDto {
NoSelection,
/// One Wallet is selected but remains locked.
Locked,
/// One explicit unlock operation is running in Rust.
PrivilegedOperation,
/// One Wallet is open with VIEW capability.
ViewOpen,
/// One Wallet is open with OWNER capability.
OwnerOpen,
}
@@ -42,7 +46,7 @@ pub(crate) struct WalletNoteDto {
pub(crate) text: String,
}
/// Authorized Wallet projection returned after creation with OWNER capability.
/// Authorized Wallet projection returned after explicit VIEW/OWNER authorization.
#[derive(serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_wallet_desk/wallet_session/WalletAuthorizedDto.ts")]
@@ -51,17 +55,19 @@ pub(crate) struct WalletAuthorizedDto {
pub(crate) alias: std::option::Option<String>,
/// Capability that produced this projection.
pub(crate) capability: String,
/// Number of effective Config-owned Wallet password candidates without their names or values.
pub(crate) configured_secret_candidate_count: usize,
/// Native Wallet filename without its parent path.
pub(crate) filename: String,
/// Native Wallet format version.
pub(crate) format_version: u32,
/// Protected notes available to the OWNER session.
/// Protected notes available to the authorized session.
pub(crate) notes: std::vec::Vec<WalletNoteDto>,
/// Authorized Solana public key.
pub(crate) pubkey: String,
/// Root-scoped identifier accepted by later Wallet Desk operations.
pub(crate) wallet_id: String,
/// Whether the created Wallet has an enabled VIEW slot.
/// Whether the Wallet has an enabled VIEW slot.
pub(crate) view_enabled: bool,
}
@@ -82,7 +88,36 @@ pub(crate) struct WalletCreateRequestDto {
pub(crate) view_password: std::option::Option<String>,
}
/// Backend Wallet session. Full paths and authorized handles never cross IPC.
/// Manual unlock request whose password exists only for the explicit frontend -> Rust call.
#[derive(serde::Deserialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_wallet_desk/wallet_session/WalletUnlockRequestDto.ts")]
pub(crate) struct WalletUnlockRequestDto {
/// Manual password transported only frontend -> Rust.
pub(crate) password: String,
}
/// Capability targeted by one explicit unlock operation.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum WalletUnlockCapability {
/// VIEW metadata-only authorization.
View,
/// OWNER full authorization.
Owner,
}
impl WalletUnlockCapability {
/// Returns a safe lowercase capability label for logs and projections.
#[must_use]
pub(crate) const fn label(self) -> &'static str {
return match self {
Self::View => "view",
Self::Owner => "owner",
};
}
}
/// Backend Wallet session. Full paths, configured secret names and authorized handles never cross IPC.
pub(crate) enum WalletSession {
/// No current Wallet selection.
NoSelection,
@@ -95,6 +130,26 @@ pub(crate) enum WalletSession {
/// Locked information re-inspected from the file.
locked_info: ksp_wallet_lib::LockedWalletInfo,
},
/// One explicit unlock operation currently owns the selected locked state.
PrivilegedOperation {
/// Capability being attempted.
capability: WalletUnlockCapability,
/// Native Wallet filename / root-scoped identifier.
wallet_id: String,
/// Full root-scoped filesystem path retained only in Rust.
path: std::path::PathBuf,
/// Locked information retained so a failed attempt can return to `Locked` without another KDF.
locked_info: ksp_wallet_lib::LockedWalletInfo,
},
/// One Wallet kept open with VIEW capability.
View {
/// Native Wallet filename / root-scoped identifier.
wallet_id: String,
/// Full root-scoped filesystem path retained only in Rust.
path: std::path::PathBuf,
/// Authorized VIEW handle retaining metadata capability only in Rust.
wallet: std::boxed::Box<ksp_wallet_lib::WalletView>,
},
/// One Wallet kept open with OWNER capability.
Owner {
/// Native Wallet filename / root-scoped identifier.
@@ -130,6 +185,16 @@ impl WalletSession {
wallet_id: std::option::Option::Some(wallet_id.clone()),
}
},
Self::PrivilegedOperation { wallet_id, .. } => WalletSessionDto {
filename: std::option::Option::Some(wallet_id.clone()),
state: WalletSessionStateDto::PrivilegedOperation,
wallet_id: std::option::Option::Some(wallet_id.clone()),
},
Self::View { wallet_id, .. } => WalletSessionDto {
filename: std::option::Option::Some(wallet_id.clone()),
state: WalletSessionStateDto::ViewOpen,
wallet_id: std::option::Option::Some(wallet_id.clone()),
},
Self::Owner { wallet_id, .. } => WalletSessionDto {
filename: std::option::Option::Some(wallet_id.clone()),
state: WalletSessionStateDto::OwnerOpen,
@@ -140,8 +205,32 @@ impl WalletSession {
}
/// Converts an OWNER handle into the authorized response DTO without exposing secret key material.
pub(crate) fn owner_projection(wallet_id: &str, view_enabled: bool, owner: &ksp_wallet_lib::WalletOwner) -> WalletAuthorizedDto {
let info = owner.info();
pub(crate) fn owner_projection(
wallet_id: &str,
view_enabled: bool,
configured_secret_candidate_count: usize,
owner: &ksp_wallet_lib::WalletOwner,
) -> WalletAuthorizedDto {
return authorized_projection(wallet_id, view_enabled, configured_secret_candidate_count, "owner", owner.info());
}
/// Converts a VIEW handle into the authorized response DTO without exposing OWNER material.
pub(crate) fn view_projection(
wallet_id: &str,
view_enabled: bool,
configured_secret_candidate_count: usize,
view: &ksp_wallet_lib::WalletView,
) -> WalletAuthorizedDto {
return authorized_projection(wallet_id, view_enabled, configured_secret_candidate_count, "view", view.info());
}
fn authorized_projection(
wallet_id: &str,
view_enabled: bool,
configured_secret_candidate_count: usize,
capability: &str,
info: &ksp_wallet_lib::WalletInfo,
) -> WalletAuthorizedDto {
let notes = info
.notes()
.iter()
@@ -151,7 +240,8 @@ pub(crate) fn owner_projection(wallet_id: &str, view_enabled: bool, owner: &ksp_
.collect();
return WalletAuthorizedDto {
alias: info.alias().map(|value| return value.to_owned()),
capability: "owner".to_owned(),
capability: capability.to_owned(),
configured_secret_candidate_count,
filename: wallet_id.to_owned(),
format_version: info.format_version(),
notes,