# Plan `0.2.6` — Wallet Desk ## 1. Objet et statut du gate `pre.001` `0.2.6` crée `ksp-app-wallet-desk`, application Tauri spécialisée et mince chargée de valider la composition réelle : ```text 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. Aucune crate `ksp-app-wallet-desk`, aucun `std.wallet` et aucun composite Wallet Desk n'existent encore dans cette base. `0.2.6-pre.001` est volontairement une tranche de **conception, inventaire, sécurité et sizing**. Elle n'ajoute ni crate Tauri, ni configuration runtime Wallet, ni dépendance frontend, ni command applicative. L'implémentation lourde commence seulement après validation opérateur du gate. Le sandbox d'audit ne fournit pas le binaire `cargo`. Le script structurel KSP a été exécuté et est propre, mais `cargo fmt --all`, `cargo check --workspace` et `cargo clippy --workspace --all-targets` ont échoué à l'invocation avec le code `127`. Le présent plan est donc une candidate `pre.001` matérialisée, **non commit-ready tant que l'opérateur n'a pas fourni les validations Cargo requises**. ## 2. Sources relues et hiérarchie appliquée La conception suit l'ordre d'autorité demandé : 1. archive KSP stable `v0.2.5` fournie ; 2. règles KSP versionnées ; 3. architecture, plans et validations actuels ; 4. contrats publics réellement exposés par les crates ; 5. sources officielles actuelles Tauri/frontend ; 6. 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` et `ksp-app-config-desk`. `docs/plans/007-V0_2_0_SERIES_PLANNING.md` conserve volontairement l'ancien forecast de `0.2.0` dans lequel Wallet Desk était numéroté `0.2.3`. Son en-tête le marque explicitement comme **plan historique clôturé**. La séquence active est celle de `ROADMAP.md` et `docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md`, où Wallet Desk est bien `0.2.6`. Ce décalage historique n'est donc pas utilisé comme autorité courante et ne justifie pas de réécrire l'historique `0.2.0`. ## 3. Frontières non négociables Direction autorisée : ```text ksp-app-wallet-desk -> ksp-config-lib -> ksp-wallet-lib -> ksp-onchain-transport-lib -> ksp-core-lib si types/erreurs partagés nécessaires -> ksp-logging-lib -> Tauri / frontend ``` Interdictions : ```text ksp-wallet-lib -X-> Config / Transport / Tauri / Store / execution policy ksp-app-wallet-desk -X-> crypto 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-> persistence password localStorage/sessionStorage ``` La seule exception Tauri au principe « pas de tracing direct hors `ksp-logging-lib` » reste l'adapter imposé par `tauri-plugin-tracing`; les événements applicatifs Rust et frontend sont réémis par la façade KSP. ## 4. Audit de `ksp-app-config-desk` comme template Tauri ### 4.1 Éléments à reprendre La structure existante est une bonne référence pour : ```text frontend/ frontend/ts/ frontend/sass/ frontend/ts/bindings/ Vite + TypeScript Bootstrap + Font Awesome splash + main TS-RS aux frontières DTO applicatives Tauri capabilities explicites bridge frontend -> command Rust -> ksp-logging-lib build frontend Tauri via beforeBuildCommand npm run check = tsc --noEmit ``` Le port Config Desk est `1430`, avec WebSocket/HMR `1431`. Wallet Desk réservera **1432** pour Vite et **1433** pour HMR afin d'éviter toute collision locale avec la première app. ### 4.2 Éléments à ne pas copier automatiquement Config Desk possède des dépendances DataTables, selection, SimpleBar et `resize-observer-polyfill` liées à ses écrans de management. Aucun besoin Wallet Desk ne justifie ces dépendances au gate `pre.001`. Le shell Wallet Desk démarre donc sans : ```text datatables.net-bs5 datatables.net-select-bs5 simplebar resize-observer-polyfill ``` Elles ne seront ajoutées ultérieurement que si un besoin mesuré apparaît. Config Desk utilise actuellement `security.csp = null`. Ce point n'est **pas** repris. Wallet Desk porte des passwords et déclenche des opérations OWNER ; une CSP locale explicite et restrictive est donc un critère de `pre.002`. ### 4.3 Logging à reprendre/refondre Le bridge éprouvé de Config Desk valide le pattern : ```text frontend helper -> invoke emit_frontend_log -> DTO validé côté Rust -> ksp-logging-lib::{trace,debug,info,warn,error}! ``` Wallet Desk doit reprendre le mécanisme, mais le formatter frontend ne doit jamais sérialiser aveuglément des objets contenant password, secret, export ou URL credentialée. Les helpers Wallet doivent journaliser des **événements sémantiques sans payload sensible**, par exemple `wallet_unlock_started`, `wallet_unlock_succeeded`, `balance_refresh_failed`. ## 5. Audit de la surface publique `ksp-wallet-lib` La release stable `0.2.5` fournit déjà les contrats nécessaires ; Wallet Desk ne doit pas les dupliquer. ### 5.1 Locked et authorized projections `LockedWalletInfo` expose seulement : ```text format_version view_enabled ``` Il n'expose ni Pubkey, ni alias, ni notes. Cette projection est la seule donnée Wallet autorisée avant unlock, hors filename/path décidé par la policy applicative. `WalletInfo` expose après autorisation : ```text format_version capability pubkey alias notes ``` ### 5.2 Handles Rust `WalletView` possède la capability VIEW et expose lecture de Pubkey/alias/notes ainsi que rotation de son credential VIEW. `WalletOwner` possède OWNER et expose : ```text sign update_alias add_note update_note delete_note rotate_owner_password rotate_view_password disable_view recreate_view export_transfer export_transfer_file ``` Les handles ne sont ni sérialisés ni envoyés au frontend. Ils restent possédés par l'état Rust Tauri. ### 5.3 Fichier et transfer Les APIs publiques stables couvrent déjà : ```text 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 stables engagés sont : ```text Solana CLI JSON Solana keypair Base58 complet ``` L'import crée un nouveau `.kspwallet` no-clobber. L'export fichier OWNER est no-clobber et permet de garder les octets secrets côté Rust. Le texte secret ne doit donc pas être exposé au frontend par défaut. ### 5.4 Conflit d'état Les mutations persistence peuvent retourner `wallet.state_conflict`. Wallet Desk le traite comme un état stale : 1. message explicite de conflit ; 2. abandon du handle VIEW/OWNER courant ; 3. purge des projections protégées ; 4. refresh/inspection locked du fichier ; 5. nouvel unlock requis. Aucun retry aveugle ni remplacement forcé n'est autorisé. ## 6. Audit Config : besoin exact de `std.wallet` ### 6.1 Constat `ConfigFileRegistry::defaults()` ne connaît aujourd'hui que : ```text cfg.std.logging schema.std.logging cfg.std.transport schema.std.transport schema.composite ``` `ConfigFileDescriptor::new` et `build_registry` sont crate-private. Wallet Desk ne peut donc pas enregistrer proprement `std.wallet` et son composite depuis l'application. **La tranche Config doit modifier `ksp-config-lib`**, puis l'application consommera uniquement la façade publique. ### 6.2 Identifiants retenus ```text cfg.std.wallet schema.std.wallet cfg.composite.ksp-app-wallet-desk ``` Fichiers physiques par défaut : ```text 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 ``` Le composite utilise le schéma générique déjà existant `schema.composite`. ### 6.3 Shape `std.wallet` Le moteur composite résout chaque `cfg.std.*` via `load_resolved_profile_with_source`; un document référencé doit donc fournir `default_profile` et `profiles` même lorsque sa seule valeur métier est globale. Shape V1 retenu pour `0.2.6` : ```json { "format_version": 1, "wallets_directory": "${KSP_WALLETS_DIRECTORY:-wallets}", "default_profile": "default", "profiles": [ { "profile_id": "default" } ] } ``` `wallets_directory` reste global parce qu'il ne varie pas par profil. Le profil vide ne porte aucune valeur métier artificielle ; il existe uniquement pour satisfaire le contrat homogène de composition/profil de Config. Aucun OWNER/VIEW password, keypair, alias, note ou secret Wallet n'entre dans Config. ### 6.4 Adapter Config -> Wallet Desk `ksp-config-lib` doit ajouter un contrat analogue à `ResolvedLoggingConfig` / `ResolvedTransportConfig`, par exemple `ResolvedWalletConfig`, contenant au minimum : ```text file_id source_path profile_id selection_source effective safe/real Config view wallets_directory: PathBuf ``` L'adapter : - résout `${KSP_WALLETS_DIRECTORY:-wallets}` via `ConfigEnvironment` ; - rejette une valeur vide ; - ancre un chemin relatif sur le current working directory comme le fait déjà l'adapter Logging ; - accepte un root absent sans le créer implicitement ; - rejette un chemin existant qui n'est pas un répertoire ; - ne dépend pas de `ksp-wallet-lib` : il produit uniquement un chemin de composition. Le nom `ResolvedWalletConfig` est retenu car le document appartient à Config et décrit la racine Wallet de l'application, sans déplacer de logique Wallet dans Config. ### 6.5 Composite Wallet Desk Shape de référence : ```json { "format_version": 1, "default_profile": "devnet", "profiles": [ { "profile_id": "devnet", "documents": [ { "component_id": "logging", "file_id": "cfg.std.logging" }, { "component_id": "transport", "file_id": "cfg.std.transport", "profile_id": "devnet_public" }, { "component_id": "wallet", "file_id": "cfg.std.wallet" } ] }, { "profile_id": "mainnet", "documents": [ { "component_id": "logging", "file_id": "cfg.std.logging" }, { "component_id": "transport", "file_id": "cfg.std.transport", "profile_id": "mainnet_public" }, { "component_id": "wallet", "file_id": "cfg.std.wallet" } ] } ] } ``` Les composants sont identifiés par `logging`, `transport`, `wallet`; les filenames ne traversent jamais le composite. Les overrides `--cfgpath`, `--schemapath` et `--filemap` restent ceux de Config. Le bootstrap Wallet Desk charge d'abord le composite, récupère les profils sélectionnés de ses trois composants, puis appelle les adapters spécialisés Config avec ces `profile_id`. Logging est initialisé une seule fois avec le profil choisi par le composite ; aucun profil Logging parallèle n'est inventé dans l'application. ## 7. Audit Transport : balance minimale Aucun nouveau wrapper Transport n'est nécessaire. La surface stable expose : ```text HttpTransportPool::get_balance( &HttpRoleName, &ksp_core_lib::Pubkey, Option<&GetBalanceConfig>, ) -> Result ``` `GetBalanceResult` expose : ```text value() -> u64 lamports context().slot() -> u64 context().api_version() -> Option<&str> ``` Le cas Wallet Desk utilise le rôle Transport `default` du profil composite et `config = None` pour le MVP, sauf besoin démontré ultérieurement. Le frontend ne fournit jamais la Pubkey à la command de refresh : Rust la prend depuis le handle VIEW/OWNER autorisé. Le DTO balance conserve les lamports en entier exact ; aucune conversion flottante n'est nécessaire à la frontière. L'UI peut afficher SOL comme format dérivé tout en conservant `lamports` comme autorité. ## 8. Runtime Rust et machine d'état État conceptuel : ```text NoSelection Locked ViewOpen OwnerOpen PrivilegedOperation ``` Une représentation Rust possible est : ```text 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` applicatif dérivé du **nom de fichier validé**, jamais un chemin arbitraire pour les opérations root-scoped. L'état Tauri doit sérialiser les opérations sur le handle, typiquement par `tokio::sync::Mutex`. Une requête réseau extrait/copie seulement la Pubkey nécessaire puis libère le verrou session avant l'I/O HTTP afin de ne pas bloquer les commandes Wallet pendant l'attente RPC. `lock` / `close` : ```text drop WalletView/WalletOwner Rust -> conserver seulement sélection + nouvelle inspection Locked -> purger WalletAuthorizedDto et balance côté frontend -> vider immédiatement les champs password DOM ``` Le changement de wallet sélectionné impose la même purge avant de charger la nouvelle projection locked. ## 9. Stratégie inventory et chemins ### 9.1 Racine La racine effective vient exclusivement de `ResolvedWalletConfig::wallets_directory()`. Si elle n'existe pas : - l'inventaire retourne un état `directory_missing` et zéro wallet ; - aucune création silencieuse n'est effectuée au bootstrap ; - une action explicite « Créer le répertoire Wallet » crée **exactement** la racine configurée, sans chemin fourni par le frontend. ### 9.2 Enumeration L'inventaire est : ```text non récursif extension exacte .kspwallet fichiers réguliers seulement symlinks exclus répertoires exclus refresh manuel refresh automatique après opération locale réussie pas de filesystem watcher en 0.2.6 ``` Chaque fichier candidat est inspecté avec `inspect_locked_wallet_file_v1`. Un fichier invalide peut apparaître comme entrée diagnostique sûre avec code d'erreur, mais jamais avec contenu brut. ### 9.3 Résolution root-scoped Create/import/select utilisent un `wallet_id`/filename validé : ```text un seul composant de chemin pas de / ou \\ pas de . ni .. extension .kspwallet requise pas de chemin absolu root.join(filename) ``` Pour une racine existante, la résolution vérifie aussi que la cible régulière canonique reste sous la racine canonique. La logique n'utilise jamais `metadata()` pour suivre implicitement un symlink d'inventaire. Le filename est une identité de stockage affichable. Il n'est jamais présenté comme alias et aucun alias n'est dérivé du filename. ### 9.4 Import/export hors racine La **destination native `.kspwallet` d'un import** reste root-scoped. La source externe est choisie par un file picker natif. L'**export OWNER** est volontairement une destination externe choisie par l'utilisateur via un save picker natif. Il n'est donc pas root-scoped, mais reste : ```text OWNER only confirmation Bootstrap avant ouverture du picker format explicite no-clobber dans ksp-wallet-lib aucun contenu secret renvoyé au frontend aucun log du chemin si sa divulgation n'est pas nécessaire ``` Aucun export clipboard/texte secret n'est prévu dans `0.2.6`. ## 10. Screen map ### 10.1 Shell principal Le shell est mono-fenêtre après splash : ```text Header runtime/profile status current wallet state lock action when authorized Sidebar / navigation Dashboard Wallets Create / Import Details Security (OWNER only) Diagnostics ``` Les écrans sont des panels d'une même fenêtre, pas une multiplication de WebViews. ### 10.2 Dashboard Affiche sans secret : ```text Config composite profile Logging status Transport profile / cluster Wallet root status selected filename si présent Locked / VIEW / OWNER state last balance status si autorisé ``` ### 10.3 Wallets Inventaire root-scoped avec : ```text filename format_version VIEW enabled/disabled inspection status ``` Aucune Pubkey/alias/note locked. ### 10.4 Locked wallet Actions : ```text Unlock VIEW si view_enabled Unlock OWNER Deselect ``` Le password est contenu dans le modal/form actif seulement et purgé après invocation. ### 10.5 Create / Import Create : filename, OWNER password, confirmation OWNER, VIEW optionnel, alias/notes initiales seulement si le contrat public les accepte sans duplication. Import : format/source inspectés côté Rust, destination `.kspwallet` root-scoped, nouveaux OWNER/VIEW passwords, metadata initiales explicitement choisies. ### 10.6 Details Après VIEW ou OWNER : ```text capability Pubkey alias notes balance lamports RPC slot / API version transport profile refresh balance ``` Les controls de mutation metadata n'apparaissent que sous OWNER. ### 10.7 Security OWNER ```text rotate OWNER password rotate VIEW password disable VIEW recreate VIEW export OWNER file ``` Chaque action destructive/privileged passe par modal Bootstrap et affiche les effets attendus avant exécution. ### 10.8 Diagnostics Surface bornée : profils Config sûrs, état pool/cluster non credentialé, dernier code d'erreur par domaine, outil de log contrôlé. Pas d'affichage de Config secret, URL réelle credentialée, password, keypair ou export. ## 11. Command map Les noms restent applicatifs et expriment une action, pas l'implémentation interne : | Command | Capability | Entrée | Sortie sûre | Délégation principale | |---|---|---|---|---| | `get_runtime_status` | aucune | — | `WalletRuntimeStatusDto` | Config/app state | | `list_wallets` | aucune | — | `Vec` | app filesystem + Wallet inspect | | `create_wallets_directory` | aucune | modal UI | `WalletOperationResultDto` | app root config | | `select_wallet` | aucune | `WalletSelectionRequestDto` | `LockedWalletDto` | app resolver + Wallet inspect | | `deselect_wallet` | aucune | — | `WalletSessionDto` | app state | | `create_wallet` | aucune | `WalletCreateRequestDto` | `WalletAuthorizedDto` OWNER | Wallet create file | | `inspect_import_source` | aucune | native picker path | `WalletTransferInspectionDto` | Wallet transfer inspect | | `import_wallet` | aucune | `WalletImportRequestDto` | `WalletAuthorizedDto` OWNER | Wallet import file | | `unlock_wallet_view` | Locked | `WalletUnlockPasswordDto` | `WalletAuthorizedDto` VIEW | Wallet open VIEW | | `unlock_wallet_owner` | Locked | `WalletUnlockPasswordDto` | `WalletAuthorizedDto` OWNER | Wallet open OWNER | | `lock_wallet` | VIEW/OWNER | — | `LockedWalletDto` | drop handle + inspect | | `refresh_wallet_balance` | VIEW/OWNER | — | `WalletBalanceDto` | Transport getBalance | | `update_wallet_alias` | OWNER | `WalletAliasMutationDto` | `WalletAuthorizedDto` | Wallet OWNER | | `add_wallet_note` | OWNER | `WalletNoteCreateDto` | `WalletAuthorizedDto` | Wallet OWNER | | `update_wallet_note` | OWNER | `WalletNoteUpdateDto` | `WalletAuthorizedDto` | Wallet OWNER | | `delete_wallet_note` | OWNER | note id | `WalletAuthorizedDto` | Wallet OWNER | | `rotate_owner_password` | OWNER | `WalletPasswordRotationDto` | operation result | Wallet OWNER | | `rotate_view_password` | OWNER | `WalletPasswordRotationDto` | operation result | Wallet OWNER | | `disable_view` | OWNER | confirmation token logique UI | `WalletAuthorizedDto` | Wallet OWNER | | `recreate_view` | OWNER | new VIEW password | `WalletAuthorizedDto` | Wallet OWNER | | `export_wallet_owner` | OWNER | format + native save path | operation result | Wallet export file | | `emit_frontend_log` | aucune | redacted log payload | `()` | Logging facade | Le « confirmation token logique UI » ne constitue pas une sécurité cryptographique : l'autorisation réelle reste OWNER côté Rust. Il sert seulement à empêcher une exécution accidentelle dans l'UX. Les commands ne font jamais confiance au frontend pour déclarer sa capability. Aucune command `sign` générique n'est exposée en `0.2.6` : Wallet Desk n'est ni un transaction builder ni une execution policy. Le contrat de signature Wallet reste validé par la crate elle-même. ## 12. DTO map et règles de secret ### 12.1 DTOs principaux ```text WalletRuntimeStatusDto WalletInventoryEntryDto WalletSessionDto LockedWalletDto WalletAuthorizedDto WalletBalanceDto WalletCreateRequestDto WalletSelectionRequestDto WalletUnlockPasswordDto WalletImportRequestDto WalletTransferInspectionDto WalletAliasMutationDto WalletNoteCreateDto WalletNoteUpdateDto WalletPasswordRotationDto WalletSecurityStatusDto WalletOperationResultDto CommandErrorDto FrontendLogPayloadDto ``` ### 12.2 Locked DTO `LockedWalletDto` contient au maximum : ```text wallet_id / filename format_version view_enabled ``` Aucune identité Solana ou metadata. ### 12.3 Authorized DTO `WalletAuthorizedDto` contient : ```text wallet_id / filename format_version capability pubkey alias notes security status sûr ``` Il n'inclut jamais les handles, key slots, ciphertexts, salts, password ou secret key material. ### 12.4 DTOs contenant password en entrée Les request DTOs password : - sont `Deserialize` + TS-RS uniquement selon besoin ; - n'utilisent jamais un `Debug` dérivé révélant les valeurs ; - ne sont pas `Clone` sans nécessité démontrée ; - sont immédiatement convertis en `OwnerPassword` / `ViewPassword` ; - ne sont jamais inclus dans `CommandErrorDto` ni les logs. Le frontend remet la valeur du champ à `""` dans un `finally` après `invoke`, succès ou erreur. ## 13. Threat model desktop | Menace | Mesure `0.2.6` | |---|---| | XSS / script injecté dans WebView | CSP restrictive, assets locaux, capabilities minimales, aucune CDN | | Password conservé frontend | aucun storage, champ vidé après invoke, pas de state global contenant le password | | Password dans logs | logging sémantique sans payload de request, DTO Debug redacted/absent | | Secret Wallet renvoyé via IPC | aucun getter/export texte ; export fichier entièrement Rust | | Identity leak locked | DTO construit exclusivement depuis `LockedWalletInfo` + filename policy | | Path traversal root-scoped | filename mono-composant validé, extension stricte, résolution Rust sous root | | Symlink escape | symlinks exclus par `symlink_metadata`, contrôle canonical lorsque possible | | TOCTOU/state stale | Wallet no-clobber/state conflict + drop handle sur conflit | | RPC credential leak | Config/Transport safe diagnostics, aucune URL brute dans DTO/error UI | | Privileged click accidentel | Bootstrap modal explicite, OWNER revalidé côté Rust | | Frontend forge capability | capability lue depuis handle Rust, jamais depuis une valeur envoyée par TS | | Export accidentel | OWNER + modal + save picker + format explicite + no-clobber | | Clipboard/history fuite secret | aucun export secret clipboard/texte en `0.2.6` | | Filesystem watcher race | aucun watcher automatique ; refresh explicite et après opérations locales | La compromission complète du processus desktop ou de l'OS reste hors du modèle de sécurité de l'application ; Wallet Desk ne promet pas de protéger un secret déjà déchiffré contre un attaquant contrôlant la mémoire du processus. ## 14. Tauri capabilities et file picker ### 14.1 Baseline `pre.002` Capabilities initiales minimales : ```text core:default tracing:default ``` Le shell n'ajoute ni filesystem plugin ni dialog plugin tant qu'aucun picker n'est consommé. ### 14.2 Import/export Lorsque l'import/export est ajouté, utiliser le plugin officiel Tauri Dialog. Les permissions doivent être granulaires : ```text dialog:allow-open dialog:allow-save ``` Ne pas utiliser `dialog:default`, car celui-ci accorde aussi les message dialogs inutiles. Les confirmations applicatives restent des modals Bootstrap. Aucun `@tauri-apps/plugin-fs` n'est nécessaire : l'inventory et toutes les opérations Wallet restent côté Rust. ## 15. Dépendances frontend/Tauri réauditées le 2026-08-20 Sources primaires/éditeur auditées : documentation Tauri v2, npm Tauri, npm `@fltsci`, Bootstrap officiel. Constats : ```text @tauri-apps/api latest 2.11.1 @tauri-apps/plugin-dialog latest 2.7.2 @fltsci/tauri-plugin-tracing latest 0.3.4 Bootstrap stable 5.3.8 ``` Les ranges workspace/template existants `^2.11`, `^0.3`, `^5.3` restent donc cohérents avec les publications actuelles auditées. Dépendances runtime initiales envisagées pour Wallet Desk : ```text @fltsci/tauri-plugin-tracing ^0.3 @fortawesome/fontawesome-free ^7.3 @tauri-apps/api ^2.11 bootstrap ^5.3 ``` Le plugin Dialog est ajouté seulement dans la tranche qui consomme réellement import/export, après réaudit exact Rust+npm de ce moment. Dev dependencies : reprendre les générations actuelles de Config Desk seulement si nécessaires : ```text @tauri-apps/cli ^2.11 @types/bootstrap ^5.2 @types/node ^26.1 sass-embedded ^1.102 typescript ^7.0 vite ^8.2 ``` Aucun DataTables/SimpleBar/resize polyfill dans la baseline. Sources : ```text 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/@tauri-apps/api https://www.npmjs.com/package/@tauri-apps/plugin-dialog https://www.npmjs.com/org/fltsci https://blog.getbootstrap.com/2025/08/25/bootstrap-5-3-8/ ``` ## 16. CSP retenue pour le shell `pre.002` doit livrer une CSP explicite adaptée aux assets locaux et à l'IPC Tauri. Le texte exact sera validé contre le bundle réel, mais les invariants sont : ```text default-src self/local protocols only script-src self + mécanismes Tauri générés style-src self (+ unsafe-inline seulement si Bootstrap/Vite l'impose réellement) font-src self img-src self/data uniquement si nécessaire connect-src IPC Tauri + dev server uniquement en dev aucune CDN / aucun remote script ``` La CSP de production ne doit pas autoriser directement les endpoints RPC Solana : le WebView n'effectue jamais la requête réseau ; Transport la fait côté Rust. ## 17. Tests déterministes ### 17.1 Config Couvrir : ```text registre cfg.std.wallet/schema/composite schema std.wallet KSP_WALLETS_DIRECTORY process/.env/fallback wallets_directory global hors profils relative path anchoring existing non-directory rejected composite devnet/mainnet -> profiles logging/wallet/transport exacts filemap overrides conservés ``` ### 17.2 Inventory/path Couvrir : ```text root missing regular .kspwallet accepted wrong extension ignored subdirectory ignored symlink ignored ../ traversal rejected absolute filename rejected separator rejected no-clobber create/import filename != alias invalid wallet safe diagnostic ``` ### 17.3 Session/DTO/security Couvrir : ```text Locked DTO sans pubkey/alias/notes VIEW projection OWNER projection lock drops capability projection selection change drops capability projection password request Debug non révélant serialized response DTOs sans secret fields state_conflict -> forced lock/refresh VIEW cannot invoke OWNER mutation successfully ``` ### 17.4 Balance Serveur HTTP local/fixture : ```text VIEW -> pubkey from Rust handle -> getBalance OWNER -> same 0 lamport accepted lamports exact slot/api_version mapped Transport error categorized safely RPC credential absent from UI error/Debug ``` ### 17.5 Commands Les tests doivent prouver que les commands délèguent : - ouverture/create/import/export/admin à `ksp-wallet-lib` ; - balance à `HttpTransportPool::get_balance` ; - Config/env aux adapters `ksp-config-lib` ; - logging à `ksp-logging-lib`. Aucune duplication crypto/JSON-RPC/Config parsing n'est tolérée. ## 18. Tests frontend Baseline obligatoire : ```bash npm run check --prefix crates/ksp-app-wallet-desk ``` Ce script reste type-check/lint/tests et ne construit pas le bundle de production. Tests ciblés à valeur élevée à considérer : ```text password input cleared in finally locked -> authorized -> locked DOM purge OWNER controls hidden/disabled under VIEW privileged modal required before destructive action balance refresh state transitions ``` Ils ne justifient pas à eux seuls l'introduction d'un framework frontend lourd si des tests simples suffisent. ## 19. Smoke Devnet opt-in Wallet Desk est la première surface appropriée pour un smoke durable de composition : ```text Config composite Devnet -> wallet test-only/éphémère -> open VIEW ou OWNER -> Pubkey -> HttpTransportPool getBalance role default -> réponse RPC valide, y compris 0 lamport ``` Le smoke : - est opt-in ; - utilise un wallet créé dans un répertoire temporaire ; - ne nécessite aucun secret de production ; - ne nécessite aucun wallet financé ; - ne persiste pas de password dans Config/.env ; - distingue skip/absence de réseau d'un résultat exécuté avec succès. Ce smoke remplace conceptuellement la destination transitoire Config des anciens smokes cross-crates ; il ne déplace rien dans `ksp-config-lib`. ## 20. Logging et redaction Chaque interaction importante produit un événement debug/trace sans contenu secret : ```text navigation inventory refresh wallet select/deselect create/import result unlock VIEW/OWNER result lock balance refresh result metadata mutation result rotation/disable/recreate result modal open/confirm/cancel export start/result state conflict DOM/state replacement significatif ``` Interdictions absolues dans message/champs : ```text password secret bytes / keypair transfer payload secret owner/view key material RPC URL credentialée Config KSP_SECRET value notes/alias en contenu brut sauf besoin diagnostic explicitement autorisé ``` Les fichiers de logs gardent la politique KSP unique par lancement. ## 21. Import/export UX retenue ### Import Flow : ```text Open native picker -> Wallet inspect transfer (Pubkey + format only) -> choose destination filename under configured root -> enter new OWNER password + optional VIEW password -> optional alias/notes -> import_wallet_transfer_file_v1 -> authorized OWNER projection ``` La source externe n'est jamais transformée côté TypeScript. ### Export Flow : ```text OWNER open -> choose format -> Bootstrap privileged confirmation -> native Save picker -> export_wallet_transfer_file -> success status only ``` Le frontend ne reçoit jamais les octets exportés. ## 22. Administration OWNER retenue pour `0.2.6` Le sizing est positif pour inclure dans la release : ```text alias mutation notes CRUD rotate OWNER password rotate VIEW password disable VIEW strong recreate VIEW strong OWNER file export CLI JSON/Base58 ``` L'import CLI JSON/Base58 est également retenu parce que la surface Wallet est déjà stable et qu'il valide un workflow réel de migration vers `.kspwallet`. La signature arbitraire n'obtient pas d'écran dédié en `0.2.6` : elle appartient au contrat Wallet mais ne sert pas la mission Wallet Desk identité/administration/balance et pourrait être confondue avec une future execution policy. ## 23. Sizing détaillé Le forecast initial `pre.002`–`pre.010` est trop dense si chaque tranche doit rester petite. Les points qui justifient une regranularisation sont : - Config nécessite une modification réelle de `ksp-config-lib`, schema, adapter et composite ; - inventory/path safety mérite une tranche autonome avant les secrets ; - create et unlock/session sont séparés ; - import ajoute une nouvelle surface native Dialog ; - metadata et credential security doivent être testées séparément ; - export secret mérite sa propre revue ; - integration/security/smoke doit rester distinct de la documentation/build final. Prévision souple recalibrée : ```text pre.001 audit + Config/Wallet/Transport/Tauri + UX + threat model + sizing pre.002 crate Tauri + frontend shell + splash/main + logging bridge + CSP + ports pre.003 cfg.std.wallet + schema + ResolvedWalletConfig + composite Wallet Desk pre.004 runtime state + inventory/path safety + locked inspection + DTO foundation pre.005 create .kspwallet + OWNER session + explicit root-directory flow pre.006 unlock VIEW/OWNER + lock/deselect + authorized details + purge guarantees pre.007 Transport pool from composite + getBalance + diagnostics/refresh pre.008 native Dialog open + inspect/import CLI JSON/Base58 + destination no-clobber pre.009 OWNER metadata administration alias/notes + state_conflict UX pre.010 rotations OWNER/VIEW + disable/recreate VIEW + capability/security UX pre.011 OWNER file export + save picker + privileged confirmation + leak audit pre.012 deterministic integration tests + frontend security checks + Devnet smoke opt-in + compliance pre.013 README/USAGE/docs/graphs + final dependency audit + production Tauri build candidate + prompt 0.2.7 rel.001 publication stable stricte ``` Ce forecast reste souple. Un `fix` ou une prerelease supplémentaire est préférable à regrouper artificiellement des surfaces sensibles. ## 24. Gates par tranche ### `pre.002` Pas de Wallet métier. Gate : app démarre, splash/main/logging/CSP/capabilities/build layout corrects. ### `pre.003` Pas d'inventory Wallet avant que Config soit capable de produire le root + composite de manière normative. ### `pre.004` Pas de password avant que la résolution des chemins et la projection locked soient testées. ### `pre.005` / `pre.006` Pas de balance avant que le lifecycle Rust VIEW/OWNER + purge soit fiable. ### `pre.007` MVP fonctionnel minimal atteint quand un wallet VIEW/OWNER ouvert affiche identité autorisée + balance HTTP. ### `pre.008+` Administration enrichie sans réduire les garanties du MVP. ### `pre.012` Aucune clôture sans audit secret/DTO/paths/capabilities/CSP/state conflict et workspace tests. ### `pre.013` `cargo tauri build` est la dernière opération de validation de la candidate, conformément aux règles KSP. ## 25. Validation opérateur prévue Après chaque tranche Rust : ```bash 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 ; checkpoints globaux : ```bash cargo test --workspace ``` Candidate finale Wallet Desk : ```bash cargo fmt --all python3 scripts/audit_rust_workspace_rules.py cargo check --workspace cargo clippy --workspace --all-targets cargo test -p ksp-app-wallet-desk cargo test --workspace npm run check --prefix crates/ksp-app-wallet-desk cargo tauri build -c crates/ksp-app-wallet-desk/tauri.conf.json ``` Le smoke Devnet opt-in est exécuté séparément avec sa commande documentée. ## 26. Hors périmètre confirmé ```text modification .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 explicitement hors `0.2.6`. `0.2.10` doit d'abord produire le transport/service offchain spécialisé puis `0.2.11` une application de visualisation/validation. Une fois ces contrats stabilisés, `0.2.11` intégrera cette capacité dans Wallet Desk sans dupliquer récupération, normalisation, cache ou policy de refresh. Wallet Desk `0.2.6` ne prépare aucune API fictive de prix et n'ajoute aucune dépendance offchain anticipée. ## 28. Critères de sortie `0.2.6` La release stable n'est clôturable que si : - Config composite sélectionne Logging/Wallet/Transport par `file_id` ; - `KSP_WALLETS_DIRECTORY` est résolu uniquement par Config ; - inventory/path safety est root-scoped et symlink-safe selon la policy ci-dessus ; - Locked ne révèle ni Pubkey, ni alias, ni notes ; - VIEW/OWNER sont détenus uniquement côté Rust ; - lock/deselect/state conflict purgent les projections protégées ; - create/select/open VIEW/OWNER fonctionnent sur `.kspwallet` réel ; - Pubkey/alias/notes apparaissent seulement après autorisation ; - `getBalance` fonctionne avec VIEW et OWNER via le Transport du composite ; - metadata/rotations/disable/recreate/import/export retenus délèguent à Wallet ; - aucun password/secret key/export payload n'entre dans Config, logs ou storage frontend ; - CSP et capabilities Tauri sont minimales et auditées ; - validations Rust/frontend/Tauri sont vertes avec preuves opérateur ; - smoke Devnet opt-in est disponible et documenté ; - README/USAGE, roadmap, plan et prompt `0.2.7` sont synchronisés. ## 29. Suite immédiate après validation de `pre.001` Après application de ce delta, l'opérateur doit exécuter la validation d'ouverture sur sa machine Cargo. Si elle est verte, `pre.001` peut être commitée sous : ```text v0.2.6-pre.001 ``` `pre.002` commence alors par la crate/shell Tauri, le bridge Logging, les ports 1432/1433 et la CSP — **sans encore implémenter Wallet/Config métier**.