85 KiB
Plan 0.2.6 — Wallet Desk
Statut : clôturé par
0.2.6-rel.001. Ce document reste la trace détaillée de construction de Wallet Desk et de.kspwalletV2. La prochaine release active est0.2.7, ouverte parprompts/012-V0_2_7_START_PROMPT.md.
1. Objet et statut de 0.2.6-pre.001
0.2.6 crée crates/ksp-app-wallet-desk, une application Tauri spécialisée et mince qui valide la composition réelle :
ksp-config-lib
+ Config composite
+ ksp-wallet-lib
+ ksp-onchain-transport-lib HTTP
+ ksp-logging-lib
La base auditée est la release stable fournie v0.2.5, avec workspace.package.version = 0.2.5 avant ouverture. La base ne contient encore ni crate ksp-app-wallet-desk, ni document std.wallet, ni composite Wallet Desk.
0.2.6-pre.001 reste une tranche d'audit, conception, inventaire et sizing. Elle ne crée pas encore la crate Tauri ni les fichiers Config runtime. Son rôle est de produire une trajectoire suffisamment détaillée pour que les prereleases suivantes implémentent des objectifs positifs et mesurables sans renégocier les frontières au fil des corrections.
Les validations opérateur du 2026-08-20 ont ensuite confirmé la base pre.001 : cargo fmt --all, python3 scripts/audit_rust_workspace_rules.py, cargo check --workspace et cargo clippy --workspace --all-targets sont verts, sans warning signalé. Le présent plan intègre les corrections documentaires de pre.001-fix.001 puis pre.001-fix.002 sans modifier la version Cargo, car aucun fichier code/build/runtime/configuration exécutable n'est touché. pre.001-fix.002 aligne en particulier le workflow frontend sur la règle KSP : npm direct sert uniquement à installer ou mettre à jour les dépendances ; les cycles dev/build passent exclusivement par Cargo/Tauri.
0.2.6-pre.002 matérialise ensuite la première tranche technique : la crate ksp-app-wallet-desk rejoint le workspace avec le shell splash/main du gabarit Config Desk, le bootstrap Config + Logging commun, les ports 1432/1433, Bootstrap, Font Awesome, DataTables/Select, SimpleBar, resize-observer-polyfill, TS-RS et le bridge frontend -> Rust -> ksp-logging-lib. Cette tranche ne branche encore ni std.wallet, ni inventory filesystem réel, ni ksp-wallet-lib, ni Transport : ces responsabilités restent dans les prereleases déjà dimensionnées.
0.2.6-pre.003 matérialise la composition Config prévue : cfg.std.wallet/schema.std.wallet, le composite cfg.composite.ksp-app-wallet-desk, les profils Wallet default/temporary/tests, ResolvedWalletConfig et les adapters qui peuvent consommer un profil déjà sélectionné par composite sans perdre la provenance Composite. Wallet Desk valide les composants logging/transport/wallet, crée la racine et le sous-répertoire effectif absents avec logs debug, refuse les objets filesystem invalides/symlinks de sous-répertoire avec logs error, et expose uniquement les chemins/profils non secrets dans le statut runtime. Aucun inventory .kspwallet n’est encore effectué. pre.003-fix.001 corrige les deux régressions de tests révélées par la validation workspace et le warning Clippy, puis rend visibles les événements trace frontend. pre.003-fix.002 corrige ensuite le canari d’inventaire .env.example qui confondait un fragment d’identifiant Rust avec une variable KSP, remplace le profil temporaire wallet_desk_dev par une famille Logging réutilisable (console_*, file_info, superdev, supertrace) et fait sélectionner temporairement supertrace par le composite Wallet Desk afin de conserver la visibilité des actions frontend sans profil spécifique à l’application. pre.003-fix.003 corrige uniquement les canaris révélés par la validation de fix.002 : conformité clippy::implicit_return et séparation explicite entre fragment lexical KSP incorporé dans un identifiant et nom d’environnement concret suffixé. pre.003-fix.004 ferme le dernier défaut Clippy de cette série en ajoutant les return explicites aux cinq closures restantes du test de composition supertrace, sans changer les profils Logging ni le composite. pre.004 matérialise ensuite l’inventory locked réel. Sa validation opérateur confirme le comportement fonctionnel et le workspace complet, mais révèle deux warnings unused_imports sur des réexports crate-root internes uniquement utiles aux unit tests ; pre.004-fix.001 les limite à #[cfg(test)] : ils disparaissent du build runtime mais restent disponibles au crate-root pour les unit tests, conformément aux règles d’exports KSP, sans changement de comportement. pre.005 matérialise ensuite la création native dans Wallet Desk et le premier lifecycle durable WalletSession : création no-clobber sous la racine Config via create_wallet_file_v1, session OWNER conservée uniquement en Rust après création, sélection locked durable, Lock/Deselect/Refresh/changement de wallet avec drop des handles et purge frontend des projections protégées/passwords. Les unlock VIEW/OWNER de wallets existants restent strictement pre.006. pre.005-fix.001 corrige le gate de compilation/tests révélé par l'opérateur sans modifier le lifecycle : la variante WalletSession::Owner contient désormais Box<WalletOwner> afin de fermer clippy::large_enum_variant, et le canari de sécurité définit explicitement request_start avant de contrôler les champs password request-only. pre.006 matérialise ensuite les unlock existants : VIEW/OWNER manuel, découverte Config des candidats KSP_SECRET_WALLET_PASS_*, ordre déterministe filename normalisé -> numérique -> nommé, actions configurées explicites et jamais automatiques, PrivilegedOperation pendant Argon2, puis sessions View/Owner Rust-only avec candidate count seul projeté vers l’UI. pre.006-fix.001 corrige les deux canaris desktop_security qui inspectaient trop largement le texte de WalletAuthorizedDto : la documentation du compteur de candidats contient légitimement le mot password, mais aucun champ password n’est sérialisé ; le test filtre désormais uniquement les lignes de champs pub(crate) de la structure. pre.007 ferme ensuite le MVP réseau : Config sait mapper un profil Transport déjà sélectionné par composite sans perdre la provenance Composite; Wallet Desk construit et conserve HttpTransportPool, publie uniquement profile/role/cluster/provider/compteurs sûrs, et refresh_wallet_balance accepte zéro argument frontend. Rust copie la Pubkey depuis WalletView/WalletOwner, libère le mutex avant l'I/O getBalance, utilise confirmed, puis projette lamports, SOL exact à neuf décimales, slot et api_version. Une réponse arrivée après lock/deselect/changement d'autorisation est refusée afin d'empêcher une balance stale de réapparaître. pre.007-fix.001 corrige uniquement le canari d'intégration qui utilisait le constructeur test-only ConfigEnvironment::from_maps; le test passe désormais par ConfigEnvironment::load, API publique utilisée en production. pre.007-fix.002 corrige le second défaut du même canari : son message d'assertion ne formate plus environment avec {:?}, ce qui aurait exigé ConfigEnvironment: Debug. ConfigEnvironment reste volontairement non-Debug et le runtime n'est pas modifié. pre.008 ajoute ensuite l'import de keypairs Solana CLI JSON/Base58 via le plugin Dialog utilisé exclusivement côté Rust : le frontend choisit seulement le format, Rust ouvre le picker natif, convertit le résultat en chemin local, lit la source avec une borne stricte, valide la Pubkey via ksp-wallet-lib, conserve les octets secrets dans Zeroizing<Vec<u8>> et ne renvoie que basename/format/Pubkey. L'import consomme cette source staged en mémoire avec import_wallet_transfer_v1, publie no-clobber sous la racine Wallet Config et ouvre la session OWNER avec de nouveaux credentials KSP. Aucun chemin source, contenu secret ou permission guest Dialog/FS ne traverse l'IPC.
Correctif 0.2.6-pre.013-fix.001
Le premier passage opérateur de pre.013 a confirmé fmt, audit, check et Clippy, mais le nouveau canari response_dtos_keep_secret_key_material_out_of_ipc a produit un faux positif sur WalletAuthorizedDto : le scanner analysait aussi les commentaires du bloc de structure et rencontrait la documentation légitime Wallet password candidates. pre.013-fix.001 aligne ce gate sur la règle déjà éprouvée en pre.006-fix.001 : seuls les champs pub(crate) réellement sérialisables sont inspectés pour password, key material, path/content/bytes. Aucun DTO ni contrat IPC runtime n'est assoupli.
Correctif 0.2.6-pre.013-fix.002
La validation de pre.013-fix.001 confirme fmt, audit, check, Clippy, les tests Wallet Desk, le canari release_compliance et le smoke Devnet opt-in. Le workspace complet révèle toutefois un second faux positif : le test global ksp-logging-lib::workspace_crates_do_not_bypass_ksp_logging_facade détecte le littéral tracing:: présent dans le code du canari Wallet Desk qui vérifie précisément l'absence de ce bypass. pre.013-fix.002 conserve le scanner global inchangé et construit ce motif à l'exécution dans release_compliance.rs; le canari continue ainsi d'auditer les sources backend sans s'auto-déclarer comme bypass. Aucun comportement runtime, DTO, dependency firewall ou capability n'est modifié.
2. Sources relues et hiérarchie appliquée
L'audit suit l'ordre d'autorité demandé :
- archive KSP stable
v0.2.5fournie ; - règles KSP versionnées ;
- architecture, plans et validations actuels ;
- contrats publics réellement exposés par les crates ;
- sources officielles actuelles Tauri/frontend ;
- archive bot3 fournie, uniquement comme référence historique.
Les documents obligatoires du prompt d'ouverture ont été relus avant conception. Les surfaces ont ensuite été réauditées dans :
ksp-core-lib
ksp-logging-lib
ksp-config-lib
ksp-onchain-transport-lib
ksp-wallet-lib
ksp-app-config-desk
Le plan historique docs/plans/007-V0_2_0_SERIES_PLANNING.md reste historique. La séquence active est celle de ROADMAP.md et docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md.
3. Frontières architecturales
Direction autorisée :
ksp-app-wallet-desk
-> ksp-config-lib
-> ksp-wallet-lib
-> ksp-onchain-transport-lib
-> ksp-core-lib lorsque nécessaire
-> ksp-logging-lib
-> Tauri / frontend dependencies
Interdictions :
ksp-wallet-lib -X-> Config / Transport / Tauri / Store / execution policy
ksp-app-wallet-desk -X-> cryptographie Wallet directe
ksp-app-wallet-desk -X-> solana-keypair / solana-signer / solana-signature directs
ksp-app-wallet-desk -X-> client RPC Solana alternatif
frontend -X-> keypair ou secret key material
frontend -X-> localStorage/sessionStorage pour les passwords
frontend -X-> lecture directe de .env
La seule intégration directe tracing tolérée reste l'adapter Tauri imposé par tauri-plugin-tracing. La façade applicative continue de passer par ksp-logging-lib.
4. ksp-app-config-desk comme gabarit Tauri
4.1 Structure réutilisée
Wallet Desk reprend le gabarit déjà validé :
frontend/
frontend/ts/
frontend/sass/
frontend/ts/bindings/
Vite + TypeScript
Bootstrap
Font Awesome
DataTables Bootstrap 5
DataTables Select Bootstrap 5
SimpleBar
resize-observer-polyfill
splash + main window
TS-RS à la frontière DTO applicative
capabilities Tauri explicites
bridge frontend -> Rust -> ksp-logging-lib
npm direct uniquement pour installer/mettre à jour les dépendances de package.json
beforeDevCommand = frontend dev déclenché automatiquement par cargo tauri dev
beforeBuildCommand = build frontend de production déclenché automatiquement par cargo tauri build
SimpleBar et resize-observer-polyfill sont conservés comme éléments du gabarit desktop, même si Wallet Desk ne les consomme pas tous dès le premier écran métier. Ils évitent de recréer un shell divergent du template éprouvé.
4.2 DataTables et sélection des wallets
Le tableau d'inventaire des wallets utilise datatables.net-bs5. datatables.net-select-bs5 est également conservé afin que la sélection de ligne reste cohérente avec le template et qu'un wallet puisse être sélectionné sans implémenter une logique de table parallèle.
Le tableau doit fournir au minimum :
état lock/unlock
filename
format_version
VIEW enabled/disabled
inspection status
La recherche, le tri et la pagination sont fournis par DataTables. Les données locked ne contiennent jamais Pubkey, alias ni notes.
4.3 Font Awesome
Font Awesome est utilisé pour les états lisibles rapidement :
fa-lock wallet verrouillé
fa-lock-open wallet actuellement ouvert
fa-eye / eye-slash selon état VIEW si utile
fa-triangle-exclamation pour une entrée invalide/diagnostic
Une icône n'est jamais l'unique information accessible : texte, title/ARIA ou colonne d'état accompagne l'icône.
4.4 Ports de développement
Config Desk utilise 1430 et son HMR 1431. Wallet Desk réserve :
Vite dev : 1432
HMR : 1433
4.5 Logging à reprendre/refondre
Le pattern validé est :
frontend helper
-> invoke emit_frontend_log
-> DTO validé côté Rust
-> ksp-logging-lib::{trace,debug,info,warn,error}!
Les helpers Wallet journalisent des événements sémantiques sans payload sensible, par exemple :
wallet_inventory_refresh_started
wallet_selected
wallet_unlock_view_succeeded
wallet_unlock_owner_failed
wallet_balance_refresh_succeeded
wallet_directory_created
wallet_export_cancelled
Ils ne sérialisent jamais aveuglément les request DTOs.
La configuration standard Logging expose aussi des profils opératoires réutilisables :
console_error / console_warn / console_info / console_debug / console_trace
console seule au niveau correspondant
file_info
console désactivée
fichier global info pour tous les targets KSP
superdev
console debug
un fichier debug par target KSP racine
supertrace
console debug
un fichier trace par target KSP racine
local_dev est conservé pour compatibilité avec Config Desk et les scénarios historiques. Pendant le développement de Wallet Desk, le composite sélectionne explicitement supertrace; ce choix reste une donnée de composition et pourra être resserré ultérieurement sans modifier l’application. Les fichiers par target utilisent des filtres de préfixe, de sorte que les sous-targets frontend (ksp-app-wallet-desk.frontend.*) restent routés dans le fichier de leur target racine.
5. Audit de la surface publique ksp-wallet-lib
La release stable 0.2.5 possède déjà la logique métier nécessaire. Wallet Desk compose et projette ; il ne réimplémente rien.
5.1 Locked projection
LockedWalletInfo expose uniquement :
format_version
view_enabled
Aucune Pubkey, aucun alias et aucune note ne sont disponibles avant autorisation.
5.2 Authorized projection
WalletInfo expose après autorisation :
format_version
capability
pubkey
alias
notes
5.3 Handles Rust
WalletView et WalletOwner restent possédés par l'état Rust de l'application.
WalletOwner fournit déjà :
sign
update_alias
add_note
update_note
delete_note
rotate_owner_password
rotate_view_password
disable_view
recreate_view
export_transfer
export_transfer_file
Le frontend ne reçoit aucun handle ni secret interne.
5.4 Persistence et transfer
Les APIs publiques stables couvrent déjà :
create_wallet_file_v1
inspect_locked_wallet_file_v1
open_wallet_view_file_v1
open_wallet_owner_file_v1
inspect_wallet_transfer_file
import_wallet_transfer_file_v1
export_wallet_transfer_file
Les formats transfer retenus sont :
Solana CLI JSON
Solana keypair Base58 complet
5.5 wallet.state_conflict
Un conflit d'état provoque :
message UI explicite
-> abandon du handle courant
-> purge de la projection autorisée et de la balance
-> nouvelle inspection locked
-> unlock requis de nouveau
Aucun retry aveugle ni overwrite forcé.
6. Config Wallet : racine globale + sous-répertoire de profil
6.1 Besoin d'étendre le registre Config
ConfigFileRegistry::defaults() connaît actuellement Logging, Transport et le schéma composite, mais pas Wallet. La tranche Config doit donc ajouter durablement :
cfg.std.wallet
schema.std.wallet
cfg.composite.ksp-app-wallet-desk
Fichiers physiques prévus :
config/std.wallet.json
config/schemas/std.wallet.schema.json
config/examples/std.wallet.example.json
config/composite.ksp-app-wallet-desk.json
config/examples/composite.ksp-app-wallet-desk.example.json
6.2 Shape de std.wallet
wallets_directory est global. Chaque profil peut ajouter un sous-répertoire relatif optionnel :
{
"format_version": 1,
"wallets_directory": "${KSP_WALLETS_DIRECTORY:-wallets}",
"default_profile": "default",
"profiles": [
{
"profile_id": "default"
},
{
"profile_id": "tests",
"wallets_subdirectory": "tests"
},
{
"profile_id": "temporary",
"wallets_subdirectory": "temporary"
}
]
}
Le profil default utilise directement la racine globale. Les profils tests, temporary ou futurs profils de scénario peuvent isoler leurs wallets sans introduire une seconde variable globale ni dupliquer le root.
Le champ wallets_subdirectory :
- est optionnel ;
- est relatif ;
- peut contenir plusieurs composants relatifs si le schema final le permet ;
- refuse chemin absolu,
..et toute résolution hors dewallets_directory; - reste une donnée non secrète de Config.
Le nom exact est retenu comme wallets_subdirectory pour rendre explicite sa relation avec wallets_directory.
6.3 ResolvedWalletConfig
ksp-config-lib doit fournir un adapter public analogue aux adapters Logging/Transport, par exemple :
ResolvedWalletConfig
file_id
source_path
profile_id
selection_source
wallets_directory
wallets_subdirectory
effective_wallets_directory
provenance sûre
effective_wallets_directory est :
wallets_directory
ou
wallets_directory / wallets_subdirectory
Config résout et valide les chemins mais n'énumère pas les wallets.
6.4 Création automatique des répertoires
Après résolution Config, Wallet Desk garantit l'existence du répertoire global et du répertoire effectif de profil avec une opération Rust create_dir_all ou équivalente.
Comportement :
absent -> tentative de création
créé/prêt -> debug
échec création -> error + erreur runtime explicite
existe mais n'est pas un répertoire -> error + refus d'inventory
Le log de succès inclut au minimum le profil et un indicateur created=true/false. Le log d'échec est error. Aucun échec de création n'est traité comme un inventaire vide normal.
La création est une responsabilité de composition de Wallet Desk après résolution Config, pas une raison d'ajouter du filesystem Wallet dans ksp-wallet-lib.
6.5 Composite Wallet Desk
Shape de référence :
{
"format_version": 1,
"default_profile": "devnet",
"profiles": [
{
"profile_id": "devnet",
"documents": [
{"component_id": "logging", "file_id": "cfg.std.logging", "profile_id": "supertrace"},
{"component_id": "transport", "file_id": "cfg.std.transport", "profile_id": "devnet_public"},
{"component_id": "wallet", "file_id": "cfg.std.wallet", "profile_id": "default"}
]
},
{
"profile_id": "tests",
"documents": [
{"component_id": "logging", "file_id": "cfg.std.logging", "profile_id": "supertrace"},
{"component_id": "transport", "file_id": "cfg.std.transport", "profile_id": "devnet_public"},
{"component_id": "wallet", "file_id": "cfg.std.wallet", "profile_id": "tests"}
]
}
]
}
Le composite référence des file_id, jamais des filenames physiques.
7. Passwords provenant de Config / .env
7.1 Évolution du contrat d'ouverture
Pour 0.2.6, les passwords Wallet peuvent être fournis de deux façons :
1. saisie éphémère dans l'UI
2. secret KSP géré par Config depuis process env ou .env
Les secrets .env sont donc explicitement autorisés pour cette application sous le namespace :
KSP_SECRET_WALLET_PASS_*
Cela ne place pas les passwords dans les documents JSON Config : Config reste seulement le propriétaire de l'environnement et de .env.
7.2 Noms supportés
Exemples opérateur :
KSP_SECRET_WALLET_PASS_01
KSP_SECRET_WALLET_PASS_02
KSP_SECRET_WALLET_PASS_MY_WALLET
KSP_SECRET_WALLET_PASS_TREASURY
Les suffixes peuvent représenter un numéro, le filename ou son stem normalisé, ou un alias de filename/opérateur. Ils ne représentent jamais l'alias interne protégé stocké dans le .kspwallet.
Le filename locked peut produire un candidat déterministe. La normalisation exacte doit être testée et documentée dans la tranche Config/secret-provider ; direction retenue :
strip .kspwallet
-> uppercase ASCII
-> caractères hors [A-Z0-9] remplacés par _
-> runs de _ compactés
-> _ de bord supprimés
Les collisions de labels sont possibles et ne deviennent pas une identité Wallet.
7.3 Alias de filename et alias interne Wallet
L'alias interne du wallet (WalletInfo.alias) reste une metadata protégée et n'intervient jamais dans la résolution des KSP_SECRET_WALLET_PASS_*. Wallet Desk n'ouvre donc jamais un wallet pour découvrir cet alias afin de choisir un secret.
Un suffixe nommé correspond uniquement au filename/stem normalisé ou à un alias de filename/opérateur défini pour identifier le fichier côté exploitation. Cet alias de filename n'est pas une identité interne du wallet et peut être connu avant unlock. Les noms de variables restent côté Rust/Config et ne sont pas renvoyés au frontend ni journalisés.
7.4 Découverte et ordre des candidats
Le moteur Config possède déjà une vue sûre des noms d'environnement présents. Wallet Desk utilise uniquement les APIs Config publiques ; aucun accès std::env direct n'est autorisé.
Ordre de tentative proposé :
1. candidat correspondant au filename normalisé
2. candidats numériques (_01, _02, ...) dans l'ordre numérique
3. candidats correspondant aux alias de filename/opérateur dans un ordre déterministe
Les valeurs provenant de Config restent entièrement côté Rust. Chaque String obtenue de Config est déplacée immédiatement dans ViewPassword ou OwnerPassword, qui possède la zeroization à la destruction. Cette règle concerne les secrets Config/.env ; elle n'interdit pas les passwords saisis par l'utilisateur de transiter du frontend vers Rust dans la request Tauri qui exécute l'opération demandée.
7.5 Tentative explicite, pas d'auto-unlock silencieux
Argon2id rend chaque tentative volontairement coûteuse. Wallet Desk ne doit donc pas essayer toutes les variables au simple clic de sélection d'une ligne.
L'UX distingue :
Unlock VIEW avec saisie
Unlock OWNER avec saisie
Unlock VIEW avec secret configuré
Unlock OWNER avec secret configuré
Une action « secret configuré » tente les candidats jusqu'au premier succès pour la capability demandée. Pour une saisie manuelle, le password est envoyé une seule fois par IPC HTML/TypeScript -> Rust, converti immédiatement en wrapper Wallet puis purgé côté frontend. Rust ne renvoie jamais ce password. Le frontend peut recevoir :
candidate_count
success/failure
capability obtenue
mais jamais la valeur d'un secret Config ni un password saisi. Les noms/suffixes de variables Config ne sont pas exposés au frontend. Les logs peuvent enregistrer le nombre de candidats et le résultat, pas leurs noms ni leurs valeurs.
7.6 .env.example
La tranche Config ajoute une documentation de pattern dans .env.example, par exemple :
# Wallet Desk password candidates. Values are secrets and never committed.
# Additional KSP_SECRET_WALLET_PASS_<LABEL> variables are supported.
KSP_SECRET_WALLET_PASS_01=
Aucun password réel n'entre dans l'archive ou le repository.
8. Audit Transport : balance minimale
Aucun nouveau wrapper Transport n'est nécessaire. La surface stable expose :
HttpTransportPool::get_balance(
&HttpRoleName,
&ksp_core_lib::Pubkey,
Option<&GetBalanceConfig>,
) -> Result<GetBalanceResult>
Le résultat fournit :
lamports
slot
api_version optionnelle
Le frontend ne fournit pas la Pubkey à la commande de refresh. Rust l'extrait du handle VIEW/OWNER autorisé.
Le DTO garde lamports comme entier exact. L'affichage SOL est une présentation dérivée.
9. Runtime Rust et machine d'état
États principaux :
NoSelection
Locked
ViewOpen
OwnerOpen
PrivilegedOperation
Représentation conceptuelle :
WalletSession
NoSelection
Locked { wallet_id, path, locked_info }
View { wallet_id, path, wallet: WalletView }
Owner { wallet_id, path, wallet: WalletOwner }
Le chemin complet reste côté Rust. Le frontend manipule un wallet_id/filename validé pour les opérations root-scoped.
Le changement de wallet, lock, deselect ou wallet.state_conflict :
drop handle Rust
-> purge WalletAuthorizedDto
-> purge balance
-> purge inputs password
-> retour Locked ou NoSelection
Pour getBalance, Rust copie uniquement la Pubkey puis libère le mutex session avant l'I/O réseau.
10. Inventory et stratégie de chemins
10.1 Racine effective
L'inventory utilise exclusivement :
ResolvedWalletConfig::effective_wallets_directory()
Le répertoire est créé/préparé au bootstrap selon la section Config. Un échec de préparation est un état d'erreur, pas directory_missing silencieux.
10.2 Enumeration
L'inventory est :
non récursive
extension exacte .kspwallet
fichiers réguliers seulement
symlinks d'entrée exclus
répertoires exclus
refresh manuel
refresh automatique après opération locale réussie
pas de filesystem watcher en 0.2.6
Chaque candidat est inspecté via inspect_locked_wallet_file_v1.
10.3 Résolution root-scoped
Create/import/select utilisent un filename validé :
pas de chemin absolu
pas de . ou ..
pas de séparateur injecté
extension .kspwallet requise
join sous effective_wallets_directory
contrôle containment/canonicalisation lorsque pertinent
Le filename est une identité de stockage affichable. Son stem/une forme normalisée peut servir d'alias de filename pour la résolution opérateur de KSP_SECRET_WALLET_PASS_*, mais il n'est jamais confondu avec l'alias interne protégé WalletInfo.alias.
10.4 Source import / destination export
L'import :
source externe via picker natif
-> inspection Rust
-> nouveau .kspwallet sous effective_wallets_directory
L'export OWNER :
destination externe choisie par save picker
-> export fichier Rust no-clobber
-> aucun contenu exporté secret dans une réponse IPC
11. Screen map
11.1 Shell
Mono-fenêtre après splash :
Header
application/runtime status
profile composite
current wallet state
action lock lorsque autorisé
Sidebar SimpleBar
Dashboard
Wallets
Create / Import
Details
Security
Diagnostics
11.2 Dashboard
Affiche :
Config composite profile
Logging status
Transport profile / cluster
Wallet global root
Wallet profile/subdirectory
effective wallet directory status
selected filename
Locked / VIEW / OWNER
last balance status
configured wallet-secret candidate count, sans noms
11.3 Wallets
DataTable principal :
status icon lock/open
filename
format_version
VIEW enabled
inspection status
Sélection de ligne via DataTables Select. La ligne du wallet ouvert utilise l'état fa-lock-open; les autres restent fa-lock.
11.4 Locked wallet / Unlock
Actions :
Unlock VIEW avec password saisi
Unlock OWNER avec password saisi
Unlock VIEW avec secret configuré
Unlock OWNER avec secret configuré
Deselect
11.5 Create
Formulaire :
filename
OWNER password + confirmation
VIEW password optionnel + confirmation
alias initial / notes initiales si l'API stable le permet directement
Destination sous le répertoire effectif, no-clobber.
11.6 Import
Flow :
picker source
-> inspection Pubkey + format sûre
-> destination filename
-> nouveaux credentials KSP
-> alias/notes éventuels
-> import no-clobber
11.7 Details
Après VIEW/OWNER :
capability
Pubkey
alias
notes
balance lamports
balance SOL formatée
RPC slot / api_version
transport profile
refresh
11.8 Security OWNER
metadata administration
rotate OWNER
rotate VIEW
disable VIEW
recreate VIEW
export OWNER file
11.9 Diagnostics
Surface sûre :
profils Config sélectionnés
répertoire Wallet effectif
nombre d'entrées inventory
nombre de secrets Wallet configurés
cluster / endpoint provider sûr sans URL credentialée
dernier code d'erreur par domaine
outil de logging contrôlé
12. Command map
| Command | Entrée frontend | Sortie sûre | Délégation |
|---|---|---|---|
get_runtime_status |
— | WalletRuntimeStatusDto |
Config/app state |
list_wallets |
— | Vec<WalletInventoryEntryDto> |
app filesystem + Wallet inspect |
refresh_wallets |
— | inventory DTO | app filesystem + Wallet inspect |
select_wallet |
wallet id | LockedWalletDto |
app resolver + Wallet inspect |
deselect_wallet |
— | session DTO | app state |
create_wallet |
create request | WalletAuthorizedDto OWNER |
Wallet create file |
unlock_wallet_view |
password | authorized VIEW | Wallet open VIEW |
unlock_wallet_owner |
password | authorized OWNER | Wallet open OWNER |
unlock_wallet_view_with_configured_secret |
— | authorized VIEW / operation result | Config env + Wallet open VIEW |
unlock_wallet_owner_with_configured_secret |
— | authorized OWNER / operation result | Config env + Wallet open OWNER |
lock_wallet |
— | LockedWalletDto |
drop handle + inspect |
refresh_wallet_balance |
— | WalletBalanceDto |
Transport getBalance |
inspect_import_source |
format only | transfer inspection DTO / cancel | Rust native picker + Wallet inspect |
import_wallet |
import request | authorized OWNER | staged bytes + Wallet import |
update_wallet_alias |
alias mutation | authorized DTO | WalletOwner |
add_wallet_note |
note create | authorized DTO | WalletOwner |
update_wallet_note |
note update | authorized DTO | WalletOwner |
delete_wallet_note |
note id | authorized DTO | WalletOwner |
rotate_owner_password |
rotation request | operation result | WalletOwner |
rotate_view_password |
rotation request | operation result | WalletOwner |
disable_view |
confirmation | authorized DTO | WalletOwner |
recreate_view |
new VIEW password | authorized DTO | WalletOwner |
export_wallet_owner |
format + save path | operation result | Wallet export file |
emit_frontend_log |
redacted payload | () |
Logging facade |
Aucune commande de signature arbitraire n'est ajoutée dans cette release.
13. DTO map
DTOs attendus :
WalletRuntimeStatusDto
WalletInventoryEntryDto
WalletSessionDto
LockedWalletDto
WalletAuthorizedDto
WalletBalanceDto
WalletCreateRequestDto
WalletSelectionRequestDto
WalletUnlockPasswordDto
WalletConfiguredSecretStatusDto
WalletImportRequestDto
WalletTransferInspectionDto
WalletAliasMutationDto
WalletNoteCreateDto
WalletNoteUpdateDto
WalletPasswordRotationDto
WalletSecurityStatusDto
WalletOperationResultDto
CommandErrorDto
FrontendLogPayloadDto
13.1 Locked DTO
Contient au maximum :
wallet_id / filename
format_version
view_enabled
session state icon/status
13.2 Authorized DTO
Contient :
wallet_id / filename
format_version
capability
pubkey
alias
notes
security status sûr
13.3 Secrets
Les DTOs de request contenant un password :
- ne dérivent pas un
Debugrévélant la valeur ; - ne sont pas
Clonesans besoin ; - sont convertis immédiatement en
OwnerPassword/ViewPassword; - ne sont jamais inclus dans un
CommandErrorDtoou un log.
Les commandes *_with_configured_secret n'ont aucun champ password. À l'inverse, les commands manuelles d'unlock, de création/import et de changement/recréation de password acceptent les passwords nécessaires dans leurs request DTOs frontend -> Rust ; ces champs ne sont jamais présents dans les DTOs de réponse.
14. Threat model desktop
| Menace | Mesure 0.2.6 |
|---|---|
| Password saisi conservé frontend | transmission request-only HTML/TS -> Rust, aucun storage, champ vidé en finally, pas de state global secret |
Password .env exposé au frontend |
résolution exclusivement Rust via Config |
| Confusion alias filename / alias interne | les suffixes KSP_SECRET_WALLET_PASS_* ciblent uniquement filename/stem ou alias de filename/opérateur ; jamais WalletInfo.alias |
| Multiples Argon2 involontaires | tentative de secrets configurés seulement sur action explicite |
| Password dans logs | logging sémantique sans request payload |
| Secret renvoyé Rust -> frontend | aucun password, keypair ou export secret en réponse IPC ; seuls les passwords saisis transitent frontend -> Rust dans les request DTOs |
| Identity leak locked | DTO locked issu de LockedWalletInfo + filename uniquement |
| Path traversal | chemins root-scoped validés sous le répertoire effectif |
| Symlink escape inventory | entrées symlink exclues ; containment vérifié |
| State stale | wallet.state_conflict => drop handle + refresh locked |
| RPC credential leak | aucune URL brute credentialée dans DTO/log UI |
| Frontend forge capability | capability déterminée depuis handle Rust |
| Export accidentel | OWNER + modal Bootstrap + save picker + no-clobber |
| Browser dialog natif non instrumenté | confirmations applicatives via Bootstrap modal |
La compromission complète du processus ou de l'OS reste hors modèle de sécurité.
15. Tauri security : capabilities d'abord, CSP inchangée par défaut
15.1 Capabilities
La réduction de la surface IPC repose d'abord sur les capabilities/permissions Tauri explicites.
Baseline :
core:default
tracing:default
À partir de pre.008, le plugin Dialog est initialisé uniquement côté Rust. Le frontend n'appelle aucune guest API Dialog ; la capability reste donc :
core:default
tracing:default
Aucune permission dialog:* ni fs:* n'est accordée. Rust ouvre le picker via tauri_plugin_dialog::DialogExt, convertit le résultat en chemin local puis lit la source lui-même. Le même principe est à conserver pour le futur save picker d'export tant qu'aucun besoin guest JavaScript n'apparaît. Aucun plugin filesystem n'est nécessaire pour l'inventory ou l'import desktop.
15.2 CSP
Config Desk utilise actuellement :
{"csp": null}
Wallet Desk réutilise ce comportement du gabarit par défaut. 0.2.6 n'introduit donc pas une CSP personnalisée comme nouveau chantier sans besoin concret.
La documentation Tauri décrit aussi la CSP comme une défense WebView contre XSS/chargement de contenu non autorisé, mais ce constat ne justifie pas à lui seul de diverger du template KSP déjà validé. Le frontend Wallet Desk reste local et le RPC est effectué côté Rust.
La CSP sera réauditée seulement si un besoin réel apparaît, par exemple :
chargement de contenu distant
WebSocket/fetch côté WebView
asset protocol / convertFileSrc
intégration nécessitant des sources supplémentaires
Une telle évolution devra être testée sur Linux/WebKitGTK et les autres plateformes ciblées avant adoption.
16. Logging et redaction
Interactions à instrumenter en debug/trace :
navigation
DataTable init/destroy/refresh
wallet select/deselect
wallet directory ready/created
create/import result
unlock manual VIEW/OWNER result
unlock configured-secret VIEW/OWNER result
lock
balance refresh
metadata mutation
rotation/disable/recreate
modal open/confirm/cancel
export start/result
state conflict
DOM/state replacement significatif
Règles de niveau :
répertoire créé/prêt correctement -> debug
échec création/résolution répertoire -> error
unlock incorrect -> warn/debug selon code final sans password
state conflict -> warn
erreur Transport -> warn/error selon nature
Interdictions :
password
secret bytes/keypair
transfer payload secret
KSP_SECRET value
nom complet KSP_SECRET_WALLET_PASS_* si son suffixe peut révéler une identité
RPC URL credentialée
Les logs restent uniques par lancement selon la politique KSP.
17. Create / Import / Export
17.1 Create
create_wallet :
répertoire effectif Config
-> filename validé
-> OwnerPassword/ViewPassword
-> create_wallet_file_v1
-> no-clobber
-> session OWNER
-> inventory refresh
17.2 Import
format choisi côté UI
-> picker open côté Rust
-> chemin local Rust-only
-> lecture bornée + Zeroizing<Vec<u8>>
-> inspect_wallet_transfer
-> projection sûre basename / format / Pubkey
-> choix destination sous root effectif
-> nouveaux credentials KSP
-> import_wallet_transfer_v1 sur les mêmes octets staged
-> no-clobber
-> session OWNER
La source n'est jamais parsée en TypeScript, son path ne traverse pas IPC et l'import consomme exactement les octets déjà inspectés.
17.3 Export
OWNER
-> choix format
-> modal Bootstrap
-> save picker
-> export_wallet_transfer_file
-> status seulement
Aucun export texte/clipboard n'est prévu en 0.2.6.
18. Administration OWNER
Le scope positif de 0.2.6 inclut :
update alias
notes CRUD
rotate OWNER password
rotate VIEW password
disable VIEW
recreate VIEW
OWNER file export CLI JSON/Base58
Les contrôles OWNER sont masqués ou désactivés sous VIEW et l'autorisation réelle est toujours revérifiée côté Rust.
19. Tests déterministes Rust
19.1 Config
Couvrir :
registre cfg.std.wallet/schema/composite
schema std.wallet
KSP_WALLETS_DIRECTORY process/.env/fallback
wallets_directory global
wallets_subdirectory absent
wallets_subdirectory simple/nested autorisé selon schema
absolute/traversal subdirectory rejetés
effective_wallets_directory
composite -> profils logging/wallet/transport exacts
filemap overrides conservés
19.2 Répertoire/inventory
Couvrir :
root absent créé
profile subdirectory absent créé
création réussie -> chemin disponible
création impossible -> erreur
existing non-directory -> erreur
regular .kspwallet accepted
wrong extension ignored
subdirectory entry ignored
symlink entry ignored
filename traversal rejected
no-clobber create/import
invalid wallet safe diagnostic
19.3 Secret provider Config
Couvrir :
aucun std::env direct hors Config
filtre KSP_SECRET_WALLET_PASS_*
filename normalization déterministe
ordre filename -> numeric -> named
process > .env conservé par Config
valeurs non présentes dans Debug/log/DTO
VIEW configured-secret success/failure
OWNER configured-secret success/failure
aucun essai automatique lors de select
suffixes filename/alias de filename testés sans dépendre de WalletInfo.alias
19.4 Session/DTO
Couvrir :
Locked DTO sans pubkey/alias/notes
VIEW projection
OWNER projection
lock purge
selection change purge
state_conflict purge
password Debug redacted/absent
responses sans secret fields
VIEW ne peut pas muter OWNER
19.5 Balance
Avec serveur HTTP local/fixture :
VIEW -> handle pubkey -> getBalance
OWNER -> même chemin
0 lamport accepté
lamports exact
slot/api_version mappés
Transport error sûr
credential absent erreur/log
20. Validation frontend
Aucun script npm applicatif n'est lancé directement pour valider Wallet Desk. npm est utilisé directement uniquement lors de l'installation initiale ou de la mise à jour des dépendances déclarées dans package.json :
cd crates/ksp-app-wallet-desk
npm i -D
# ou, lors d'une mise à jour des packages déjà déclarés :
npm update
cd ../..
Le cycle normal de développement/validation frontend passe par Tauri depuis la racine du workspace :
(cd crates/ksp-app-wallet-desk && cargo tauri dev)
Tauri déclenche alors automatiquement le script configuré dans beforeDevCommand. Les contrôles fonctionnels frontend à forte valeur portent notamment sur :
DataTable init/refresh/destroy sans duplication
row selection -> wallet selection
lock icon -> open icon -> lock icon
password input cleared in finally
VIEW/OWNER projection purge au lock
OWNER controls absents/inactifs sous VIEW
secret-configured action ne reçoit aucune valeur secret en TS
modal requis avant opérations privilégiées
SimpleBar/resize lifecycle sans fuite d'event listener
Le build frontend de production n'est jamais lancé directement avec npm. Il est déclenché par Tauri via beforeBuildCommand uniquement lors du cargo tauri build final.
21. Smoke Devnet opt-in
Scénario :
Config composite Devnet
-> profil Wallet test/temporary
-> répertoire créé automatiquement
-> wallet éphémère test-only
-> open VIEW ou OWNER
-> Pubkey
-> HttpTransportPool getBalance
-> réponse RPC valide, y compris 0 lamport
Le smoke :
- est opt-in ;
- n'utilise aucun wallet de production ;
- peut utiliser un password généré/test-only en mémoire ;
- n'exige pas de wallet financé ;
- nettoie son espace temporaire ;
- documente distinctement skip réseau et succès exécuté.
22. Audit dépendances frontend/Tauri
Le gabarit Config Desk stable contient déjà :
@fltsci/tauri-plugin-tracing ^0.3
@fortawesome/fontawesome-free ^7.3
@tauri-apps/api ^2.11
bootstrap ^5.3
datatables.net-bs5 ^3.0
datatables.net-select-bs5 ^4.0
resize-observer-polyfill ^1.5
simplebar ^6.3
Wallet Desk reprend cette baseline. Au 2026-08-20, l'audit externe confirme notamment :
@fortawesome/fontawesome-free 7.3.1
datatables.net-bs5 3.0.1
datatables.net-select-bs5 4.0.0
resize-observer-polyfill 1.5.1
Les ranges du template couvrent ces générations. simplebar ^6.3 est conservé depuis le template validé ; la version exacte sera réaudité au moment de création du package.json comme toute dépendance effectivement ajoutée.
pre.008 ajoute tauri-plugin-dialog ^2.7 côté Rust (2.7.2 courant lors de l'audit du 2026-08-21). Le package npm n'est volontairement pas ajouté puisque le guest JavaScript n'utilise aucune API Dialog ; le picker est entièrement possédé par les commandes Rust.
Sources d'audit actuelles :
https://v2.tauri.app/security/
https://v2.tauri.app/security/capabilities/
https://v2.tauri.app/security/csp/
https://v2.tauri.app/plugin/dialog/
https://www.npmjs.com/package/datatables.net-bs5
https://www.npmjs.com/package/datatables.net-select-bs5
https://www.npmjs.com/package/resize-observer-polyfill
https://www.npmjs.com/package/@fortawesome/fontawesome-free
23. Sizing : principes
Le forecast d'ouverture était utile mais trop compact. Le sizing révisé cherche à :
- garder une tranche Config cohérente avant les usages Wallet ;
- poser le shell complet avec son vrai gabarit frontend dès le début ;
- séparer inventory, secrets/unlock, réseau et administration ;
- ne pas mélanger import/export secret avec les premières projections ;
- garder une vraie tranche de compliance/intégration avant la documentation finale ;
- réserver explicitement une dernière tranche à README/USAGE/docs/validation/graphes/prompt suivant et build final.
Le numéro final reste souple. Si une tranche révèle un manque, un fix ou une prerelease supplémentaire est préférable à comprimer le travail.
24. Forecast souple détaillé des prereleases
pre.001 — audit, UX, composition et sizing
Livrables :
réaudit base v0.2.5
réaudit Config Desk / Config / Wallet / Transport / Logging
screen map
command + DTO map
modèle std.wallet root + profile subdirectory
stratégie KSP_SECRET_WALLET_PASS_*
stratégie inventory/path
threat model
sizing et forecast
Cette tranche se termine par le présent plan/delta et les validations opérateur Rust de base.
pre.002 — crate Tauri et shell complet du gabarit
État : matérialisé dans le delta 0.2.6-pre.002, sous réserve des validations opérateur Cargo/Tauri.
Objectifs/livrables :
crates/ksp-app-wallet-desk
package.json / Vite / TS / SCSS
ports 1432/1433
splash + main
Bootstrap
Font Awesome
DataTables + Select
SimpleBar + resize-observer-polyfill
TS-RS foundation
logging frontend/Rust
capabilities initiales
installation npm uniquement si package.json vient d'être créé/modifié
parcours cargo tauri dev via beforeDevCommand
Le shell peut afficher des données mockées/runtime minimales, mais doit déjà valider navigation, scroll, layout et logging.
pre.003 — std.wallet, composite et préparation des répertoires
Livrables de la tranche :
cfg.std.wallet / schema.std.wallet enregistrés dans Config
wallets_directory global = ${KSP_WALLETS_DIRECTORY:-wallets}
wallets_subdirectory optionnel, relatif et multi-composants normaux
profils default / temporary / tests
ResolvedWalletConfig avec root/subdirectory/effective path/provenance
resolve_logging_config_profile / resolve_wallet_config_profile pour profils composites
cfg.composite.ksp-app-wallet-desk = logging(supertrace) + transport + wallet
.env.example documente KSP_WALLETS_DIRECTORY et le pattern futur KSP_SECRET_WALLET_PASS_*
bootstrap Wallet Desk valide les trois component_id/file_id requis
création automatique du root et du sous-répertoire effectif absents
rejet d'un root non-directory et d'un composant de sous-répertoire symlink/non-directory
debug sur répertoires prêts/créés ; error sur préparation impossible
profils Logging génériques : console_error/warn/info/debug/trace, file_info, superdev et supertrace ; local_dev conservé pour compatibilité
composite Wallet Desk temporairement sur supertrace : console debug + un fichier trace par target KSP racine
trace frontend metadata-only sur clics boutons/navigation/tabs et actions shell
status DTO/UI : profil composite, profils Logging/Wallet, root/subdirectory/effective directory
Tests de tranche : registre/schema, composition et provenance, precedence environnement, containment syntaxique, création idempotente des répertoires, rejet non-directory/symlink et contrats desktop. L’inventory .kspwallet reste strictement pre.004.
pre.004 — inventory DataTables et inspection locked
Objectifs :
enumeration .kspwallet non récursive
path safety + symlink policy
inspect_locked_wallet_file_v1
WalletInventoryEntryDto / LockedWalletDto
DataTable réel
row selection
Font Awesome lock/open/error states
refresh manuel + après opérations locales futures
À la fin de cette tranche, l'application sait lister et sélectionner un wallet sans révéler son identité protégée.
État matérialisé par pre.004 :
ksp-app-wallet-desk -> ksp-wallet-lib ajouté explicitement
effective_wallets_directory comme unique racine d'inventory
read_dir asynchrone non récursif
filename UTF-8 + suffixe exact .kspwallet
regular file uniquement ; symlink/non-file exclus
inspection via inspect_locked_wallet_file_v1
ligne invalide conservée avec CommandErrorDto sûr
WalletInventoryEntryDto / LockedWalletDto / WalletSelectionRequestDto
list_wallets / refresh_wallets / select_wallet
DataTable réel + refresh + sélection
fa-lock pour valid locked ; fa-triangle-exclamation pour invalid
selection revalidée côté Rust, filename single-component uniquement
root symlink désormais rejeté lors de la préparation Config
aucun Pubkey / alias / notes / password dans la projection locked
La sélection de pre.004 reste une projection locked sans handle durable : le WalletSession Rust et les handles OWNER/VIEW arrivent en pre.005/pre.006. Un refresh invalide donc la sélection frontend pour éviter de conserver une projection potentiellement stale.
Validation opérateur pre.004 : inventory/tests/workspace/Tauri dev verts, avec deux warnings unused_imports sur WalletInspectionStatusDto et WalletInventoryStateDto réexportés au crate-root uniquement pour les unit tests. pre.004-fix.001 place ces réexports sous #[cfg(test)] ; les unit tests conservent crate::WalletInspectionStatusDto et crate::WalletInventoryStateDto, tandis que les builds non-test ne portent plus ces imports inutilisés. Aucun DTO TS-RS, aucune commande Tauri, aucune règle de path safety et aucun comportement frontend ne changent.
pre.005 — création Wallet et lifecycle de session Rust
Objectifs :
AppState / WalletSession
create_wallet_file_v1
form create
OWNER + VIEW optionnel
no-clobber
session OWNER après création
select/deselect
lock/drop handle
purge frontend protégée
Cette tranche pose le lifecycle avant d'ajouter les providers de password existants.
État matérialisé par pre.005 :
WalletSession Rust = NoSelection | Locked | Owner
create_wallet -> create_wallet_file_v1 -> session OWNER
select_wallet -> session Locked
deselect_wallet -> drop handle/path state -> NoSelection
lock_wallet -> drop OWNER -> re-inspect -> Locked
refresh_wallets -> deselect backend -> inventory
Le formulaire Create transmet OWNER/VIEW passwords uniquement frontend -> Rust. WalletCreateRequestDto n'est ni Debug ni Clone; les wrappers OwnerPassword/ViewPassword prennent immédiatement possession des String. Les réponses ne contiennent aucun password ni secret key. Après création, WalletAuthorizedDto peut exposer Pubkey, alias et notes parce que la session OWNER est explicitement autorisée. Les valeurs protégées ne sont pas journalisées.
Après création, l'inventory est relu avec list_wallets sans détruire la session OWNER nouvellement créée. En revanche, un Refresh utilisateur appelle refresh_wallets, qui purge d'abord la session Rust afin que l'UI et le backend ne divergent jamais. Lock, deselect, sélection d'un autre wallet et échec de transition purgent aussi les projections protégées et inputs frontend.
Les unlock manuels/configurés d'un wallet existant restent strictement pre.006.
pre.006 — unlock manuel + KSP_SECRET_WALLET_PASS_*
État matérialisé :
Locked -> PrivilegedOperation -> ViewOpen / OwnerOpen
unlock VIEW/OWNER manuel via WalletUnlockRequestDto request-only
ConfigManagement::environment_report pour découvrir les candidats sans valeur
ConfigManagement::reveal_effective_environment_value uniquement pendant l’action explicite
normalisation stem : uppercase ASCII, hors [A-Z0-9] -> _, runs compactés, bords supprimés
ordre : filename normalisé, puis numériques en ordre numérique, puis nommés lexicographiques
aucun auto-unlock lors de select_wallet
frontend reçoit uniquement configured_secret_candidate_count
VIEW disabled refuse l’action VIEW avant KDF
OwnerPassword/ViewPassword prennent immédiatement possession des String révélés/saisis
Lock/Deselect/Refresh/changement de wallet détruisent les handles autorisés
Le frontend Security expose quatre actions distinctes : VIEW manuel, OWNER manuel, VIEW configuré et OWNER configuré. Les noms/suffixes/valeurs des secrets Config ne traversent jamais IPC et ne sont pas journalisés ; les logs se limitent au wallet_id root-scoped, capability, candidate_count et résultat. PrivilegedOperation évite de tenir un mutex pendant Argon2 et empêche une complétion de KDF obsolète de réinstaller un handle après changement de session. Tests spécifiques : normalisation/ordre, requests non Clone/Debug, redaction, candidate-count only et non-auto-unlock.
pre.007 — détails autorisés + balance HTTP
Statut : implémenté dans 0.2.6-pre.007.
WalletAuthorizedDto -> Pubkey / alias / notes
Config composite -> ResolvedTransportConfig (selection_source = Composite)
ResolvedTransportConfig -> HttpTransportPool
VIEW/OWNER Rust-only -> Pubkey copiée avant I/O
getBalance(role=default, commitment=confirmed)
-> lamports exacts
-> SOL formaté sans flottant
-> slot / api_version
-> transport profile / role
Le frontend appelle refresh_wallet_balance sans argument ; il ne peut donc injecter ni Pubkey ni URL d'endpoint. Les diagnostics exposent seulement profile, rôle, cluster(s), provider(s), total d'endpoints et endpoints disponibles. Lock/Deselect/Refresh/changement de wallet purgent la balance ; le backend revérifie aussi que la session autorisée n'a pas changé pendant l'I/O avant de livrer le résultat.
À ce point le MVP obligatoire est complet : wallet autorisé + identité + balance réseau.
pre.007-fix.001 — canari ConfigEnvironment public
Statut : correctif technique de 0.2.6-pre.007.
Le test d'intégration Wallet Desk wallet_desk_composite_transport_profile_builds_get_balance_pool appelait ConfigEnvironment::from_maps, constructeur pub(crate) volontairement réservé aux unit tests internes de ksp-config-lib. Le runtime utilisait déjà la bonne surface publique. Le correctif remplace cette construction par :
ConfigEnvironment::load()
-> resolve_transport_config_profile
-> HttpTransportPool::new
-> route getBalance / role default
Aucune API runtime, configuration Transport, URL, logique de balance ou surface frontend n'est modifiée.
pre.007-fix.002 — canari ConfigEnvironment sans contrainte Debug
Statut : correctif technique de 0.2.6-pre.007.
Le canari corrigé par pre.007-fix.001 appelait bien ConfigEnvironment::load(), mais son message d'assertion utilisait encore {environment:?}. Comme Result<T, E>: Debug exige T: Debug, cela imposait artificiellement ConfigEnvironment: Debug, contrat volontairement absent afin de ne pas faciliter la projection accidentelle d'un snapshot d'environnement.
Le correctif conserve simplement :
let environment = ConfigEnvironment::load();
assert!(environment.is_ok(), "public Config environment snapshot should load");
Aucun Debug n'est ajouté à ConfigEnvironment; aucune API Config, logique Transport/getBalance ou surface frontend n'est modifiée.
pre.008 — import Solana CLI JSON / Base58
Statut : implémenté dans 0.2.6-pre.008.
Surface livrée :
tauri-plugin-dialog Rust-only
format choisi par le frontend
picker natif ouvert côté Rust
source externe -> path local Rust uniquement
lecture bornée selon le format
Zeroizing<Vec<u8>> en staging Rust-only
inspect_wallet_transfer -> basename / format / Pubkey sûrs
import_wallet_transfer_v1 -> destination root-scoped
nouveaux OWNER/VIEW credentials request-only
no-clobber
OWNER session ouverte après succès
inventory refresh
La source n'est jamais parsée en TypeScript et son chemin n'est jamais sérialisé dans un DTO. inspect_import_source accepte uniquement un WalletImportSourceRequestDto { format }; le picker est invoqué par Rust avec DialogExt. Une source valide est lue une seule fois dans une mémoire bornée et zeroized, puis conservée comme PendingWalletImport. import_wallet consomme ce staging en mémoire avec import_wallet_transfer_v1, ce qui supprime le risque de réutiliser un chemin externe fourni par le frontend et évite une seconde lecture de la source entre inspection et import.
Le plugin Dialog n'est pas exposé au guest JavaScript : aucun package npm @tauri-apps/plugin-dialog, aucune permission dialog:* et aucun plugin filesystem ne sont ajoutés. Le frontend ne reçoit que source_name (basename), format et pubkey. Les nouveaux passwords OWNER/VIEW transitent uniquement HTML/TypeScript -> Rust pour l'opération demandée, sont déplacés dans les wrappers Wallet, puis purgés côté frontend ; ils ne reviennent jamais dans une réponse.
pre.008-fix.001 — conformité Clippy du staging import
Statut : préparé après validation opérateur de 0.2.6-pre.008.
Le runtime et les tests fonctionnels de pre.008 sont verts. Le gate cargo clippy --workspace --all-targets révèle uniquement deux warnings idiomatiques dans Wallet Desk :
mem_replace_option_with_some -> utiliser Option::replace
explicit_auto_deref -> utiliser &mut bytes
Le fix applique strictement ces deux remédiations :
let previous = (*slot).replace(staged);
let read_result = reader.read_to_end(&mut bytes).await;
Aucun changement n’est apporté au picker natif, à Zeroizing<Vec<u8>>, aux bornes de lecture, aux adapters Wallet d’import, au no-clobber, aux credentials, aux DTOs ou au frontend.
pre.009 — metadata OWNER
Statut : implémenté dans 0.2.6-pre.009; validations opérateur fonctionnelles vertes, correctif Clippy 0.2.6-pre.009-fix.001 requis avant clôture de tranche.
Objectifs matérialisés :
update alias / clear alias
notes add/update/delete
controls strictement OWNER
projection autorisée rafraîchie après mutation
aucun mutex std::sync tenu pendant l'I/O async Wallet
wallet.state_conflict => purge handle stale + réinspection Locked + re-unlock obligatoire
DataTable/details cohérents après mutation
confirmation Bootstrap pour suppression de note
Les mutations délèguent directement à WalletOwner::{update_alias, add_note, update_note, delete_note}. Pendant la persistence async, le handle OWNER est déplacé hors du mutex AppState et remplacé par un état de réservation OwnerOperation; une sélection/désélection concurrente empêche donc la réinstallation d'un handle devenu obsolète. Les alias/textes de note ne sont jamais journalisés.
Un wallet.state_conflict n'est jamais retenté avec le même handle : Wallet Desk détruit l'OWNER stale, réinspecte le fichier courant sans KDF, revient au plus à Locked, puis le frontend purge Pubkey/alias/notes/balance et impose une nouvelle autorisation OWNER.
Le correctif pre.009-fix.001 est strictement hygiénique : suppression du champ runtime view_enabled devenu inutilisé dans la variante WalletSession::View (aucun #[cfg(test)] ni allow), return explicite exigé par la politique Clippy du workspace dans la recovery de conflit, et helper de canari DeserializeOwned rendu réellement générique via PhantomData<T>. Aucun contrat IPC, comportement metadata, format Wallet ou frontend n'est modifié.
pre.010 — rotations credentials
Objectifs réalisés :
rotate OWNER password depuis une session OWNER
rotate VIEW password depuis une session OWNER lorsque VIEW est activé
self-rotate VIEW password depuis une session VIEW autorisée
double saisie/confirmation uniquement dans le frontend
un seul nouveau password traverse IPC vers Rust
conversion immédiate en OwnerPassword / ViewPassword
réutilisation du lifecycle OwnerOperation hors mutex pendant Argon2 + persistence
wallet.state_conflict => purge du handle stale + retour Locked + re-unlock de la capacité initiatrice
session OWNER ou VIEW conservée après succès ou échec non conflictuel
Pubkey, alias, notes et identité Solana inchangés
VIEW rotation administrative sans ancien password VIEW conformément au contrat WalletOwner
VIEW self-rotation conformément au contrat WalletView
secrets Config volontairement inchangés après rotation
message UI explicite avant Lock/unlock configuré
redaction/canaries request-only
La rotation ne choisit ni ne modifie implicitement un KSP_SECRET_WALLET_PASS_*. Plusieurs candidats Config peuvent viser le même filename et leur politique de maintenance appartient à Config/opérateur. Wallet Desk expose donc seulement le compteur sûr déjà existant et avertit qu'un candidat contenant l'ancien password cessera de fonctionner après Lock tant qu'il n'aura pas été mis à jour.
rotate_owner_password retourne une projection OWNER. rotate_view_password conserve la capacité initiatrice : projection OWNER pour l'administration OWNER, projection VIEW pour la self-rotation VIEW. Aucun credential n'est réexposé. Le password de confirmation reste strictement local au frontend et n'est jamais envoyé à Rust.
Le correctif pre.010-fix.001 complète ce contrat : la personne déjà autorisée en VIEW peut changer son propre password VIEW via WalletView::rotate_view_password, tandis qu'OWNER conserve le pouvoir administratif de remplacer le password VIEW sans connaître l'ancien. Wallet Desk réserve ViewOperation pendant l'Argon2/persistence async, refuse les complétions stale et applique au chemin VIEW la même politique de recovery wallet.state_conflict que pour OWNER.
pre.011 — strong VIEW disable/recreate
Cette tranche branche les primitives fortes déjà possédées par ksp-wallet-lib sans dupliquer leur cryptographie dans l'application.
Contrat retenu :
OWNER uniquement -> disable_wallet_view
-> WalletOwner::disable_view
-> nouvelle metadata content key
-> suppression du slot/descriptor VIEW
-> session OWNER conservée avec view_enabled=false
OWNER uniquement -> recreate_wallet_view(new VIEW password)
-> WalletOwner::recreate_view
-> nouvelle metadata content key
-> nouveau slot ID VIEW
-> nouveau credential VIEW
-> session OWNER conservée avec view_enabled=true
recreate_wallet_view est volontairement disponible même lorsque VIEW est déjà enabled : dans ce cas l'opération remplace fortement l'autorité VIEW courante au lieu d'effectuer une simple rotation du password.
Le résultat IPC est un WalletViewSecurityStatusDto sûr et minimal :
wallet_id
capability
view_enabled
configured_secret_candidate_count
Il ne contient ni Pubkey, ni alias, ni notes, ni password. Les metadata autorisées déjà détenues par la session OWNER restent côté frontend dans leur projection existante et ne sont pas retransmises pour cette opération.
Le lifecycle réutilise OwnerOperation et libère le mutex avant persistence/Argon2. OwnerOperationContext distingue l'état VIEW réservé avant l'opération de l'état VIEW résultant afin que le contrôle anti-stale continue à comparer l'état original tout en réinstallant la session OWNER avec le nouveau view_enabled.
UX :
Strong disable VIEW -> modal Bootstrap destructif
Strong recreate VIEW -> password + confirmation frontend-only -> modal Bootstrap privilégié
aucun window.confirm / alert / prompt
DataTable rafraîchie après succès
VIEW enabled/disabled immédiatement synchronisé
La confirmation du password de recreate reste strictement frontend ; un seul nouveau password traverse IPC. Les KSP_SECRET_WALLET_PASS_* ne sont jamais modifiés implicitement.
Garantie explicitement affichée : la révocation forte concerne l'état courant et futur du Wallet ; une copie historique déjà détenue reste hors de la garantie de révocation.
Validation runtime attendue :
OWNER -> strong disable VIEW
Lock -> VIEW unlock impossible
OWNER -> strong recreate VIEW avec nouveau password
Lock -> ancien VIEW password refusé
nouveau VIEW password accepté
Pubkey / alias / notes / OWNER inchangés
Validation opérateur pre.011 : verte le 21 août 2026. fmt, audit Rust KSP, cargo check --workspace, Clippy sans warning, tests Wallet Desk et workspace complet passent. Le runtime Tauri confirme view_enabled=true -> strong disable -> false, puis strong recreate -> true; l'ancien credential VIEW est refusé après recreate et le nouveau est accepté.
pre.012 — export OWNER fichier
Cette tranche expose uniquement l'adapter fichier déjà possédé par WalletOwner; Wallet Desk ne sérialise jamais lui-même la keypair.
WalletTransferFormatDto quitte wallet_import.rs pour un module Desk commun wallet_transfer.rs, puisque le même contrat est désormais consommé par import et export. Les codes IPC restent inchangés.
Contrat retenu :
OWNER session requise avant ouverture du save picker
frontend -> Rust : format uniquement
Rust -> native save picker : destination locale
Rust -> WalletOwner::export_transfer_file(destination, format)
Wallet -> création atomique no-clobber du fichier secret
Rust -> frontend : wallet_id + format + basename uniquement
Formats disponibles :
Solana CLI JSON
Solana keypair Base58 complet
Le frontend ne reçoit jamais :
chemin complet de destination
bytes de keypair
Base58 de keypair
JSON de keypair
password OWNER
handle WalletOwner
L'opération réutilise OwnerOperation afin de déplacer le handle OWNER hors du mutex pendant l'écriture async. Une annulation du picker ne touche pas la session. Une erreur d'I/O ou destination_exists restaure la session OWNER ; la primitive Wallet conserve le no-clobber et les permissions privées best-effort sur Unix.
UX :
format CLI JSON / Base58
modal Bootstrap avertissant qu'il s'agit d'une keypair non chiffrée
confirmation -> save picker natif Rust
aucun export texte
aucun clipboard
aucun chemin arbitraire fourni par le frontend
status final avec basename seulement
Le nom de destination et le contenu secret ne sont pas journalisés. Les logs persistants ne contiennent que wallet_id, format et statut de l'opération.
pre.013 — intégration, compliance, sécurité et smoke
Cette tranche consolide les preuves transversales sans ajouter de nouvelle capacité Wallet Desk métier. Elle matérialise :
tests déterministes de composition et de frontières release-wide
inventory/path adversarial renforcé : non récursif, suffixe exact, traversal/absolute/nested rejetés
audit DTO/IPC : aucun secret, keypair, endpoint URL ou path arbitraire projeté vers le frontend
audit frontend : aucun filesystem/dialog plugin guest, aucun fetch/WebSocket, aucun storage/clipboard secret
audit capabilities : core:default + tracing:default uniquement
audit dependency firewall Wallet Desk : pas de dépendance Solana/protocole directe hors crates KSP
smoke Devnet opt-in Config -> Wallet -> Transport -> getBalance depuis la surface Wallet Desk
matrice docs/validation/009-V0_2_6_WALLET_DESK_COMPLIANCE.md
cargo tree / inverse tree opérateur
workspace checkpoint complet
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 les tranches V2 binaires intercalées avant la candidate finale.
pre.014 — polish gabarit Bootstrap et splashscreen
Tranche explicitement réservée au retour visuel/opérateur avant la clôture. Le détail sera borné au moment de l'ouverture de cette tranche ; la liste actuelle est volontairement indicative :
corrections du templating Bootstrap Wallet Desk
cohérence sizing/overflow des conteneurs desktop
splashscreen : suppression des scrollbars occasionnelles
régression visuelle splash -> main
réutilisation du gabarit commun sans déplacer de logique métier
Cette tranche n'est pas un prétexte pour refondre l'UX métier. Les détails précis des défauts et critères d'acceptation seront fournis par l'opérateur lorsqu'elle sera ouverte.
pre.015 — .kspwallet V2 : wire binaire + codec canonique
Cette tranche intercalée remplace l'ancien départ immédiat en documentation finale. Elle fixe le wire binaire V2 et son codec structurel dans ksp-wallet-lib sans modifier encore Wallet Desk ni les APIs de persistence V1 utilisées en production.
KSPWALLET magic binaire
format_version = 2
longueur totale explicite
flags réservés stricts
OWNER puis VIEW optionnel
Argon2id / XChaCha20-Poly1305 / Ed25519 par IDs numériques
compartiments OWNER-CONTROL / METADATA / SECRET
entiers big-endian
aucune Base64
aucune compression
aucun trailing byte
domains/transcripts/AAD V2 distincts de V1
fixture wire-only canonique + comparaison de taille
La politique API est figée dès cette tranche : DEFAULT_WALLET_FORMAT et LATEST_SUPPORTED_WALLET_FORMAT sont deux notions distinctes. À l'intégration V2, le default devient V2. Une future V3 n'entraîne jamais automatiquement le déplacement du default.
pre.016 — APIs génériques/versionnées + création/ouverture V2
Statut : matérialisé par 0.2.6-pre.016.
Cette tranche réalise :
DEFAULT_WALLET_FORMAT = V2
LATEST_SUPPORTED_WALLET_FORMAT = V2, sans couplage automatique entre les deux
WalletFormat public non_exhaustive
create_wallet/create_wallet_file/import_wallet_transfer génériques -> V2 default
variantes explicites _v1 conservées et _v2 ajoutées
open/inspect génériques -> détection bornée V1 JSON / V2 binaire
open/inspect _v1/_v2 -> format forcé strict
create/open in-memory V2 + crypto/transcripts V2
persistence V2 no-clobber et remplacement atomique avec state-conflict
WalletOwner/WalletView version-neutral derrière des états runtime V1/V2
metadata, rotations OWNER/VIEW, disable/recreate VIEW et VIEW self-rotation restent dans le format ouvert
to_native_bytes() sérialise V1 ou V2 ; to_json_bytes() reste V1-only
import transfer générique -> V2, variantes _v1/_v2 forcées
Wallet Desk migre vers les APIs génériques, jamais vers un numéro de format
canaris explicites default V2, compatibilité lecture V1, strict _v1/_v2 et administration V2
Une future API _v3 pourra être ajoutée sans transformer automatiquement le default en V3. pre.016 ne migre aucun fichier V1 existant : la migration authentifiée reste réservée à pre.017.
pre.017 — migration V1 -> V2 + persistence/canaris
Statut : matérialisé par 0.2.6-pre.017 et checkpoint opérateur entièrement vert.
La migration est une opération explicite OWNER-authentifiée et reste séparée de toute ouverture normale :
migrate_wallet_v1_to_v2(...) snapshot mémoire V1 -> V2
migrate_wallet_file_v1_to_v2(...) source V1 conservée + destination V2 no-clobber
migrate_wallet_file_v1_to_v2_in_place(...) remplacement atomique V1 -> V2
Invariants retenus :
- source obligatoirement V1 et authentifiée par OWNER avant conversion ;
- même identité Solana, alias, textes de notes et identifiants stables de notes après migration ;
- OWNER conserve le même mot de passe fourni à la migration ;
- VIEW reste activé ou désactivé comme dans V1 ; quand VIEW est activé, le caller fournit le mot de passe cible V2, qui peut être l'ancien ou un remplacement autorisé par OWNER ;
- aucun ciphertext/slot V1 n'est transcodé directement : V2 reçoit de nouvelles clés de contenu, nouveaux salts/nonces/slot IDs, un nouvel OWNER auth key et ses propres transcripts/AAD ;
- copy migration = destination no-clobber, source inchangée ;
- in-place migration = vérification de l'enveloppe V1 authentifiée attendue puis remplacement atomique ; un état concurrent/stale retourne
wallet.state_conflict; wallet.migration_invalidcouvre les demandes incohérentes avec la forme VIEW source/cible ;- une ouverture générique V1 continue de laisser le fichier en V1.
Les canaris pre.017 couvrent migration mémoire, conservation identité/metadata/note IDs, choix du credential VIEW cible, no-clobber, remplacement in-place, tampering, stale-state et exposition API publique. Wallet Desk reste version-neutral et non-migrant : aucune ouverture/inventory du Desk ne déclenche la migration.
pre.018 — documentation finale, runtime packagé et candidate build
Statut : candidate validée ; le gate complet et le build final ont été fermés par pre.018-fix.001, puis pre.018-fix.002 a renforcé uniquement le prompt de reprise.
Cette tranche ferme la stratégie CWD/resources release qui restait ouverte depuis le polish desktop :
bundle.resources : 4 documents Config + 4 schemas du registre courant
Config packagée : seed uniquement si absente
Schemas : synchronisés depuis le package courant à chaque lancement
.env : jamais embarqué ni seedé
racine runtime writable : ProjectDirs commun KSP
release Tauri : resource_dir -> prepare_packaged_runtime -> set_current_dir -> AppState::initialize
debug Tauri : comportement workspace-root existant conservé
Les deux Desks utilisent le même runtime KSP writable et ne dépendent donc plus du checkout source en distribution. Aucun accès filesystem supplémentaire n'est accordé au guest frontend. ksp-config-lib reste propriétaire de la préparation des Config/schemas et de la localisation user-writable.
Livrables candidate :
README/USAGE Wallet Desk créés
README/USAGE Config Desk synchronisés sur le runtime packagé
docs/validation 0.2.6 finalisée jusqu'au gate opérateur
règles KSP packaging/CWD durables
ROADMAP / functional sequence / indexes mis à jour globalement
CHANGELOG réservé à rel.001 puis synchronisé avec la publication stable
versions Cargo/package/Tauri alignées sur la candidate, puis sur la release stable en rel.001
prompt de démarrage 0.2.7 WebSocket finalisé
canaris resources/packaging ajoutés
Le checkpoint pre.017 de référence est vert : fmt/audit/check/clippy/workspace tests passent, ksp-wallet-lib compte 84 tests passés + 1 benchmark ignoré, et le parcours Tauri confirme un import créant réellement un V2 puis un getBalance Devnet réussi.
Le premier passage pre.018 a révélé un faux positif du canari d’ownership Config, corrigé par pre.018-fix.001 sans relâcher la règle. Après ce fix, cargo fmt, audit Python, cargo check, Clippy, les tests ciblés, cargo test --workspace puis (cd crates/ksp-app-wallet-desk && cargo tauri build) ont tous réussi ; ce build était bien la dernière opération et a produit les bundles Linux .deb, .rpm et .AppImage en 0.2.6-pre.18.fix.1. pre.018-fix.002 ne touche qu’aux documents et renforce le prompt 0.2.7.
rel.001 — publication stable
Après preuves finales acquises :
version stable 0.2.6
commit v0.2.6-rel.001
tag stable v0.2.6 selon workflow KSP
aucune nouvelle fonctionnalité introduite dans rel.001
prochaine tranche 0.2.7-pre.001 via prompt 012
25. Validation continue
Après toute modification Rust :
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
cargo check --workspace
cargo clippy --workspace --all-targets
Tests ciblés pendant le développement. Aux checkpoints globaux :
cargo test --workspace
Lors de la création initiale de package.json ou d'une modification de ses dépendances, npm est utilisé directement uniquement pour installer ou mettre à jour les packages :
cd crates/ksp-app-wallet-desk
npm i -D
# ou, pour mettre à jour les packages déclarés :
npm update
cd ../..
Le parcours normal de développement et de validation fonctionnelle frontend passe par :
(cd crates/ksp-app-wallet-desk && cargo tauri dev)
Tauri exécute automatiquement le beforeDevCommand configuré. Aucun script npm applicatif de contrôle, développement ou build n'est lancé directement par l'opérateur.
Candidate finale :
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
(cd crates/ksp-app-wallet-desk && cargo tauri dev)
(cd crates/ksp-app-wallet-desk && cargo tauri build)
(cd crates/ksp-app-wallet-desk && cargo tauri build) reste la toute dernière opération de validation. Il déclenche lui-même le build frontend via beforeBuildCommand. Le smoke Devnet opt-in est exécuté séparément avant cette dernière opération lorsqu'il appartient au checkpoint final.
26. Hors périmètre confirmé
modification du wire .kspwallet V1 sans défaut démontré
WalletPolicy / execution policy
transaction build/simulate/send
WebSocket / gRPC
Store
hardware wallet / Ledger
remote signer
mobile / browser extension
cloud custody
trading
clipboard/text secret export
filesystem watcher
RPC direct depuis frontend
prix offchain pendant 0.2.6
27. TODO 0.2.11 — intégration prix offchain
La visualisation de prix reste hors 0.2.6.
Une application spécialisée doit d'abord valider les sources offchain, normalisation, cache, rafraîchissement et UX. Ensuite 0.2.11 intégrera cette capacité dans Wallet Desk via le composant partagé retenu, sans dupliquer la logique de récupération de prix.
Aucune API fictive de prix n'est introduite dans Wallet Desk en 0.2.6.
28. Critères de sortie 0.2.6
Les critères de sortie suivants sont satisfaits au gate final pre.018-fix.001 puis publiés par rel.001 :
ksp-app-wallet-deskexiste comme application Tauri spécialisée ;- le gabarit frontend réutilise Bootstrap, Font Awesome, DataTables, DataTables Select, SimpleBar et resize-observer-polyfill ;
- Config composite sélectionne Logging/Wallet/Transport par
file_id; wallets_directoryest global etwallets_subdirectorypermet l'isolation par profil ;- le répertoire Wallet effectif absent est créé automatiquement, avec succès loggé en debug et échec en error ;
- l'inventory
.kspwalletest root-scoped et symlink-safe ; - locked ne révèle ni Pubkey, ni alias, ni notes ;
- VIEW/OWNER restent exclusivement côté Rust ;
- saisie manuelle et
KSP_SECRET_WALLET_PASS_*permettent l'ouverture ; les passwords manuels transitent uniquement frontend -> Rust et les secrets Config restent exclusivement côté Rust ; - aucune tentative de liste de secrets n'est déclenchée silencieusement à la sélection ;
- create/select/open VIEW/OWNER fonctionnent sur des fichiers réels ;
- Pubkey/alias/notes apparaissent seulement après autorisation ;
getBalancefonctionne avec VIEW et OWNER via Transport ;- import, metadata, rotations, disable/recreate VIEW et export fichier délèguent à Wallet ;
- aucun secret Wallet n'entre dans les documents JSON Config, les logs ou le storage frontend ;
- les secrets Wallet en process env/
.envrestent possédés et résolus par Config ; - capabilities Tauri sont auditées et la CSP n'est modifiée que si un besoin concret le justifie ;
- tests Rust, parcours frontend via
cargo tauri devet smoke retenu sont documentés/validés ; - README/USAGE, validation, roadmap et prompt
0.2.7sont synchronisés ; - le build Tauri final est vert et exécuté en dernière opération.
29. Addendum historique pre.001-fix.002
Les validations opérateur de pre.001 sont vertes. pre.001-fix.001 puis pre.001-fix.002 sont des correctifs documentaires : ils ne modifient pas workspace.package.version, qui reste 0.2.6-pre.1. pre.001-fix.002 corrige la règle et le plan pour supprimer toute validation frontend standalone par script npm et réserver les commandes npm directes à l'installation/mise à jour des dépendances.
La séquence Git attendue conserve l'historique réel :
v0.2.6-pre.001
v0.2.6-pre.001-fix.001
v0.2.6-pre.001-fix.002
Après application/commit du fix documentaire, pre.002 démarre la crate Tauri et le shell complet du gabarit, avec Bootstrap, Font Awesome, DataTables/Select, SimpleBar, resize-observer-polyfill, splash/main et logging bridge.
30. Addendum historique pre.014 — polish desktop partagé et lancement Tauri multi-app
La tranche harmonise Config Desk et Wallet Desk sans modifier les contrats Wallet métier :
HTML : en-têtes Khadhroony file/version sur main + splash
Splash : fade-in / minimum / fade-out distincts via Config
Splash : messages généraux défilants en bas
Splash debug : diagnostics défilants en haut uniquement en debug
Splash : aucune scrollbar fenêtre, titre central non sélectionnable
Main : pills/tabs dans une sidebar du contenu, pas dans le header
Palette : Wallet Desk revient sur la palette claire de référence Config Desk
Tauri : lancement crate-local obligatoire dans le workspace multi-app
La cause du mélange observé Config-Vite / Wallet-backend est explicitement corrigée : -c/--config fusionne une configuration avec le projet Tauri déjà découvert ; il ne choisit pas la crate. Les commandes de validation visuelle sont donc crate-locales :
(cd crates/ksp-app-config-desk && \
KSP_DESK_SPLASH_FADE_IN_MS=1500 \
KSP_DESK_SPLASH_MINIMUM_MS=6000 \
KSP_DESK_SPLASH_FADE_OUT_MS=1500 \
cargo tauri dev)
(cd crates/ksp-app-wallet-desk && \
KSP_DESK_SPLASH_FADE_IN_MS=1500 \
KSP_DESK_SPLASH_MINIMUM_MS=6000 \
KSP_DESK_SPLASH_FADE_OUT_MS=1500 \
KSP_WALLETS_DIRECTORY=var/wallet-desk-pre014 \
cargo tauri dev)
À ce stade de pre.014, le build Tauri de production était encore reporté à pre.015. Cette décision historique est superseded par l’extension décidée en pre.015 : le build final est désormais réservé à pre.018 et doit demeurer l’absolue dernière opération.
30.1 Correctif 0.2.6-pre.014-fix.001 — layout desktop et canari durable
Le retour opérateur de pre.014 confirme le lancement Tauri crate-local mais révèle plusieurs écarts purement gabarit. Le correctif conserve pre.014 comme tranche de polish et applique les décisions suivantes :
Tauri dev : crate-local, puis backend debug recale explicitement le CWD sur la racine workspace
main : row sidebar/content non wrappable + contenu min-width: 0
Wallet Desk : header typographique, shell card/card-body et footer alignés sur Config Desk
Wallet Desk : #shellStatus supprimé du HTML et du TypeScript
splash : feed #debug-info pleine largeur dans les deux Desks
pre.013 gate : le canari ne fige plus une shell_phase destinée à évoluer à chaque tranche
Le recalage du CWD n'existait alors qu'en debug_assertions dans les deux main.rs. Ce point historique est fermé par pre.018 : le runtime release résout les resources Tauri puis une racine KSP user-writable via Config avant le bootstrap ; il ne suppose plus le comportement du parcours cargo tauri dev.
Ce fix touche le frontend/build contract et porte donc le signal technique workspace.package.version = 0.2.6-pre.14.fix.1. ROADMAP.md, CHANGELOG.md et docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md restent inchangés.