Files
khadhroony-solana-project/docs/plans/013-V0_2_6_WALLET_DESK_PLAN.md

52 KiB
Raw Blame History

Plan 0.2.6 — Wallet Desk

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 nest 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 dinventaire .env.example qui confondait un fragment didentifiant 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 à lapplication.

2. Sources relues et hiérarchie appliquée

L'audit 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
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 lapplication. 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 de wallets_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 native picker path transfer inspection DTO Wallet inspect transfer
import_wallet import request authorized OWNER Wallet import file
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 Debug révélant la valeur ;
  • ne sont pas Clone sans besoin ;
  • sont convertis immédiatement en OwnerPassword / ViewPassword ;
  • ne sont jamais inclus dans un CommandErrorDto ou 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

Lorsque les pickers sont réellement ajoutés :

dialog:allow-open
dialog:allow-save

Aucun plugin filesystem n'est nécessaire pour l'inventory : Rust effectue les opérations côté application.

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

picker open
-> inspect_wallet_transfer_file
-> choix destination sous root effectif
-> nouveaux credentials KSP
-> import_wallet_transfer_file_v1
-> no-clobber
-> session OWNER

La source n'est jamais parsée en TypeScript.

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 :

cargo tauri dev -c crates/ksp-app-wallet-desk/tauri.conf.json

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.

Le plugin Dialog n'est ajouté qu'à la tranche import/export qui le consomme et sa version Rust/npm est vérifiée à ce moment.

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.003std.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. Linventory .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.

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.

pre.006 — unlock manuel + KSP_SECRET_WALLET_PASS_*

Objectifs :

unlock VIEW manuel
unlock OWNER manuel
Config-owned wallet secret candidate discovery
filename normalization
numeric/named candidate ordering
unlock VIEW avec secret configuré
unlock OWNER avec secret configuré
secrets Config jamais Rust -> frontend ; password manuel request-only frontend -> Rust
zeroization via OwnerPassword/ViewPassword
UX coût Argon2 / tentative explicite

Tests spécifiques de redaction, ordre et non-auto-unlock.

pre.007 — détails autorisés + balance HTTP

Objectifs :

WalletAuthorizedDto
Pubkey / alias / notes
HttpTransportPool depuis composite
getBalance VIEW
getBalance OWNER
lamports + SOL formaté + slot/api_version
refresh manuel
diagnostics Transport sûrs

À ce point le MVP obligatoire est complet : wallet autorisé + identité + balance réseau.

pre.008 — import Solana CLI JSON / Base58

Objectifs :

plugin Dialog open minimal
picker source
inspect transfer sûr
import destination root-scoped
nouveaux OWNER/VIEW credentials
no-clobber
inventory/session refresh

Le contenu secret de la source importée reste côté Rust. Les nouveaux passwords éventuellement saisis dans l'UI transitent uniquement dans la request Tauri HTML/TypeScript -> Rust, puis sont purgés côté frontend ; ils ne reviennent jamais dans une réponse.

pre.009 — metadata OWNER

Objectifs :

update alias
notes add/update/delete
controls OWNER
projection refresh
wallet.state_conflict UX
DataTable/details coherence après mutation

pre.010 — rotations credentials

Objectifs :

rotate OWNER password
rotate VIEW password
password confirmation UX
configured-secret interaction après rotation
session/projection coherence
redaction tests

pre.011 — strong VIEW disable/recreate

Objectifs :

disable_view
recreate_view
security status DTO
VIEW enabled/disabled dans DataTable
modals Bootstrap privilégiés
réouverture VIEW vérifiée

pre.012 — export OWNER fichier

Objectifs :

plugin Dialog save minimal
choix CLI JSON/Base58
confirmation OWNER
export_transfer_file
no-clobber
aucun export texte/clipboard
leak audit ciblé

pre.013 — intégration, compliance, sécurité et smoke

Objectifs :

tests déterministes de composition Config -> Wallet -> Transport
inventory/path adversarial
dto/debug/log secret audit
frontend state purge/security checks
capabilities audit
cargo tree / inverse tree pertinents
Devnet smoke opt-in
validation/009 ou numéro courant dédié à 0.2.6
workspace checkpoint complet

Cette tranche doit produire la preuve que les surfaces ajoutées fonctionnent ensemble avant la documentation de clôture.

pre.014 — documentation finale, candidate build et prompt suivant

Dernière tranche prévue avant rel.001 :

crates/ksp-app-wallet-desk/README.md
crates/ksp-app-wallet-desk/USAGE.md
docs/architecture / inventory / dependency graph synchronisés si impactés
docs/validation 0.2.6 finalisée
ROADMAP / functional sequence / CHANGELOG synchronisés
dépendances et versions finales réauditées
commandes smoke documentées
prompt de démarrage 0.2.7 préparé
validation Rust workspace finale
parcours fonctionnel cargo tauri dev final
cargo tauri build en toute dernière opération

Si le build final ou la documentation révèle une correction technique, utiliser pre.014-fix.NNN ou insérer une tranche supplémentaire. pre.014 n'est pas une obligation de clôture artificielle.

rel.001 — publication stable

Après preuves finales :

version stable 0.2.6
commit rel.001
publication/tag v0.2.6 selon workflow KSP
aucune nouvelle fonctionnalité introduite dans rel.001

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 :

cargo tauri dev -c crates/ksp-app-wallet-desk/tauri.conf.json

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
cargo tauri dev -c crates/ksp-app-wallet-desk/tauri.conf.json
cargo tauri build -c crates/ksp-app-wallet-desk/tauri.conf.json

cargo tauri build -c crates/ksp-app-wallet-desk/tauri.conf.json 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

La release stable est clôturable si :

  • ksp-app-wallet-desk existe 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_directory est global et wallets_subdirectory permet 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 .kspwallet est 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 ;
  • getBalance fonctionne 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/.env restent 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 dev et smoke retenu sont documentés/validés ;
  • README/USAGE, validation, roadmap et prompt 0.2.7 sont synchronisés ;
  • le build Tauri final est vert et exécuté en dernière opération.

29. Suite après 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.