36 KiB
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é :
- archive KSP stable
v0.2.5fournie ; - règles KSP versionnées ;
- architecture, plans et validations actuels ;
- contrats publics réellement exposés par les crates ;
- sources officielles actuelles Tauri/frontend ;
- archive bot3 fournie, uniquement comme référence historique.
Les documents obligatoires du prompt d'ouverture ont été relus avant conception. Les surfaces ont ensuite été réauditées dans ksp-core-lib, ksp-logging-lib, ksp-config-lib, ksp-onchain-transport-lib, ksp-wallet-lib 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 :
- message explicite de conflit ;
- abandon du handle VIEW/OWNER courant ;
- purge des projections protégées ;
- refresh/inspection locked du fichier ;
- 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}viaConfigEnvironment; - 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_missinget 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
Debugdérivé révélant les valeurs ; - ne sont pas
Clonesans nécessité démontrée ; - sont immédiatement convertis en
OwnerPassword/ViewPassword; - ne sont jamais inclus dans
CommandErrorDtoni 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.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 :
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_DIRECTORYest 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
.kspwalletréel ; - Pubkey/alias/notes apparaissent seulement après autorisation ;
getBalancefonctionne 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.7sont 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.