# Delta `0.2.6-pre.016` — runtime `.kspwallet` V2, APIs multi-version et Wallet Desk version-neutral ## Base requise ```text 0.2.6-pre.015-fix.001 appliquée workspace.package.version = 0.2.6-pre.15.fix.1 ``` Le checkpoint opérateur de cette base est intégralement vert : ```text cargo fmt --all OK python3 scripts/audit_rust_workspace_rules.py clean cargo check --workspace OK cargo clippy --workspace --all-targets OK cargo test --workspace OK ksp-wallet-lib 71 passed / 1 ignored Wallet Desk release_compliance 4/4 OK smokes réseau / benchmark ignored comme prévu ``` Le correctif `pre.015-fix.001` est donc accepté comme base de `pre.016`. ## Signal technique Cette tranche modifie les APIs/runtime Wallet et le backend Wallet Desk : ```text workspace.package.version = 0.2.6-pre.16 commit = v0.2.6-pre.016 ``` Aucun tag prerelease. Les `package.json`/`tauri.conf.json` ne sont pas modifiés : aucune dépendance frontend, configuration Tauri, permission ou contrat de bundle n'est changé. Le signal technique Rust reste porté par `workspace.package.version`. ## Objectif de la tranche `pre.016` matérialise le contrat décidé en `pre.015` : V2 devient le **format natif default explicite** des créations/imports non versionnés, sans rendre le default dépendant de la dernière version supportée. La tranche ajoute simultanément : ```text runtime cryptographique V2 create/open VIEW/OWNER persistence V2 create no-clobber + remplacement atomique state-conflict V2 authentifié façade in-memory multi-version façade filesystem multi-version variantes explicites V1/V2 import transfer default V2 + variantes V1/V2 handles OWNER/VIEW version-neutral administration V2 complète Wallet Desk exclusivement sur APIs génériques canaris default/latest, V1/V2 et strict version dispatch ``` La migration persistée V1 -> V2 reste volontairement hors scope et appartient à `pre.017`. ## Politique durable : default distinct de latest La politique est désormais matérialisée dans `ksp-wallet-lib` : ```rust DEFAULT_WALLET_FORMAT = WalletFormat::V2 LATEST_SUPPORTED_WALLET_FORMAT = WalletFormat::V2 ``` `WalletFormat` est public et `non_exhaustive` afin qu'un futur V3 puisse être ajouté sans inviter les consumers à supposer que V2 est la dernière version possible. Invariant durable : ```text DEFAULT_WALLET_FORMAT != conceptuellement LATEST_SUPPORTED_WALLET_FORMAT ``` Les deux valeurs sont V2 dans cette release, mais leur égalité est contingente. Si V3 apparaît plus tard : ```text DEFAULT_WALLET_FORMAT peut rester V2 LATEST_SUPPORTED_WALLET_FORMAT peut devenir V3 ``` Une nouvelle version supportée ne change jamais automatiquement le default. ## APIs de création ### In-memory ```text create_wallet(...) -> DEFAULT_WALLET_FORMAT, donc V2 create_wallet_v1(...) -> V1 forcé create_wallet_v2(...) -> V2 forcé ``` ### Filesystem ```text create_wallet_file(...) -> DEFAULT_WALLET_FORMAT, donc V2 create_wallet_file_v1(...) -> V1 forcé create_wallet_file_v2(...) -> V2 forcé ``` Le type version-neutral `WalletCreateMetadata` est exposé pour les nouvelles APIs ; `WalletCreateMetadataV1` reste conservé pour compatibilité explicite V1. Le profil plaintext initial de metadata V2 conserve la sémantique établie en V1, conformément à la spec V2. ## APIs de lecture et inspection Les APIs génériques détectent uniquement le framing nécessaire à la sélection du parser : ```text open_wallet_view(...) open_wallet_owner(...) inspect_locked_wallet(...) open_wallet_view_file(...) open_wallet_owner_file(...) inspect_locked_wallet_file(...) ``` Dispatch : ```text JSON dont le premier octet significatif est '{' -> parser strict V1 magic binaire KSPWALLET + version 2 -> parser strict V2 autre framing/version -> erreur format/version ``` La détection n'est jamais une validation de substitution : le parser V1/V2 sélectionné reste autoritatif pour toutes les contraintes structurelles et cryptographiques. Les APIs explicitement versionnées restent strictes : ```text *_v1 refuse V2 *_v2 refuse V1 ``` ## Runtime V2 `wallet_v2.rs` compose le wire/transcript figé en `pre.015` avec les primitives Wallet existantes : ```text Argon2id password KDF XChaCha20-Poly1305 key wrapping XChaCha20-Poly1305 OWNER-CONTROL / METADATA / SECRET Ed25519 OWNER state signature VIEW / OWNER indépendants Solana keypair secret compartment ``` V2 conserve le profil plaintext fonctionnel initial de V1 pour owner-control, metadata et secret, mais utilise exclusivement : ```text wire V2 IDs V2 AAD V2 state transcript V2 format_version = 2 ``` Les domains V1/V2 restent non interchangeables. ## Handles OWNER / VIEW version-neutral `WalletOwner` et `WalletView` ne sont plus couplés à `OwnerStateV1` / `ViewStateV1`. Un dispatch runtime interne conserve le format authentifié : ```text OwnerState::V1 / OwnerState::V2 ViewState::V1 / ViewState::V2 ``` Les opérations publiques restent stables et dispatchent dans le format du handle : ```text signature OWNER metadata alias/notes rotation OWNER rotation VIEW par OWNER VIEW self-rotation disable VIEW fort recreate VIEW fort export transfer ``` Une mutation V1 reste V1. Une mutation V2 reste V2. Aucune de ces opérations ne constitue une migration. ### Sérialisation des handles Nouvelle surface : ```text to_native_bytes() -> V1 JSON si handle V1, V2 binaire si handle V2 ``` Compatibilité historique : ```text to_json_bytes() -> fonctionne uniquement pour V1 rejette V2 ``` Il n'existe aucune transcodification implicite V2 -> V1 ou V1 -> V2. ## Persistence V2 et stale-handle protection La création V2 conserve les garanties V1 : ```text construction complète en mémoire écriture temporaire sync publication no-clobber aucun overwrite d'une destination existante ``` Les mutations V2 réutilisent la discipline atomique avec une vérification format-spécifique : ```text relire destination bornée parse_binary V2 strict vérifier state signature V2 comparer l'enveloppe authentifiée attendue publier seulement si état courant identique ``` Un handle V2 stale reçoit le même contrat `wallet.state_conflict` qu'un handle V1. ## Import transfer Les formats de transfert externes restent indépendants du format natif `.kspwallet` : ```text Solana CLI JSON Solana keypair Base58 complet ``` La sortie native suit désormais : ```text import_wallet_transfer(...) -> V2 default import_wallet_transfer_v1(...) -> V1 forcé import_wallet_transfer_v2(...) -> V2 forcé import_wallet_transfer_file(...) -> V2 default import_wallet_transfer_file_v1(...) -> V1 forcé import_wallet_transfer_file_v2(...) -> V2 forcé ``` La keypair Solana reste identique ; seul le nouvel environnement natif KSP est créé dans la version choisie. ## Wallet Desk `ksp-app-wallet-desk` ne référence plus aucune API native versionnée en production. Ses callsites utilisent uniquement : ```text create_wallet_file import_wallet_transfer open_wallet_view_file open_wallet_owner_file inspect_locked_wallet_file ``` Conséquences : ```text nouveau Wallet créé depuis le Desk V2 nouvel import depuis le Desk V2 wallet V1 existant toujours inspectable/ouvrable wallet V2 inspectable/ouvrable mutations après unlock format natif conservé frontend ne connaît pas V1/V2 pour dispatcher ``` La version reste visible dans les projections métier (`format_version`) mais n'est pas une responsabilité de routing du frontend/Tauri. ## Canaris `pre.016` Les nouveaux tests couvrent notamment : ```text DEFAULT_WALLET_FORMAT = V2 LATEST_SUPPORTED_WALLET_FORMAT = V2 framing detector V1/V2 création V2 + open générique VIEW/OWNER lecture générique d'une fixture V1 stable création fichier générique -> V2 metadata V2 persistée rotation OWNER V2 rotation VIEW V2 par OWNER disable/recreate VIEW V2 VIEW self-rotation V2 wire V2 conservé après administration lecteur V1 forcé refuse V2 lecteur V2 forcé refuse V1 import transfer générique -> V2 import transfer V1 explicite toujours disponible Wallet Desk sans callsite *_v1/*_v2 ``` Les anciens canaris Desk `pre.006`/`pre.009` sont réalignés sur la façade générique sans supprimer leurs garanties de sécurité historiques. ## Hors scope `pre.016` ne réalise pas : ```text migration V1 -> V2 réécriture automatique à l'ouverture suppression des APIs V1 V3 / second facteur changement de default futur automatique frontend spécifique au format nouvelle permission Tauri build Tauri de production ``` ## Documentation synchronisée Cette tranche met à jour : ```text ROADMAP.md crates/ksp-wallet-lib/README.md crates/ksp-wallet-lib/USAGE.md docs/IDEAS.md docs/architecture/004-COMPONENT_INVENTORY.md docs/formats/KSPWALLET_V2.md docs/plans/013-V0_2_6_WALLET_DESK_PLAN.md docs/validation/009-V0_2_6_WALLET_DESK_COMPLIANCE.md prompts/011-V0_2_6_START_PROMPT.md ``` `CHANGELOG.md` reste réservé à la clôture `pre.018`. ## Prochaine tranche `0.2.6-pre.017` doit maintenant porter la migration **explicite et authentifiée** V1 -> V2 : ```text API de migration nommée explicitement source V1 authentifiée nouveau document V2 cryptographiquement valide aucune migration lors d'un open ordinaire stratégie destination/no-clobber ou remplacement explicitement choisie préservation identité Solana + metadata + policy VIEW applicable canaris stale/tampering/failure atomicity régression complète Wallet Desk V1/V2 ``` La candidate/documentation finale reste `pre.018`. ## Validation opérateur requise Après application du delta : ```bash cargo fmt --all python3 scripts/audit_rust_workspace_rules.py cargo check --workspace cargo clippy --workspace --all-targets cargo test -p ksp-wallet-lib cargo test -p ksp-app-wallet-desk cargo test --workspace ``` Un contrôle fonctionnel Wallet Desk est également utile avant `pre.017` : ```bash (cd crates/ksp-app-wallet-desk && cargo tauri dev) ``` Vérifier au minimum : ```text création -> format_version 2 import -> format_version 2 inventory V1/V2 unlock OWNER/VIEW metadata + rotations sur V2 balance depuis handle autorisé ``` **Ne pas exécuter `cargo tauri build` en `pre.016`.** Le build de production reste réservé à `pre.018` et doit demeurer l'ultime opération de validation de la candidate.