7.7 KiB
Delta 0.2.6-pre.017 — migration explicite .kspwallet V1 -> V2 et canaris de persistence
Base requise
0.2.6-pre.016-fix.002 appliquée
workspace.package.version = 0.2.6-pre.16.fix.2
Le checkpoint opérateur de cette base est intégralement vert :
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 78 passed / 1 ignored
Wallet Desk desktop_contract 16/16 OK
Wallet Desk release_compliance 4/4 OK
smokes réseau / benchmark ignored comme prévu
pre.016-fix.002 est donc accepté comme base de pre.017.
Signal technique
Cette tranche ajoute des APIs/runtime/persistence de migration Wallet :
workspace.package.version = 0.2.6-pre.17
commit = v0.2.6-pre.017
Aucun tag prerelease. Aucun package.json/tauri.conf.json n'est modifié.
Objectif de la tranche
pre.017 ferme le chantier fonctionnel V1/V2 avant la candidate documentaire pre.018 :
migration V1 -> V2 explicite
OWNER authentifie toujours la source
migration mémoire
copie filesystem no-clobber
remplacement in-place atomique et stale-protected
identité Solana conservée
metadata protégées conservées exactement
note IDs stables conservés
VIEW enabled/disabled conservé
credential VIEW cible explicite si VIEW est activé
aucune migration implicite lors d'un open/inspect
canaris tampering / no-clobber / stale-state
régression Wallet Desk non-migrante
APIs publiques de migration
Snapshot mémoire
migrate_wallet_v1_to_v2(
source,
owner_password,
target_view_password,
)
Le source doit être un V1 strict. La fonction :
- parse le V1 ;
- authentifie OWNER ;
- récupère en mémoire l'identité Solana autorisée et le payload metadata exact ;
- construit un nouvel envelope V2 ;
- restaure alias, notes et identifiants stables de notes dans le payload V2 ;
- retourne un
WalletOwnerV2.
Aucun octet source n'est modifié.
Migration vers une nouvelle destination
migrate_wallet_file_v1_to_v2(
source,
destination,
owner_password,
target_view_password,
)
La source V1 est conservée. La destination V2 utilise la persistence no-clobber existante : une destination existante retourne wallet.destination_exists et reste inchangée.
Migration in-place
migrate_wallet_file_v1_to_v2_in_place(
source,
owner_password,
target_view_password,
)
L'enveloppe V1 observée avant OWNER unlock devient l'état authentifié attendu. Après construction complète du V2, la publication passe par le remplacement atomique V1 existant :
relire source bornée
parse V1 strict
vérifier signature OWNER
comparer à expected V1
écrire/sync temp
revérifier expected V1 juste avant publication
rename atomique
sync fichier / parent best-effort
Un autre état V1 valide publié entre-temps provoque wallet.state_conflict et n'est jamais écrasé.
Politique credential VIEW pendant migration
V1 et V2 ont des domaines/AAD distincts. Le VIEW key-wrap V1 ne peut donc pas être réutilisé directement dans V2.
Règle pre.017 :
source V1 VIEW disabled -> target_view_password = None
source V1 VIEW enabled -> target_view_password = Some(...)
Lorsque VIEW est activé, le credential cible peut être :
- le même mot de passe VIEW que V1 ;
- un nouveau mot de passe VIEW choisi pendant la migration.
Cela ne constitue pas une élévation supplémentaire : OWNER possède déjà en V1/V2 l'autorité de rotation du credential VIEW sans connaître l'ancien mot de passe VIEW.
La migration pure ne change toutefois pas la forme de capability : elle ne peut ni activer VIEW sur une source désactivée ni supprimer VIEW sur une source activée.
Une incohérence de cette politique retourne le nouveau code :
wallet.migration_invalid
Ce qui est préservé et ce qui est renouvelé
Préservé :
Solana keypair / Pubkey
alias protégé
notes protégées
note IDs stables
état VIEW enabled/disabled
credential OWNER logique fourni par le caller
Renouvelé en V2 :
owner_root
metadata_key
secret_key
OWNER auth Ed25519 key
OWNER/VIEW slot IDs
Argon2 salts
XChaCha nonces
ciphertexts
state signature
AAD/transcripts V2
La migration n'est donc jamais une transformation syntaxique JSON -> binaire des ciphertexts existants. Elle ouvre/authentifie V1 puis reconstruit cryptographiquement un document V2.
Secret lifetime interne
OwnerPassword reste publiquement non-Clone. La migration doit toutefois dériver :
V1 OWNER KDF pour authentifier la source
V2 OWNER KDF pour créer la cible
ksp-wallet-lib ajoute donc uniquement un duplicate interne court-vivant du wrapper OWNER, non exposé aux consumers et toujours zéroïsé au drop. Aucun contrat public Clone n'est ajouté à OwnerPassword ou ViewPassword.
Canaris ksp-wallet-lib
Nouveau module unit_tests/migration.rs :
- migration V1 full-vector -> V2 ;
- identité Solana conservée ;
- alias/notes/note IDs conservés ;
- OWNER V2 rouvert avec le même credential OWNER ;
- VIEW V2 rouvert avec le credential cible ;
- ancien VIEW refusé lorsqu'un nouveau credential cible est choisi ;
- forme VIEW activée/désactivée obligatoirement conservée ;
- copie vers destination existante = no-clobber ;
- copie réussie laisse V1 source inchangé ;
- migration in-place produit un V2 ouvrable via APIs génériques ;
- tampering OWNER-authenticated V1 rejeté avant publication ;
- remplacement concurrent par un autre V1 valide =
wallet.state_conflict; - état concurrent plus récent conservé.
Le public API canary expose les trois fonctions de migration et ERROR_CODE_MIGRATION_INVALID depuis la racine de crate.
Régression Wallet Desk
Wallet Desk ne reçoit volontairement aucun bouton/commande de migration automatique dans cette tranche.
Il reste sur :
create/import génériques -> V2
open OWNER/VIEW générique -> V1 ou V2
inventory inspect générique -> V1 ou V2
Un nouveau canari pre_017_wallet_desk_open_paths_remain_non_migrating interdit l'introduction d'un appel migrate_wallet_* dans les chemins normaux app_state/inventory.
Cette séparation garantit qu'ouvrir un ancien V1 dans Wallet Desk ne modifie jamais le fichier à l'insu de l'utilisateur.
Documentation synchronisée
Cette tranche met à jour :
docs/formats/KSPWALLET_V2.md
docs/plans/013-V0_2_6_WALLET_DESK_PLAN.md
docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md
docs/validation/009-V0_2_6_WALLET_DESK_COMPLIANCE.md
crates/ksp-wallet-lib/README.md
crates/ksp-wallet-lib/USAGE.md
prompts/011-V0_2_6_START_PROMPT.md
docs/000-README.md
docs/plans/000-README.md
ROADMAP.md état global uniquement
CHANGELOG.md reste réservé à pre.018/clôture de release conformément à la politique KSP.
Validation opérateur requise
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
Aucun cargo tauri build dans cette tranche.
Si ce checkpoint est vert, la prochaine tranche est :
0.2.6-pre.018
avec documentation/candidate finale, fermeture CWD/resources du bundle, parcours Tauri final, prompt 0.2.7, puis cargo tauri build comme toute dernière opération de validation.