Files
khadhroony-solana-project/docs/plans/013-V0_2_6_WALLET_DESK_PLAN.md
2026-08-20 14:00:34 +02:00

36 KiB
Raw Blame History

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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à :

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 :

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 :

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

cfg.std.wallet
schema.std.wallet
cfg.composite.ksp-app-wallet-desk

Fichiers physiques par défaut :

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 :

{
  "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 :

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 :

{
  "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 :

HttpTransportPool::get_balance(
    &HttpRoleName,
    &ksp_core_lib::Pubkey,
    Option<&GetBalanceConfig>,
) -> Result<GetBalanceResult>

GetBalanceResult expose :

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 :

NoSelection
Locked
ViewOpen
OwnerOpen
PrivilegedOperation

Une représentation Rust possible est :

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 :

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 :

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é :

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 :

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 :

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 :

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 :

filename
format_version
VIEW enabled/disabled
inspection status

Aucune Pubkey/alias/note locked.

10.4 Locked wallet

Actions :

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 :

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

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<WalletInventoryEntryDto> 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

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 :

wallet_id / filename
format_version
view_enabled

Aucune identité Solana ou metadata.

12.3 Authorized DTO

WalletAuthorizedDto contient :

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 :

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 :

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 :

@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 :

@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 :

@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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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.002pre.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 :

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 :

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 :

cargo test --workspace

Candidate finale Wallet Desk :

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é

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 :

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.