v0.2.6-pre.018
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
# file: Cargo.toml
|
||||
# version: 189
|
||||
# version: 190
|
||||
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = ["crates/ksp-app-config-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-logging-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-wallet-lib"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.2.6-pre.17"
|
||||
version = "0.2.6-pre.18"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"
|
||||
@@ -16,13 +16,14 @@ publish = false
|
||||
[workspace.dependencies]
|
||||
argon2 = { version = "^0.5", default-features = false }
|
||||
chacha20poly1305 = { version = "^0.11", default-features = false }
|
||||
directories = { version = "^6.0" }
|
||||
ed25519-dalek = { version = "^3.0", default-features = false }
|
||||
getrandom = { version = "^0.4", default-features = false }
|
||||
base64 = { version = "^0.23" }
|
||||
fs2 = { version = "^0.4" }
|
||||
serde = { version = "^1.0" }
|
||||
serde_json = { version = "^1.0" }
|
||||
jsonschema = { version = "^0.49", default-features = false }
|
||||
jsonschema = { version = "^0.50", default-features = false }
|
||||
reqwest = { version = "^0.13", default-features = false }
|
||||
solana-keypair = { version = "^3.1", default-features = false }
|
||||
solana-pubkey = { version = "^4.3", default-features = false }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: ROADMAP.md -->
|
||||
<!-- version: 78 -->
|
||||
<!-- version: 79 -->
|
||||
|
||||
# Roadmap KSP
|
||||
|
||||
@@ -50,7 +50,7 @@ Le roadmap décrit les objectifs à atteindre et les grandes étapes prévues. U
|
||||
- [X] `0.2.3` — HTTP Transactions stable : 11/11 wrappers typés publiés, classification `8 Read / 2 WriteSubmission / 1 Simulation`, no-resend ambigu prouvé pour les write submissions, `KSP-TRANSPORT-007` réaudité conforme sur les 37 wrappers HTTP courants, graphes Cargo et deux smokes Devnet validés ; `0.2.4` reprend les 15 Blocks/Economics restants.
|
||||
- [X] `0.2.4` — HTTP Blocks + Economics stable : 15/15 wrappers `V0_2_4` publiés, surface typed complète à 52/52 méthodes courantes, 14/14 historiques conservées, réaudit SIMD/inventaire final et `KSP-TRANSPORT-007` global validés ; deux smokes Devnet passés avant publication.
|
||||
- [X] `0.2.5` — Wallet foundation stable : `.kspwallet` V1, VIEW/OWNER indépendants, Argon2id/XChaCha20-Poly1305, autorité Ed25519 OWNER, persistence no-clobber, signature, administration/rotations/révocation VIEW forte, import/export Solana CLI JSON + Base58, canaris adversariaux, interop externe et documentation durable publiés. La clôture `pre.010-fix.001`–`fix.003` ajoute `ed25519-dalek 3.0.0` direct, normalise le Rust workspace et installe l’audit structurel Python complémentaire à rustfmt/Clippy. `Pubkey` reste via `ksp-core-lib`, la keypair reste encapsulée dans Wallet et Config/Transport/ExecutionPolicy/Store/Tauri restent hors Wallet.
|
||||
- [/] `0.2.6` — `ksp-app-wallet-desk` + évolution `.kspwallet` V2 : surface Desk acquise ; `pre.014` polish et `pre.015` wire binaire V2 acquis ; `pre.016` matérialise APIs génériques/versionnées + runtime V2 ; `pre.017` matérialise migration explicite V1 -> V2 et canaris ; prochain gate `pre.018` candidate finale. Plan détaillé : `docs/plans/013-V0_2_6_WALLET_DESK_PLAN.md`.
|
||||
- [/] `0.2.6` — `ksp-app-wallet-desk` + évolution `.kspwallet` V2 : surface Desk, wire V2, APIs multi-version et migration explicite acquis ; `pre.018` matérialise la candidate documentaire et le runtime Tauri packagé user-writable. Gate opérateur/build final puis `rel.001` restent à fermer. Plan détaillé : `docs/plans/013-V0_2_6_WALLET_DESK_PLAN.md`.
|
||||
- [ ] `0.2.7` — Étendre `ksp-onchain-transport-lib` au WebSocket Solana standard complet ; permettre plusieurs sessions sur une même URL sans imposer encore un pool automatique complexe.
|
||||
- [ ] `0.2.8` — Ajouter Helius LaserStream WebSocket comme extension du moteur WebSocket standard, sans duplication de client.
|
||||
- [ ] `0.2.9` — Ajouter une première fondation Yellowstone gRPC standard/provider-neutral ; dimensionner la surface exacte à `pre.001` selon la documentation normative actuelle.
|
||||
|
||||
@@ -139,6 +139,12 @@ Si le document Logging, sa résolution environnementale ou l'initialisation du r
|
||||
|
||||
Le `LoggingGuard` est conservé dans `AppState` et sert désormais au hot reload transactionnel du runtime Logging après sauvegarde.
|
||||
|
||||
## Runtime packagé
|
||||
|
||||
Le parcours release ne dépend pas du checkout source ni du répertoire depuis lequel le binaire est lancé. Les documents Config et schemas enregistrés sont embarqués comme resources Tauri. Avant `AppState::initialize`, `tauri.rs` résout le répertoire de resources puis délègue à `ksp-config-lib::prepare_packaged_runtime`. Config résout un répertoire de données utilisateur KSP writable via `ProjectDirs`, y seed les documents Config absents et y synchronise les schemas possédés par le package courant. Le process adopte ensuite ce répertoire writable comme current working directory.
|
||||
|
||||
Les documents Config déjà présents restent user-managed et ne sont jamais écrasés silencieusement lors d'une mise à jour. Les schemas sont resynchronisés à chaque lancement packagé. Le `.env` n'est jamais embarqué ni seedé ; les chemins relatifs `logs/` et `wallets/` restent ancrés dans ce runtime partagé sauf override Config. Config Desk et Wallet Desk utilisent le même identifiant de projet KSP afin de partager cette racine runtime sans dupliquer l'ownership des ressources.
|
||||
|
||||
## Lifecycle des fenêtres
|
||||
|
||||
`tw_splash.rs` valide l'origine de `splash_frontend_ready`, ignore les readiness dupliquées et orchestre le passage à `main`. Les timings sont résolus par `ksp-config-lib` depuis :
|
||||
|
||||
@@ -101,6 +101,12 @@ Les bindings sont générés au premier DTO Tauri réel ; aucune structure facti
|
||||
|
||||
Les arguments `--cfgpath`, `--schemapath` et `--filemap=...` sont transmis tels quels à `ksp-config-lib`. En build debug, le launcher replace le current working directory Rust à la racine du workspace avant ce bootstrap afin que les defaults relatifs `config/`, `config/schemas/` et `.env` désignent les ressources racine même lorsque Tauri lance `cargo run` depuis la crate de l'application. Le launcher ne lit pas ces ressources lui-même. Le profil Logging initial est le `default_profile` de `std.logging.json`.
|
||||
|
||||
### Runtime packagé
|
||||
|
||||
En release, le CWD de développement n'est pas réutilisé. Les resources Tauri embarquent les documents Config et schemas enregistrés ; `ksp-config-lib::prepare_packaged_runtime` prépare une racine de données utilisateur KSP writable résolue par `ProjectDirs`, seed les Config uniquement lorsqu'elles sont absentes et synchronise les schemas depuis le package courant. `tauri.rs` active ensuite cette racine comme current working directory avant le bootstrap Config.
|
||||
|
||||
Le `.env` n'est pas inclus dans le bundle. Les Config existantes restent intactes lors d'une mise à jour et les chemins relatifs de logs/wallets sont résolus depuis la racine runtime writable, sauf configuration explicite contraire. Le chemin physique dépend de la plateforme et ne doit pas être codé en dur par l'application ou l'utilisateur.
|
||||
|
||||
Si cette configuration ne peut pas être utilisée, l'application doit rester démarrable pour permettre sa réparation : elle utilise alors un fallback Logging console/stderr en mémoire. Le fallback n'écrit aucun fichier de configuration et n'écrase aucune valeur utilisateur.
|
||||
|
||||
La commande Tauri `get_app_snapshot` expose un état sûr du bootstrap. Ses types TypeScript sont générés par `cargo test -p ksp-app-config-desk` sous `frontend/ts/bindings/`.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "ksp-app-config-desk",
|
||||
"private": true,
|
||||
"version": "0.2.6-pre.14.fix.1",
|
||||
"version": "0.2.6-pre.18",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
// file: crates/ksp-app-config-desk/src/tauri.rs
|
||||
// version: 16
|
||||
// version: 17
|
||||
|
||||
//! Tauri runtime assembly for the KSP configuration desktop application.
|
||||
|
||||
/// Runs the configuration desktop application.
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run(arguments: &[std::ffi::OsString]) -> ksp_core_lib::Result<()> {
|
||||
let context = tauri::generate_context!();
|
||||
let runtime_layout = configure_packaged_runtime(&context);
|
||||
if let std::result::Result::Err(error) = runtime_layout {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let app_state = crate::AppState::initialize(arguments);
|
||||
let app_state = match app_state {
|
||||
std::result::Result::Ok(value) => value,
|
||||
@@ -16,7 +21,7 @@ pub fn run(arguments: &[std::ffi::OsString]) -> ksp_core_lib::Result<()> {
|
||||
builder = configure_plugins(builder);
|
||||
builder = configure_commands(builder);
|
||||
builder = configure_setup(builder);
|
||||
let run_result = builder.run(tauri::generate_context!());
|
||||
let run_result = builder.run(context);
|
||||
return match run_result {
|
||||
std::result::Result::Ok(()) => std::result::Result::Ok(()),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(
|
||||
@@ -26,6 +31,36 @@ pub fn run(arguments: &[std::ffi::OsString]) -> ksp_core_lib::Result<()> {
|
||||
};
|
||||
}
|
||||
|
||||
fn configure_packaged_runtime(context: &tauri::Context<tauri::Wry>) -> ksp_core_lib::Result<()> {
|
||||
if cfg!(debug_assertions) {
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
let resource_root = tauri::utils::platform::resource_dir(context.package_info(), &tauri::Env::default());
|
||||
let resource_root = match resource_root {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_TAURI_RUNTIME_FAILED, "Cannot resolve packaged Tauri resource directory")
|
||||
.with_context("tauri_error", error.to_string()),
|
||||
);
|
||||
},
|
||||
};
|
||||
let layout = ksp_config_lib::prepare_packaged_runtime(resource_root.as_path());
|
||||
let layout = match layout {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let working_directory = std::env::set_current_dir(layout.runtime_root());
|
||||
return match working_directory {
|
||||
std::result::Result::Ok(()) => std::result::Result::Ok(()),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_TAURI_RUNTIME_FAILED, "Cannot activate packaged KSP runtime directory")
|
||||
.with_context("runtime_root", layout.runtime_root().to_string_lossy().into_owned())
|
||||
.with_source(error),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
fn configure_state(builder: tauri::Builder<tauri::Wry>, app_state: crate::AppState) -> tauri::Builder<tauri::Wry> {
|
||||
return builder.manage(app_state);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "KSP Config Desk",
|
||||
"version": "0.2.6-pre.14.fix.1",
|
||||
"version": "0.2.6-pre.18",
|
||||
"identifier": "com.sasedev.ksp-app-config-desk",
|
||||
"build": {
|
||||
"beforeDevCommand": {
|
||||
@@ -51,6 +51,16 @@
|
||||
"icon": [
|
||||
"icons/favicon.png",
|
||||
"icons/favicon.ico"
|
||||
]
|
||||
],
|
||||
"resources": {
|
||||
"../../config/composite.ksp-app-wallet-desk.json": "config/composite.ksp-app-wallet-desk.json",
|
||||
"../../config/std.logging.json": "config/std.logging.json",
|
||||
"../../config/std.transport.json": "config/std.transport.json",
|
||||
"../../config/std.wallet.json": "config/std.wallet.json",
|
||||
"../../config/schemas/composite.schema.json": "config/schemas/composite.schema.json",
|
||||
"../../config/schemas/std.logging.schema.json": "config/schemas/std.logging.schema.json",
|
||||
"../../config/schemas/std.transport.schema.json": "config/schemas/std.transport.schema.json",
|
||||
"../../config/schemas/std.wallet.schema.json": "config/schemas/std.wallet.schema.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-config-desk/tests/desktop_contract.rs
|
||||
// version: 4
|
||||
// version: 5
|
||||
|
||||
//! Desktop build/shell contract audits for Config Desk.
|
||||
|
||||
@@ -99,3 +99,24 @@ fn pre_014_template_uses_sidebar_navigation_and_kbot_style_splash_contract() {
|
||||
assert!(splash_ts.contains("add_debug"));
|
||||
assert!(splash_ts.contains("add_message"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_018_packaged_runtime_bundles_config_resources_and_activates_shared_writable_root() {
|
||||
let root = app_root();
|
||||
let tauri = read_json(root.join("tauri.conf.json").as_path());
|
||||
assert_eq!(tauri.pointer("/version").and_then(serde_json::Value::as_str), std::option::Option::Some("0.2.6-pre.18"));
|
||||
let resources = tauri.pointer("/bundle/resources").and_then(serde_json::Value::as_object);
|
||||
assert!(resources.is_some(), "packaged Config resources map must exist");
|
||||
if let std::option::Option::Some(resources) = resources {
|
||||
assert_eq!(resources.len(), 8);
|
||||
assert_eq!(resources.get("../../config/std.logging.json").and_then(serde_json::Value::as_str), std::option::Option::Some("config/std.logging.json"));
|
||||
assert_eq!(
|
||||
resources.get("../../config/schemas/std.wallet.schema.json").and_then(serde_json::Value::as_str),
|
||||
std::option::Option::Some("config/schemas/std.wallet.schema.json"),
|
||||
);
|
||||
}
|
||||
let tauri_source = read_text(root.join("src/tauri.rs").as_path());
|
||||
assert!(tauri_source.contains("ksp_config_lib::prepare_packaged_runtime"));
|
||||
assert!(tauri_source.contains("tauri::utils::platform::resource_dir"));
|
||||
assert!(tauri_source.contains("std::env::set_current_dir(layout.runtime_root())"));
|
||||
}
|
||||
|
||||
136
crates/ksp-app-wallet-desk/README.md
Normal file
136
crates/ksp-app-wallet-desk/README.md
Normal file
@@ -0,0 +1,136 @@
|
||||
<!-- file: crates/ksp-app-wallet-desk/README.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# `ksp-app-wallet-desk`
|
||||
|
||||
`ksp-app-wallet-desk` est l'application desktop spécialisée d'administration et de validation des wallets KSP.
|
||||
|
||||
La crate est un package Tauri mixte :
|
||||
|
||||
```text
|
||||
package : ksp-app-wallet-desk
|
||||
lib : ksp_app_wallet_desk_lib
|
||||
bin : ksp-app-wallet-desk
|
||||
```
|
||||
|
||||
## Responsabilités
|
||||
|
||||
Wallet Desk reste une couche d'interface et de composition. Il ne possède ni le wire `.kspwallet`, ni la cryptographie Wallet, ni la résolution Config, ni le transport Solana :
|
||||
|
||||
```text
|
||||
ksp-config-lib -> configuration/composite/.env
|
||||
ksp-wallet-lib -> format, crypto, VIEW/OWNER, import/export, migration
|
||||
ksp-onchain-transport-lib -> RPC Solana HTTP
|
||||
ksp-logging-lib -> logging/tracing applicatif
|
||||
ksp-app-wallet-desk -> orchestration Tauri + projections sûres
|
||||
```
|
||||
|
||||
Le frontend ne reçoit jamais les keypairs, ciphertexts, passwords Config, paths complets import/export ou handles VIEW/OWNER. Les opérations privilégiées sont exécutées côté Rust.
|
||||
|
||||
## `.kspwallet` V1/V2
|
||||
|
||||
La release `0.2.6` conserve V1 et ajoute V2 :
|
||||
|
||||
```text
|
||||
V1 : JSON UTF-8 historique, lecture explicite toujours supportée
|
||||
V2 : wire binaire KSP, format de création/import par défaut
|
||||
```
|
||||
|
||||
Wallet Desk consomme uniquement les APIs non versionnées de `ksp-wallet-lib` :
|
||||
|
||||
- création/import -> format par défaut `V2` ;
|
||||
- inventory/inspection/open -> détection V1/V2 ;
|
||||
- mutations OWNER/VIEW -> restent dans le format natif ouvert ;
|
||||
- ouverture d'un V1 -> aucune migration implicite.
|
||||
|
||||
`ksp-wallet-lib` conserve parallèlement les APIs explicites `_v1` / `_v2` pour les consumers qui doivent imposer un format. `DEFAULT_WALLET_FORMAT` et `LATEST_SUPPORTED_WALLET_FORMAT` sont des politiques distinctes ; l'apparition future d'un V3 n'impose donc pas de modifier automatiquement le format créé par les APIs génériques.
|
||||
|
||||
La migration V1 -> V2 est une opération explicite OWNER-authentifiée possédée par `ksp-wallet-lib`. Wallet Desk `0.2.6` ne migre jamais silencieusement un wallet lors de sa sélection, inspection ou ouverture.
|
||||
|
||||
## Capacités fonctionnelles `0.2.6`
|
||||
|
||||
La surface validée comprend :
|
||||
|
||||
- inventory root-scoped des `.kspwallet` ;
|
||||
- create/import Solana CLI JSON et Base58 ;
|
||||
- sélection et inspection locked ;
|
||||
- ouverture VIEW et OWNER, manuelle ou via candidats secrets Config ;
|
||||
- affichage Pubkey/alias/notes uniquement après autorisation ;
|
||||
- `getBalance` via le Transport HTTP KSP ;
|
||||
- mutation alias/notes OWNER ;
|
||||
- rotations OWNER et VIEW ;
|
||||
- self-rotation VIEW ;
|
||||
- disable/recreate VIEW fort par OWNER ;
|
||||
- export OWNER Solana CLI JSON/Base58 no-clobber ;
|
||||
- wire V2 par défaut tout en conservant la compatibilité V1.
|
||||
|
||||
## Frontières desktop
|
||||
|
||||
Le frontend utilise Bootstrap, Font Awesome, DataTables/Select, SimpleBar et le bridge Logging KSP. Les capabilities Tauri du guest restent minimales :
|
||||
|
||||
```text
|
||||
core:default
|
||||
tracing:default
|
||||
```
|
||||
|
||||
Les pickers import/export sont invoqués côté Rust. Aucun accès direct filesystem ou réseau Solana n'est accordé au frontend.
|
||||
|
||||
## Développement
|
||||
|
||||
Le workspace contient plusieurs applications Tauri. `-c/--config` n'est pas un sélecteur de crate ; le cycle doit être lancé depuis le répertoire applicatif :
|
||||
|
||||
```bash
|
||||
(cd crates/ksp-app-wallet-desk && cargo tauri dev)
|
||||
```
|
||||
|
||||
En développement, le launcher Rust normalise ensuite son current working directory vers la racine du workspace avant le bootstrap Config. Les chemins relatifs `config/`, `config/schemas/`, `.env`, `logs/` et `wallets/` restent ainsi cohérents avec le checkout de développement sans déplacer leur ownership hors de `ksp-config-lib`.
|
||||
|
||||
Les ports réservés sont :
|
||||
|
||||
```text
|
||||
Vite HTTP : 1432
|
||||
Vite WS : 1433
|
||||
```
|
||||
|
||||
## Runtime packagé
|
||||
|
||||
Une application distribuée ne dépend pas du checkout source ni du répertoire depuis lequel l'utilisateur lance le binaire.
|
||||
|
||||
Les documents Config et schemas enregistrés sont embarqués comme resources Tauri. Au démarrage release :
|
||||
|
||||
1. Tauri résout son répertoire de resources ;
|
||||
2. `ksp-config-lib` résout un répertoire de données utilisateur KSP writable via `ProjectDirs` ;
|
||||
3. les documents Config packagés sont copiés uniquement lorsqu'ils sont absents ;
|
||||
4. les schemas, possédés par le package courant, sont resynchronisés à chaque lancement ;
|
||||
5. le process adopte ce répertoire KSP writable comme current working directory avant `AppState::initialize`.
|
||||
|
||||
Le `.env` n'est jamais embarqué ni prérempli. Les Config déjà modifiées par l'utilisateur ne sont jamais écrasées silencieusement. Les chemins relatifs de logs et wallets restent ancrés dans ce runtime writable, sauf override explicite par Config.
|
||||
|
||||
## Validation
|
||||
|
||||
Les validations Rust courantes sont :
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
Le parcours fonctionnel utilise exclusivement Tauri :
|
||||
|
||||
```bash
|
||||
(cd crates/ksp-app-wallet-desk && cargo tauri dev)
|
||||
```
|
||||
|
||||
Le build production de la release est exécuté seulement après tous les autres gates :
|
||||
|
||||
```bash
|
||||
(cd crates/ksp-app-wallet-desk && cargo tauri build)
|
||||
```
|
||||
|
||||
Cette commande doit rester l'absolue dernière opération de validation de la candidate `0.2.6`.
|
||||
|
||||
Voir également [`USAGE.md`](USAGE.md), [`../../docs/formats/KSPWALLET_V1.md`](../../docs/formats/KSPWALLET_V1.md), [`../../docs/formats/KSPWALLET_V2.md`](../../docs/formats/KSPWALLET_V2.md) et [`../../docs/validation/009-V0_2_6_WALLET_DESK_COMPLIANCE.md`](../../docs/validation/009-V0_2_6_WALLET_DESK_COMPLIANCE.md).
|
||||
140
crates/ksp-app-wallet-desk/USAGE.md
Normal file
140
crates/ksp-app-wallet-desk/USAGE.md
Normal file
@@ -0,0 +1,140 @@
|
||||
<!-- file: crates/ksp-app-wallet-desk/USAGE.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Utilisation de `ksp-app-wallet-desk`
|
||||
|
||||
## 1. Lancement de développement
|
||||
|
||||
Depuis la racine du workspace :
|
||||
|
||||
```bash
|
||||
(cd crates/ksp-app-wallet-desk && cargo tauri dev)
|
||||
```
|
||||
|
||||
Pour isoler les wallets d'un parcours manuel :
|
||||
|
||||
```bash
|
||||
KSP_WALLETS_DIRECTORY=var/wallet-desk-manual \
|
||||
bash -lc 'cd crates/ksp-app-wallet-desk && cargo tauri dev'
|
||||
```
|
||||
|
||||
Le processus Rust debug recale son current working directory sur la racine du workspace avant le bootstrap Config. `KSP_WALLETS_DIRECTORY`, les autres variables KSP et le `.env` restent résolus exclusivement par `ksp-config-lib`.
|
||||
|
||||
## 2. Format natif
|
||||
|
||||
Wallet Desk ne sélectionne pas directement une version de wire : il utilise la façade générique de `ksp-wallet-lib`.
|
||||
|
||||
```text
|
||||
nouveau wallet -> V2
|
||||
import Solana CLI/Base58 -> V2
|
||||
inspection/ouverture -> auto-détection V1/V2
|
||||
mutation d'un V1 ouvert -> V1
|
||||
mutation d'un V2 ouvert -> V2
|
||||
```
|
||||
|
||||
La présence de V2 ne réécrit pas automatiquement les fichiers V1 existants.
|
||||
|
||||
## 3. Création / import
|
||||
|
||||
Dans **Create / Import** :
|
||||
|
||||
- la création génère une nouvelle identité Solana et un `.kspwallet` V2 ;
|
||||
- l'import accepte les formats de transfert Solana retenus, puis crée un `.kspwallet` V2 ;
|
||||
- OWNER est toujours requis ; VIEW est optionnel ;
|
||||
- les passwords sont request-only et ne sont jamais renvoyés par IPC.
|
||||
|
||||
L'import natif utilise un picker Rust. Le path source complet et les bytes de keypair ne traversent pas le frontend.
|
||||
|
||||
## 4. Inventory et ouverture
|
||||
|
||||
L'inventory n'affiche que des projections locked sûres. La sélection ne déverrouille jamais automatiquement un secret Config.
|
||||
|
||||
L'ouverture peut ensuite utiliser :
|
||||
|
||||
- un password saisi manuellement ;
|
||||
- un candidat secret Config explicitement sélectionné côté backend.
|
||||
|
||||
VIEW et OWNER produisent des handles Rust-only. La Pubkey, l'alias et les notes ne sont projetés qu'après autorisation.
|
||||
|
||||
## 5. Administration
|
||||
|
||||
OWNER permet notamment :
|
||||
|
||||
- modifier l'alias ;
|
||||
- ajouter/modifier/supprimer des notes ;
|
||||
- changer le password OWNER ;
|
||||
- changer, désactiver ou recréer VIEW ;
|
||||
- exporter la keypair en Solana CLI JSON ou Base58.
|
||||
|
||||
VIEW peut effectuer sa self-rotation lorsqu'il est actif. Les opérations destructives/privilégiées utilisent les modals Bootstrap du shell et non `window.confirm`/`alert`.
|
||||
|
||||
## 6. Migration V1 -> V2
|
||||
|
||||
La migration existe dans `ksp-wallet-lib`, mais n'est jamais un effet secondaire d'une ouverture Wallet Desk.
|
||||
|
||||
Les APIs bibliothèque dédiées sont :
|
||||
|
||||
```text
|
||||
migrate_wallet_v1_to_v2
|
||||
migrate_wallet_file_v1_to_v2
|
||||
migrate_wallet_file_v1_to_v2_in_place
|
||||
```
|
||||
|
||||
Elles authentifient OWNER, reconstruisent un document cryptographique V2 neuf et conservent l'identité Solana ainsi que les metadata/note IDs. Lorsque VIEW est activé, un password VIEW cible doit être fourni. La copie est no-clobber ; le remplacement in-place est stale-protected.
|
||||
|
||||
## 7. Balance Solana
|
||||
|
||||
Une session VIEW ou OWNER autorisée peut demander `getBalance` via `ksp-onchain-transport-lib`. Le frontend ne choisit ni Pubkey arbitraire ni URL RPC : ces valeurs sont dérivées de l'état Rust autorisé et de Config.
|
||||
|
||||
## 8. Runtime packagé
|
||||
|
||||
En build release, les Config/schemas enregistrés sont des resources du bundle Tauri. Avant le bootstrap applicatif, `ksp-config-lib` prépare un répertoire de données utilisateur commun à KSP :
|
||||
|
||||
```text
|
||||
<runtime KSP writable>/
|
||||
├── config/
|
||||
│ ├── composite.ksp-app-wallet-desk.json
|
||||
│ ├── std.logging.json
|
||||
│ ├── std.transport.json
|
||||
│ ├── std.wallet.json
|
||||
│ └── schemas/
|
||||
├── .env # créé/modifié uniquement par Config si nécessaire
|
||||
├── logs/ # par défaut si path relatif
|
||||
└── wallets/ # par défaut si path relatif
|
||||
```
|
||||
|
||||
Le chemin physique exact dépend de la plateforme et est résolu par `ProjectDirs`; l'application ne suppose aucune home directory particulière.
|
||||
|
||||
Les Config existantes sont conservées lors d'une mise à jour. Les schemas sont synchronisés depuis le package courant. Le `.env` et les secrets ne sont jamais embarqués dans le bundle.
|
||||
|
||||
## 9. Validation candidate `0.2.6`
|
||||
|
||||
Avant le build :
|
||||
|
||||
```bash
|
||||
cargo fmt --all
|
||||
python3 scripts/audit_rust_workspace_rules.py
|
||||
cargo check --workspace
|
||||
cargo clippy --workspace --all-targets
|
||||
cargo test -p ksp-config-lib
|
||||
cargo test -p ksp-wallet-lib
|
||||
cargo test -p ksp-app-config-desk
|
||||
cargo test -p ksp-app-wallet-desk
|
||||
cargo test --workspace
|
||||
```
|
||||
|
||||
Puis parcours fonctionnel :
|
||||
|
||||
```bash
|
||||
(cd crates/ksp-app-wallet-desk && cargo tauri dev)
|
||||
```
|
||||
|
||||
Le smoke Devnet opt-in, s'il est rejoué, doit l'être avant le build final.
|
||||
|
||||
L'absolue dernière opération est :
|
||||
|
||||
```bash
|
||||
(cd crates/ksp-app-wallet-desk && cargo tauri build)
|
||||
```
|
||||
|
||||
Aucune commande de validation ne doit être exécutée après ce build pour conserver la preuve d'ordre de la candidate.
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: crates/ksp-app-wallet-desk/frontend/main.html -->
|
||||
<!-- version: 2 -->
|
||||
<!-- version: 3 -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
@@ -53,7 +53,7 @@
|
||||
<h1 id="viewTitle" class="h3 mb-1">Dashboard</h1>
|
||||
<p class="text-body-secondary mb-0">Identité autorisée et balance SOL via le Transport HTTP composite sont actives.</p>
|
||||
</div>
|
||||
<span class="badge text-bg-secondary">0.2.6-pre.14.fix.1</span>
|
||||
<span class="badge text-bg-secondary">0.2.6-pre.18</span>
|
||||
</div>
|
||||
<section data-view-panel="dashboard">
|
||||
<div class="row g-3">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "ksp-app-wallet-desk",
|
||||
"private": true,
|
||||
"version": "0.2.6-pre.14.fix.1",
|
||||
"version": "0.2.6-pre.18",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-wallet-desk/src/tauri.rs
|
||||
// version: 10
|
||||
// version: 11
|
||||
|
||||
//! Tauri runtime assembly for the KSP wallet desktop application.
|
||||
|
||||
@@ -8,6 +8,11 @@ use tauri_plugin_dialog::DialogExt; // rust-rules: trait-import
|
||||
/// Runs the Wallet Desk application.
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run(arguments: &[std::ffi::OsString]) -> ksp_core_lib::Result<()> {
|
||||
let context = tauri::generate_context!();
|
||||
let runtime_layout = configure_packaged_runtime(&context);
|
||||
if let std::result::Result::Err(error) = runtime_layout {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let app_state = crate::AppState::initialize(arguments);
|
||||
let app_state = match app_state {
|
||||
std::result::Result::Ok(value) => value,
|
||||
@@ -18,7 +23,7 @@ pub fn run(arguments: &[std::ffi::OsString]) -> ksp_core_lib::Result<()> {
|
||||
builder = configure_plugins(builder);
|
||||
builder = configure_commands(builder);
|
||||
builder = configure_setup(builder);
|
||||
let run_result = builder.run(tauri::generate_context!());
|
||||
let run_result = builder.run(context);
|
||||
return match run_result {
|
||||
std::result::Result::Ok(()) => std::result::Result::Ok(()),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(
|
||||
@@ -28,6 +33,36 @@ pub fn run(arguments: &[std::ffi::OsString]) -> ksp_core_lib::Result<()> {
|
||||
};
|
||||
}
|
||||
|
||||
fn configure_packaged_runtime(context: &tauri::Context<tauri::Wry>) -> ksp_core_lib::Result<()> {
|
||||
if cfg!(debug_assertions) {
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
let resource_root = tauri::utils::platform::resource_dir(context.package_info(), &tauri::Env::default());
|
||||
let resource_root = match resource_root {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_TAURI_RUNTIME_FAILED, "Cannot resolve packaged Tauri resource directory")
|
||||
.with_context("tauri_error", error.to_string()),
|
||||
);
|
||||
},
|
||||
};
|
||||
let layout = ksp_config_lib::prepare_packaged_runtime(resource_root.as_path());
|
||||
let layout = match layout {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let working_directory = std::env::set_current_dir(layout.runtime_root());
|
||||
return match working_directory {
|
||||
std::result::Result::Ok(()) => std::result::Result::Ok(()),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_TAURI_RUNTIME_FAILED, "Cannot activate packaged KSP runtime directory")
|
||||
.with_context("runtime_root", layout.runtime_root().to_string_lossy().into_owned())
|
||||
.with_source(error),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
fn configure_state(builder: tauri::Builder<tauri::Wry>, app_state: crate::AppState) -> tauri::Builder<tauri::Wry> {
|
||||
return builder.manage(app_state);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "KSP Wallet Desk",
|
||||
"version": "0.2.6-pre.14.fix.1",
|
||||
"version": "0.2.6-pre.18",
|
||||
"identifier": "com.sasedev.ksp-app-wallet-desk",
|
||||
"build": {
|
||||
"beforeDevCommand": {
|
||||
@@ -51,6 +51,16 @@
|
||||
"icon": [
|
||||
"icons/favicon.png",
|
||||
"icons/favicon.ico"
|
||||
]
|
||||
],
|
||||
"resources": {
|
||||
"../../config/composite.ksp-app-wallet-desk.json": "config/composite.ksp-app-wallet-desk.json",
|
||||
"../../config/std.logging.json": "config/std.logging.json",
|
||||
"../../config/std.transport.json": "config/std.transport.json",
|
||||
"../../config/std.wallet.json": "config/std.wallet.json",
|
||||
"../../config/schemas/composite.schema.json": "config/schemas/composite.schema.json",
|
||||
"../../config/schemas/std.logging.schema.json": "config/schemas/std.logging.schema.json",
|
||||
"../../config/schemas/std.transport.schema.json": "config/schemas/std.transport.schema.json",
|
||||
"../../config/schemas/std.wallet.schema.json": "config/schemas/std.wallet.schema.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-wallet-desk/tests/desktop_contract.rs
|
||||
// version: 19
|
||||
// version: 20
|
||||
|
||||
//! Desktop build, shell and Config-status contract audits for Wallet Desk.
|
||||
|
||||
@@ -417,3 +417,31 @@ fn pre_017_wallet_desk_open_paths_remain_non_migrating() {
|
||||
assert!(state.contains("ksp_wallet_lib::open_wallet_owner_file"));
|
||||
assert!(inventory.contains("ksp_wallet_lib::inspect_locked_wallet_file"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_018_packaged_runtime_bundles_config_resources_and_keeps_wallet_desk_version_current() {
|
||||
let root = app_root();
|
||||
let tauri = read_json(root.join("tauri.conf.json").as_path());
|
||||
assert_eq!(tauri.pointer("/version").and_then(serde_json::Value::as_str), std::option::Option::Some("0.2.6-pre.18"));
|
||||
let package = read_json(root.join("package.json").as_path());
|
||||
assert_eq!(package.pointer("/version").and_then(serde_json::Value::as_str), std::option::Option::Some("0.2.6-pre.18"));
|
||||
let resources = tauri.pointer("/bundle/resources").and_then(serde_json::Value::as_object);
|
||||
assert!(resources.is_some(), "packaged Wallet Desk Config resources map must exist");
|
||||
if let std::option::Option::Some(resources) = resources {
|
||||
assert_eq!(resources.len(), 8);
|
||||
assert_eq!(
|
||||
resources.get("../../config/composite.ksp-app-wallet-desk.json").and_then(serde_json::Value::as_str),
|
||||
std::option::Option::Some("config/composite.ksp-app-wallet-desk.json"),
|
||||
);
|
||||
assert_eq!(
|
||||
resources.get("../../config/schemas/composite.schema.json").and_then(serde_json::Value::as_str),
|
||||
std::option::Option::Some("config/schemas/composite.schema.json"),
|
||||
);
|
||||
}
|
||||
let main = read_text(root.join("frontend/main.html").as_path());
|
||||
assert!(main.contains("0.2.6-pre.18"));
|
||||
let tauri_source = read_text(root.join("src/tauri.rs").as_path());
|
||||
assert!(tauri_source.contains("ksp_config_lib::prepare_packaged_runtime"));
|
||||
assert!(tauri_source.contains("tauri::utils::platform::resource_dir"));
|
||||
assert!(tauri_source.contains("std::env::set_current_dir(layout.runtime_root())"));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-wallet-desk/tests/release_compliance.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
//! Release-wide deterministic compliance canaries for Wallet Desk.
|
||||
|
||||
@@ -181,3 +181,36 @@ fn wallet_desk_dependency_firewall_and_tauri_capabilities_are_minimal() {
|
||||
assert!(!capability_source.contains("dialog:"));
|
||||
assert!(!capability_source.contains("fs:"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn packaged_resources_include_only_registered_config_sources_and_schemas() {
|
||||
let root = app_root();
|
||||
let tauri_source = read_text(root.join("tauri.conf.json").as_path());
|
||||
let tauri = serde_json::from_str::<serde_json::Value>(tauri_source.as_str());
|
||||
assert!(tauri.is_ok(), "Wallet Desk Tauri config should parse");
|
||||
let tauri = match tauri {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let resources = tauri.pointer("/bundle/resources").and_then(serde_json::Value::as_object);
|
||||
assert!(resources.is_some(), "Wallet Desk package must declare Config resources");
|
||||
let resources = match resources {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
assert_eq!(resources.len(), 8);
|
||||
for (source, destination) in resources {
|
||||
let destination = destination.as_str();
|
||||
assert!(destination.is_some(), "resource destination must be textual");
|
||||
let destination = match destination {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => continue,
|
||||
};
|
||||
assert!(source.starts_with("../../config/"));
|
||||
assert!(destination.starts_with("config/"));
|
||||
for forbidden in [".env", "wallets", "logs", "secret", "private"] {
|
||||
assert!(!source.contains(forbidden), "mutable/secret runtime resource must not be bundled: {source}");
|
||||
assert!(!destination.contains(forbidden), "mutable/secret runtime destination must not be bundled: {destination}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# file: crates/ksp-config-lib/Cargo.toml
|
||||
# version: 6
|
||||
# version: 7
|
||||
|
||||
[package]
|
||||
name = "ksp-config-lib"
|
||||
@@ -8,6 +8,7 @@ edition.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
directories.workspace = true
|
||||
ksp-core-lib = { path = "../ksp-core-lib" }
|
||||
ksp-logging-lib = { path = "../ksp-logging-lib" }
|
||||
ksp-onchain-transport-lib = { path = "../ksp-onchain-transport-lib" }
|
||||
@@ -16,6 +17,7 @@ serde_json.workspace = true
|
||||
jsonschema.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile.workspace = true
|
||||
tokio = { workspace = true, features = ["macros", "rt"] }
|
||||
|
||||
[lints]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-config-lib/src/error.rs
|
||||
// version: 9
|
||||
// version: 10
|
||||
|
||||
/// Error code used when a Config bootstrap argument is missing its value.
|
||||
pub const ERROR_CODE_BOOTSTRAP_ARGUMENT_MISSING_VALUE: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "bootstrap_argument_missing_value");
|
||||
@@ -37,6 +37,9 @@ pub const ERROR_CODE_JSON_FILE_READ_FAILED: ksp_core_lib::ErrorCode = ksp_core_l
|
||||
pub const ERROR_CODE_JSON_SYNTAX_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "json_syntax_invalid");
|
||||
/// Error code used when an explicit Config management operation is unsupported or targets the wrong managed resource kind.
|
||||
pub const ERROR_CODE_MANAGEMENT_OPERATION_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "management_operation_invalid");
|
||||
/// Error code used when packaged desktop resources cannot initialize the shared writable KSP runtime layout.
|
||||
pub const ERROR_CODE_PACKAGED_RUNTIME_PREPARATION_FAILED: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("config", "packaged_runtime_preparation_failed");
|
||||
/// Error code used when an atomic managed Config or `.env` persistence operation fails before commit.
|
||||
pub const ERROR_CODE_PERSISTENCE_WRITE_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "persistence_write_failed");
|
||||
/// Error code used when an explicitly requested Config profile does not exist in a validated document.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-config-lib/src/lib.rs
|
||||
// version: 15
|
||||
// version: 16
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -20,6 +20,7 @@ mod environment;
|
||||
mod error;
|
||||
mod logging;
|
||||
mod management;
|
||||
mod packaging;
|
||||
mod persistence;
|
||||
mod profile;
|
||||
mod registry;
|
||||
@@ -91,6 +92,8 @@ pub use self::error::ERROR_CODE_JSON_FILE_READ_FAILED;
|
||||
pub use self::error::ERROR_CODE_JSON_SYNTAX_INVALID;
|
||||
/// Error code used when an explicit management operation is unsupported or targets the wrong managed resource kind.
|
||||
pub use self::error::ERROR_CODE_MANAGEMENT_OPERATION_INVALID;
|
||||
/// Error code used when packaged desktop resources cannot initialize the shared writable KSP runtime layout.
|
||||
pub use self::error::ERROR_CODE_PACKAGED_RUNTIME_PREPARATION_FAILED;
|
||||
/// Error code used when atomic managed Config or `.env` persistence fails before commit.
|
||||
pub use self::error::ERROR_CODE_PERSISTENCE_WRITE_FAILED;
|
||||
/// Error code used when an explicitly requested Config profile does not exist.
|
||||
@@ -123,6 +126,10 @@ pub use self::management::LoggingOutputFilterConfig;
|
||||
pub use self::management::LoggingProfileConfig;
|
||||
/// Typed source contract for one global Logging target override.
|
||||
pub use self::management::LoggingTargetFilterConfig;
|
||||
/// Writable KSP runtime roots prepared from packaged Config resources.
|
||||
pub use self::packaging::PackagedRuntimeLayout;
|
||||
/// Prepares the shared writable KSP desktop runtime from immutable packaged resources.
|
||||
pub use self::packaging::prepare_packaged_runtime;
|
||||
/// Source that selected an effective standard Config profile.
|
||||
pub use self::profile::ConfigProfileSelectionSource;
|
||||
/// Origin of one top-level value in a resolved standard Config profile.
|
||||
|
||||
167
crates/ksp-config-lib/src/packaging.rs
Normal file
167
crates/ksp-config-lib/src/packaging.rs
Normal file
@@ -0,0 +1,167 @@
|
||||
// file: crates/ksp-config-lib/src/packaging.rs
|
||||
// version: 1
|
||||
|
||||
//! Packaged desktop runtime layout owned by Config.
|
||||
|
||||
const PACKAGED_PROJECT_APPLICATION: &str = "khadhroony-solana-project";
|
||||
const PACKAGED_PROJECT_ORGANIZATION: &str = "SASEDEV";
|
||||
const PACKAGED_PROJECT_QUALIFIER: &str = "com";
|
||||
|
||||
/// Writable KSP runtime roots prepared from packaged Config resources.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct PackagedRuntimeLayout {
|
||||
runtime_root: std::path::PathBuf,
|
||||
cfg_path: std::path::PathBuf,
|
||||
schema_path: std::path::PathBuf,
|
||||
}
|
||||
|
||||
impl PackagedRuntimeLayout {
|
||||
/// Returns the shared writable KSP runtime root used as the packaged process working directory.
|
||||
#[must_use]
|
||||
pub fn runtime_root(&self) -> &std::path::Path {
|
||||
return self.runtime_root.as_path();
|
||||
}
|
||||
|
||||
/// Returns the writable root containing Config-managed runtime documents.
|
||||
#[must_use]
|
||||
pub fn cfg_path(&self) -> &std::path::Path {
|
||||
return self.cfg_path.as_path();
|
||||
}
|
||||
|
||||
/// Returns the writable root containing the package-owned current JSON Schemas.
|
||||
#[must_use]
|
||||
pub fn schema_path(&self) -> &std::path::Path {
|
||||
return self.schema_path.as_path();
|
||||
}
|
||||
}
|
||||
|
||||
/// Prepares the shared writable KSP desktop runtime from immutable packaged resources.
|
||||
///
|
||||
/// Runtime Config documents are seeded only when absent so existing user-managed sources are never silently overwritten. JSON Schemas are package-owned
|
||||
/// authority and are synchronized on each packaged launch. The local `.env`, Logging outputs and Wallet roots remain ordinary relative runtime resources
|
||||
/// below the returned shared root unless their managed Config explicitly selects another path.
|
||||
pub fn prepare_packaged_runtime(resource_root: &std::path::Path) -> ksp_core_lib::Result<PackagedRuntimeLayout> {
|
||||
let project_dirs = directories::ProjectDirs::from(PACKAGED_PROJECT_QUALIFIER, PACKAGED_PROJECT_ORGANIZATION, PACKAGED_PROJECT_APPLICATION);
|
||||
let project_dirs = match project_dirs {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_PACKAGED_RUNTIME_PREPARATION_FAILED,
|
||||
"Cannot resolve the writable packaged KSP runtime directory",
|
||||
));
|
||||
},
|
||||
};
|
||||
return prepare_packaged_runtime_at(resource_root, project_dirs.data_dir());
|
||||
}
|
||||
|
||||
fn prepare_packaged_runtime_at(resource_root: &std::path::Path, runtime_root: &std::path::Path) -> ksp_core_lib::Result<PackagedRuntimeLayout> {
|
||||
let packaged_cfg_path = resource_root.join(crate::DEFAULT_CFG_PATH);
|
||||
let packaged_schema_path = resource_root.join(crate::DEFAULT_SCHEMA_PATH);
|
||||
let cfg_path = runtime_root.join(crate::DEFAULT_CFG_PATH);
|
||||
let schema_path = runtime_root.join(crate::DEFAULT_SCHEMA_PATH);
|
||||
let runtime_creation = std::fs::create_dir_all(runtime_root);
|
||||
if let std::result::Result::Err(error) = runtime_creation {
|
||||
return std::result::Result::Err(packaging_io_error(runtime_root, "packaged KSP runtime root cannot be created", error));
|
||||
}
|
||||
let cfg_creation = std::fs::create_dir_all(cfg_path.as_path());
|
||||
if let std::result::Result::Err(error) = cfg_creation {
|
||||
return std::result::Result::Err(packaging_io_error(cfg_path.as_path(), "packaged Config root cannot be created", error));
|
||||
}
|
||||
let schema_creation = std::fs::create_dir_all(schema_path.as_path());
|
||||
if let std::result::Result::Err(error) = schema_creation {
|
||||
return std::result::Result::Err(packaging_io_error(schema_path.as_path(), "packaged Config schema root cannot be created", error));
|
||||
}
|
||||
let registry = crate::ConfigFileRegistry::defaults();
|
||||
let registry = match registry {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
for descriptor in registry.descriptors() {
|
||||
let (source_root, destination_root) = match descriptor.kind() {
|
||||
crate::ConfigFileKind::Config => (packaged_cfg_path.as_path(), cfg_path.as_path()),
|
||||
crate::ConfigFileKind::Schema => (packaged_schema_path.as_path(), schema_path.as_path()),
|
||||
};
|
||||
let source = source_root.join(descriptor.filename());
|
||||
let destination = destination_root.join(descriptor.filename());
|
||||
let content = read_packaged_resource(source.as_path());
|
||||
let content = match content {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let persistence = match descriptor.kind() {
|
||||
crate::ConfigFileKind::Config => seed_config_document(destination.as_path(), content.as_slice()),
|
||||
crate::ConfigFileKind::Schema => synchronize_schema(destination.as_path(), content.as_slice()),
|
||||
};
|
||||
if let std::result::Result::Err(error) = persistence {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
}
|
||||
return std::result::Result::Ok(PackagedRuntimeLayout { runtime_root: runtime_root.to_path_buf(), cfg_path, schema_path });
|
||||
}
|
||||
|
||||
fn read_packaged_resource(path: &std::path::Path) -> ksp_core_lib::Result<std::vec::Vec<u8>> {
|
||||
let metadata = std::fs::symlink_metadata(path);
|
||||
let metadata = match metadata {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(packaging_io_error(path, "packaged Config resource metadata cannot be read", error));
|
||||
},
|
||||
};
|
||||
if metadata.file_type().is_symlink() || !metadata.is_file() {
|
||||
return std::result::Result::Err(packaging_error(path, "packaged Config resource must be a regular non-symlink file"));
|
||||
}
|
||||
let content = std::fs::read(path);
|
||||
return match content {
|
||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(packaging_io_error(path, "packaged Config resource cannot be read", error)),
|
||||
};
|
||||
}
|
||||
|
||||
fn seed_config_document(path: &std::path::Path, content: &[u8]) -> ksp_core_lib::Result<()> {
|
||||
let existing = destination_state(path);
|
||||
let existing = match existing {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
if existing {
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
return crate::atomic_write(path, content);
|
||||
}
|
||||
|
||||
fn synchronize_schema(path: &std::path::Path, content: &[u8]) -> ksp_core_lib::Result<()> {
|
||||
let existing = destination_state(path);
|
||||
if let std::result::Result::Err(error) = existing {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
return crate::atomic_write(path, content);
|
||||
}
|
||||
|
||||
fn destination_state(path: &std::path::Path) -> ksp_core_lib::Result<bool> {
|
||||
let metadata = std::fs::symlink_metadata(path);
|
||||
return match metadata {
|
||||
std::result::Result::Ok(value) => {
|
||||
if value.file_type().is_symlink() || !value.is_file() {
|
||||
std::result::Result::Err(packaging_error(path, "packaged runtime destination must be a regular non-symlink file when it exists"))
|
||||
} else {
|
||||
std::result::Result::Ok(true)
|
||||
}
|
||||
},
|
||||
std::result::Result::Err(error) if error.kind() == std::io::ErrorKind::NotFound => std::result::Result::Ok(false),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(packaging_io_error(path, "packaged runtime destination metadata cannot be read", error)),
|
||||
};
|
||||
}
|
||||
|
||||
fn packaging_error(path: &std::path::Path, reason: &'static str) -> ksp_core_lib::Error {
|
||||
return ksp_core_lib::Error::new(crate::ERROR_CODE_PACKAGED_RUNTIME_PREPARATION_FAILED, "Packaged KSP runtime preparation failed")
|
||||
.with_context("path", path.to_string_lossy().into_owned())
|
||||
.with_context("reason", reason);
|
||||
}
|
||||
|
||||
fn packaging_io_error(path: &std::path::Path, reason: &'static str, source: std::io::Error) -> ksp_core_lib::Error {
|
||||
return packaging_error(path, reason).with_source(source);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/packaging.rs"]
|
||||
mod tests;
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-config-lib/tests/public_api.rs
|
||||
// version: 20
|
||||
// version: 21
|
||||
|
||||
//! Integration tests for the public `ksp-config-lib` bootstrap, registry, JSON/profile/composite, environment-resolution, sensitivity,
|
||||
//! Logging/Transport adapters and management contracts.
|
||||
@@ -270,3 +270,11 @@ fn wallet_adapter_contract_is_available_from_crate_root() {
|
||||
assert_eq!(ksp_config_lib::DEFAULT_STD_WALLET_FILENAME, "std.wallet.json");
|
||||
assert_eq!(ksp_config_lib::DEFAULT_STD_WALLET_SCHEMA_FILENAME, "std.wallet.schema.json");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn packaged_runtime_layout_contract_is_available_from_crate_root() {
|
||||
let prepare: fn(&std::path::Path) -> ksp_core_lib::Result<ksp_config_lib::PackagedRuntimeLayout> = ksp_config_lib::prepare_packaged_runtime;
|
||||
let _ = prepare;
|
||||
assert_eq!(ksp_config_lib::ERROR_CODE_PACKAGED_RUNTIME_PREPARATION_FAILED.domain(), "config");
|
||||
assert_eq!(ksp_config_lib::ERROR_CODE_PACKAGED_RUNTIME_PREPARATION_FAILED.code(), "packaged_runtime_preparation_failed");
|
||||
}
|
||||
|
||||
90
crates/ksp-config-lib/unit_tests/packaging.rs
Normal file
90
crates/ksp-config-lib/unit_tests/packaging.rs
Normal file
@@ -0,0 +1,90 @@
|
||||
// file: crates/ksp-config-lib/unit_tests/packaging.rs
|
||||
// version: 2
|
||||
|
||||
fn write_packaged_registry(resource_root: &std::path::Path, marker: &str) -> std::io::Result<()> {
|
||||
let registry = crate::ConfigFileRegistry::defaults();
|
||||
assert!(registry.is_ok(), "Config registry fixture should be constructible: {registry:?}");
|
||||
if let std::result::Result::Ok(registry) = registry {
|
||||
for descriptor in registry.descriptors() {
|
||||
let root = match descriptor.kind() {
|
||||
crate::ConfigFileKind::Config => resource_root.join(crate::DEFAULT_CFG_PATH),
|
||||
crate::ConfigFileKind::Schema => resource_root.join(crate::DEFAULT_SCHEMA_PATH),
|
||||
};
|
||||
let create = std::fs::create_dir_all(root.as_path());
|
||||
if let std::result::Result::Err(error) = create {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let path = root.join(descriptor.filename());
|
||||
let content = format!("{marker}:{}", descriptor.file_id().as_str());
|
||||
let write = std::fs::write(path, content.as_bytes());
|
||||
if let std::result::Result::Err(error) = write {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn packaged_runtime_seeds_configs_and_synchronizes_schemas_without_overwriting_user_config() {
|
||||
let resource = tempfile::tempdir();
|
||||
assert!(resource.is_ok(), "resource fixture should be creatable: {resource:?}");
|
||||
let runtime = tempfile::tempdir();
|
||||
assert!(runtime.is_ok(), "runtime fixture should be creatable: {runtime:?}");
|
||||
if let (std::result::Result::Ok(resource), std::result::Result::Ok(runtime)) = (resource, runtime) {
|
||||
let packaged = write_packaged_registry(resource.path(), "first");
|
||||
assert!(packaged.is_ok(), "packaged Config fixture should be writable: {packaged:?}");
|
||||
let first = super::prepare_packaged_runtime_at(resource.path(), runtime.path());
|
||||
assert!(first.is_ok(), "first packaged runtime should prepare: {first:?}");
|
||||
if let std::result::Result::Ok(first) = first {
|
||||
assert_eq!(first.runtime_root(), runtime.path());
|
||||
assert_eq!(first.cfg_path(), runtime.path().join(crate::DEFAULT_CFG_PATH));
|
||||
assert_eq!(first.schema_path(), runtime.path().join(crate::DEFAULT_SCHEMA_PATH));
|
||||
let user_logging = first.cfg_path().join(crate::DEFAULT_STD_LOGGING_FILENAME);
|
||||
let user_write = std::fs::write(user_logging.as_path(), b"user-owned-config");
|
||||
assert!(user_write.is_ok(), "Config fixture should be replaceable: {user_write:?}");
|
||||
let packaged = write_packaged_registry(resource.path(), "second");
|
||||
assert!(packaged.is_ok(), "updated packaged fixture should be writable: {packaged:?}");
|
||||
let second = super::prepare_packaged_runtime_at(resource.path(), runtime.path());
|
||||
assert!(second.is_ok(), "second packaged runtime should prepare: {second:?}");
|
||||
let retained = std::fs::read(user_logging.as_path());
|
||||
assert!(retained.is_ok(), "retained Config fixture should remain readable: {retained:?}");
|
||||
if let std::result::Result::Ok(retained) = retained {
|
||||
assert_eq!(retained, b"user-owned-config");
|
||||
}
|
||||
let schema = first.schema_path().join(crate::DEFAULT_STD_LOGGING_SCHEMA_FILENAME);
|
||||
let synchronized = std::fs::read_to_string(schema.as_path());
|
||||
assert!(synchronized.is_ok(), "synchronized schema fixture should be readable: {synchronized:?}");
|
||||
if let std::result::Result::Ok(synchronized) = synchronized {
|
||||
assert!(synchronized.starts_with("second:"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn packaged_runtime_rejects_symlink_destination() {
|
||||
let resource = tempfile::tempdir();
|
||||
assert!(resource.is_ok(), "resource fixture should be creatable: {resource:?}");
|
||||
let runtime = tempfile::tempdir();
|
||||
assert!(runtime.is_ok(), "runtime fixture should be creatable: {runtime:?}");
|
||||
if let (std::result::Result::Ok(resource), std::result::Result::Ok(runtime)) = (resource, runtime) {
|
||||
let packaged = write_packaged_registry(resource.path(), "packaged");
|
||||
assert!(packaged.is_ok(), "packaged Config fixture should be writable: {packaged:?}");
|
||||
let cfg = runtime.path().join(crate::DEFAULT_CFG_PATH);
|
||||
let create = std::fs::create_dir_all(cfg.as_path());
|
||||
assert!(create.is_ok(), "runtime Config root should be creatable: {create:?}");
|
||||
let outside = runtime.path().join("outside.json");
|
||||
let outside_write = std::fs::write(outside.as_path(), b"outside");
|
||||
assert!(outside_write.is_ok(), "symlink target should be creatable: {outside_write:?}");
|
||||
let link = cfg.join(crate::DEFAULT_STD_LOGGING_FILENAME);
|
||||
let link_result = std::os::unix::fs::symlink(outside.as_path(), link.as_path());
|
||||
assert!(link_result.is_ok(), "symlink fixture should be creatable: {link_result:?}");
|
||||
let result = super::prepare_packaged_runtime_at(resource.path(), runtime.path());
|
||||
assert!(result.is_err(), "symlink destination must be rejected");
|
||||
if let std::result::Result::Err(error) = result {
|
||||
assert_eq!(error.code(), crate::ERROR_CODE_PACKAGED_RUNTIME_PREPARATION_FAILED);
|
||||
}
|
||||
}
|
||||
}
|
||||
256
deltas/0.2.6/pre.018.md
Normal file
256
deltas/0.2.6/pre.018.md
Normal file
@@ -0,0 +1,256 @@
|
||||
<!-- file: deltas/0.2.6/pre.018.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Delta `0.2.6-pre.018` — candidate finale Wallet Desk, runtime packagé et documentation
|
||||
|
||||
## Base requise
|
||||
|
||||
```text
|
||||
0.2.6-pre.017 appliquée
|
||||
workspace.package.version = 0.2.6-pre.17
|
||||
```
|
||||
|
||||
Le checkpoint opérateur du 22 août 2026 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 84 passed / 1 ignored
|
||||
Wallet Desk desktop_contract 17/17 OK
|
||||
Wallet Desk desktop_security 12/12 OK
|
||||
Wallet Desk release_compliance 4/4 OK
|
||||
smokes réseau / benchmark ignored comme prévu
|
||||
```
|
||||
|
||||
Le parcours manuel Tauri crate-local est également acquis : avec un `KSP_WALLETS_DIRECTORY` isolé, Wallet Desk importe une keypair Solana CLI JSON, `ksp-wallet-lib` journalise la création native `format_version=2`, la session OWNER s'ouvre, l'inventory retrouve le V2 et `getBalance` Devnet réussit avec un slot non nul.
|
||||
|
||||
`pre.017` est donc accepté comme base de la candidate finale.
|
||||
|
||||
## Signal technique
|
||||
|
||||
Cette tranche modifie le bootstrap release, les resources Tauri, Config et la documentation :
|
||||
|
||||
```text
|
||||
workspace.package.version = 0.2.6-pre.18
|
||||
package.json Config Desk = 0.2.6-pre.18
|
||||
package.json Wallet Desk = 0.2.6-pre.18
|
||||
tauri.conf Config Desk = 0.2.6-pre.18
|
||||
tauri.conf Wallet Desk = 0.2.6-pre.18
|
||||
commit = v0.2.6-pre.018
|
||||
```
|
||||
|
||||
Aucun tag prerelease.
|
||||
|
||||
## Objectifs de la candidate
|
||||
|
||||
`pre.018` n'ajoute aucune nouvelle capacité Wallet métier. La tranche ferme :
|
||||
|
||||
```text
|
||||
runtime distribué indépendant du checkout source
|
||||
resources Config/Schemas packagées
|
||||
racine runtime KSP user-writable commune aux Desks
|
||||
README/USAGE Wallet Desk
|
||||
synchronisation docs/règles/compliance/prompt 0.2.7
|
||||
versions applicatives finales de prerelease
|
||||
canaris packaging/resources
|
||||
ordre du gate final et build Tauri absolu dernier
|
||||
```
|
||||
|
||||
## Runtime packagé : ownership Config
|
||||
|
||||
Le parcours debug existant reste inchangé : Tauri est lancé crate-local, puis `main.rs` normalise le CWD Rust vers la racine du workspace avant Config.
|
||||
|
||||
Le parcours release ne dépend plus de ce checkout.
|
||||
|
||||
Les deux `tauri.conf.json` embarquent exactement les huit resources actuellement connues du `ConfigFileRegistry` :
|
||||
|
||||
```text
|
||||
config/composite.ksp-app-wallet-desk.json
|
||||
config/std.logging.json
|
||||
config/std.transport.json
|
||||
config/std.wallet.json
|
||||
config/schemas/composite.schema.json
|
||||
config/schemas/std.logging.schema.json
|
||||
config/schemas/std.transport.schema.json
|
||||
config/schemas/std.wallet.schema.json
|
||||
```
|
||||
|
||||
Ne sont jamais embarqués :
|
||||
|
||||
```text
|
||||
.env
|
||||
logs/
|
||||
wallets/
|
||||
secrets runtime
|
||||
```
|
||||
|
||||
`ksp-config-lib` reçoit la dépendance `directories ^6.0` et expose :
|
||||
|
||||
```rust
|
||||
prepare_packaged_runtime(resource_root)
|
||||
PackagedRuntimeLayout
|
||||
ERROR_CODE_PACKAGED_RUNTIME_PREPARATION_FAILED
|
||||
```
|
||||
|
||||
La racine writable commune est résolue par `directories::ProjectDirs` avec l'identité projet KSP, sans path user codé en dur dans les applications.
|
||||
|
||||
Politique de préparation :
|
||||
|
||||
```text
|
||||
Config documents package-owned defaults
|
||||
-> seed uniquement si la destination utilisateur est absente
|
||||
-> une Config existante n'est jamais écrasée silencieusement
|
||||
|
||||
JSON Schemas
|
||||
-> package-owned current authority
|
||||
-> synchronisés depuis les resources à chaque lancement
|
||||
|
||||
.env
|
||||
-> jamais packagé / jamais seedé
|
||||
-> reste possédé et géré par Config dans la racine runtime writable
|
||||
```
|
||||
|
||||
La persistence réutilise la primitive atomique Config existante et rejette les destinations symlink/non-file.
|
||||
|
||||
## Bootstrap Tauri release
|
||||
|
||||
`src/tauri.rs` des deux applications suit désormais :
|
||||
|
||||
```text
|
||||
generate_context
|
||||
-> configure_packaged_runtime(context)
|
||||
-> AppState::initialize(arguments)
|
||||
-> Builder/run
|
||||
```
|
||||
|
||||
`configure_packaged_runtime` compile dans tous les profils afin que `cargo check`/Clippy debug vérifient également les APIs Tauri utilisées par le parcours release. En debug, il retourne immédiatement sans modifier le comportement acquis.
|
||||
|
||||
En release :
|
||||
|
||||
```text
|
||||
tauri::utils::platform::resource_dir
|
||||
-> ksp_config_lib::prepare_packaged_runtime
|
||||
-> std::env::set_current_dir(runtime_root)
|
||||
-> AppState::initialize
|
||||
```
|
||||
|
||||
Le guest frontend ne reçoit aucune capability filesystem supplémentaire.
|
||||
|
||||
## Canaris Config / desktop
|
||||
|
||||
`ksp-config-lib` ajoute des tests déterministes qui prouvent :
|
||||
|
||||
- premier seed des Config packagées ;
|
||||
- conservation d'une Config utilisateur déjà modifiée ;
|
||||
- resynchronisation d'un schema mis à jour dans le package ;
|
||||
- layout runtime/cfg/schema cohérent ;
|
||||
- rejet d'une destination symlink sous Unix ;
|
||||
- exposition du contrat public depuis la racine de crate.
|
||||
|
||||
Les deux `desktop_contract.rs` vérifient les mappings `bundle.resources` et l'appel de préparation du runtime packagé.
|
||||
|
||||
`release_compliance.rs` Wallet Desk vérifie en plus qu'aucune resource `.env`, `logs`, `wallets`, `secret` ou `private` n'entre dans le bundle.
|
||||
|
||||
## Documentation candidate
|
||||
|
||||
Créés :
|
||||
|
||||
```text
|
||||
crates/ksp-app-wallet-desk/README.md
|
||||
crates/ksp-app-wallet-desk/USAGE.md
|
||||
```
|
||||
|
||||
Synchronisés :
|
||||
|
||||
```text
|
||||
crates/ksp-app-config-desk/README.md
|
||||
crates/ksp-app-config-desk/USAGE.md
|
||||
docs/rules/RULES_KSP.md
|
||||
docs/architecture/010-APPS_SERVICES_SCENARIOS_AND_CONTROL.md
|
||||
docs/validation/009-V0_2_6_WALLET_DESK_COMPLIANCE.md
|
||||
docs/validation/000-README.md
|
||||
docs/formats/000-README.md
|
||||
docs/plans/013-V0_2_6_WALLET_DESK_PLAN.md
|
||||
docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md
|
||||
docs/plans/000-README.md
|
||||
docs/000-README.md
|
||||
docs/IDEAS.md
|
||||
ROADMAP.md état global uniquement
|
||||
prompts/011-V0_2_6_START_PROMPT.md
|
||||
prompts/012-V0_2_7_START_PROMPT.md
|
||||
```
|
||||
|
||||
`prompts/012-V0_2_7_START_PROMPT.md` reste dédié à `0.2.7 — WebSocket Solana standard` sur base stable `v0.2.6`.
|
||||
|
||||
`CHANGELOG.md` est audité mais **volontairement inchangé** dans la prerelease. L'entrée stable `0.2.6` appartient à `rel.001` après validation du build final.
|
||||
|
||||
## Dépendances
|
||||
|
||||
Nouvelle dépendance externe :
|
||||
|
||||
```text
|
||||
directories = ^6.0
|
||||
```
|
||||
|
||||
Elle est déclarée uniquement sous `[workspace.dependencies]` et consommée par `ksp-config-lib` via `directories.workspace = true`.
|
||||
|
||||
`tempfile`, déjà possédé par le workspace, est ajouté aux dev-dependencies de Config pour les fixtures packaging isolées.
|
||||
|
||||
Aucune dépendance Solana, réseau, filesystem Tauri guest ou cryptographique supplémentaire n'est ajoutée.
|
||||
|
||||
## Validation opérateur candidate
|
||||
|
||||
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-config-lib
|
||||
cargo test -p ksp-wallet-lib
|
||||
cargo test -p ksp-app-config-desk
|
||||
cargo test -p ksp-app-wallet-desk
|
||||
cargo test --workspace
|
||||
```
|
||||
|
||||
Puis réaudit de dépendances si nécessaire :
|
||||
|
||||
```bash
|
||||
cargo tree -p ksp-config-lib --depth 1
|
||||
cargo tree -p ksp-app-wallet-desk --depth 1
|
||||
cargo tree -p ksp-app-wallet-desk -d
|
||||
```
|
||||
|
||||
Le parcours fonctionnel final et les smokes live retenus sont exécutés avant le build. Exemple :
|
||||
|
||||
```bash
|
||||
KSP_WALLETS_DIRECTORY=var/wallet-desk-pre018 \
|
||||
bash -lc 'cd crates/ksp-app-wallet-desk && cargo tauri dev'
|
||||
```
|
||||
|
||||
Le smoke Devnet opt-in peut être rejoué avant le build si souhaité.
|
||||
|
||||
## Build final
|
||||
|
||||
L'absolue dernière opération de validation est :
|
||||
|
||||
```bash
|
||||
(cd crates/ksp-app-wallet-desk && cargo tauri build)
|
||||
```
|
||||
|
||||
Ne lancer ensuite ni fmt, ni check, ni test, ni autre build avant d'enregistrer le résultat du gate. Si une correction est requise, produire `pre.018-fix.NNN`, rejouer les validations depuis le début, puis refaire le build en dernière opération.
|
||||
|
||||
Si le gate complet est vert, la prochaine étape est uniquement :
|
||||
|
||||
```text
|
||||
0.2.6-rel.001
|
||||
workspace.package.version -> 0.2.6
|
||||
CHANGELOG stable -> 0.2.6
|
||||
tag final -> v0.2.6
|
||||
```
|
||||
|
||||
`rel.001` n'introduit aucune nouvelle fonctionnalité.
|
||||
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/IDEAS.md -->
|
||||
<!-- version: 22 -->
|
||||
<!-- version: 23 -->
|
||||
|
||||
# Idées à explorer
|
||||
|
||||
@@ -206,7 +206,7 @@ Chaque format doit être étudié côté sécurité, round-trip, secret/public,
|
||||
|
||||
**Status :** V2 retenu et matérialisé en `0.2.6-pre.015` / facteurs futurs à explorer
|
||||
|
||||
Le JSON V1 actuel reste le format historique stable et lisible. Base64 seul n’apporte aucune sécurité et resterait un texte trivialement décodable avec environ un tiers de surcharge. La décision initialement envisagée pour `0.2.7` a été ramenée dans `0.2.6` : `pre.015` définit un **wire binaire V2 KSP** avec magic/framing explicite, entiers big-endian, identifiants numériques stables, longueurs bornées et lecture/écriture canonique stricte. V1 reste supporté sans réinterprétation ; la façade de lecture multi-version, la création/persistence V2, les APIs `_v1/_v2` et la politique `DEFAULT_WALLET_FORMAT = V2` sont matérialisées en `pre.016`, puis la migration explicite V1 -> V2 reste planifiée en `pre.017`. Le V2 ne modifie pas à lui seul les garanties cryptographiques de VIEW/OWNER, keypair ou import/export.
|
||||
Le JSON V1 actuel reste le format historique stable et lisible. Base64 seul n’apporte aucune sécurité et resterait un texte trivialement décodable avec environ un tiers de surcharge. La décision initialement envisagée pour `0.2.7` a été ramenée dans `0.2.6` : `pre.015` définit un **wire binaire V2 KSP** avec magic/framing explicite, entiers big-endian, identifiants numériques stables, longueurs bornées et lecture/écriture canonique stricte. V1 reste supporté sans réinterprétation ; la façade de lecture multi-version, la création/persistence V2, les APIs `_v1/_v2` et la politique `DEFAULT_WALLET_FORMAT = V2` sont matérialisées en `pre.016`, puis la migration explicite OWNER-authentifiée V1 -> V2 est matérialisée en `pre.017`. Le V2 ne modifie pas à lui seul les garanties cryptographiques de VIEW/OWNER, keypair ou import/export.
|
||||
|
||||
V2 est désormais réservé au wire binaire KSP sans second facteur. Un futur V3 pourra introduire d’autres modèles d’autorisation, notamment password + facteur supplémentaire. `ksp-wallet-lib` restera propriétaire du format, des challenges et de la vérification, mais toute interaction réelle (OTP, enrollment/recovery, hardware/WebAuthn, validation distante) exigera une évolution de Wallet Desk ou du client concerné. Un seed TOTP stocké uniquement dans le même fichier que le wallet ne doit pas être présenté automatiquement comme un second facteur indépendant contre un attaquant possédant ce fichier.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/architecture/010-APPS_SERVICES_SCENARIOS_AND_CONTROL.md -->
|
||||
<!-- version: 3 -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# Applications, services, scenarios et control plane
|
||||
|
||||
@@ -125,6 +125,8 @@ TS-RS reste principalement une frontière applicative pour les DTO exposés à T
|
||||
|
||||
Une application Tauri peut devoir intégrer un plugin/framework de tracing, mais `ksp-logging-lib` reste la façade/politique de logging KSP.
|
||||
|
||||
En distribution, une application Tauri KSP ne dépend pas du checkout source ni du CWD du launcher. Les resources Config/Schemas nécessaires sont embarquées dans le bundle ; Tauri résout leur racine immutable et `ksp-config-lib` prépare une racine KSP user-writable commune avant le bootstrap applicatif. Les Config utilisateur existantes sont conservées, les schemas package-owned sont resynchronisés et `.env` reste une ressource locale writable jamais embarquée. Cette adaptation de packaging ne transfère ni ownership Config ni accès filesystem au frontend.
|
||||
|
||||
# Workers comme services indépendants
|
||||
|
||||
Les workers KSP doivent pouvoir fonctionner comme **services/processus indépendants**.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/formats/000-README.md -->
|
||||
<!-- version: 7 -->
|
||||
<!-- version: 8 -->
|
||||
|
||||
# Formats KSP
|
||||
|
||||
@@ -10,4 +10,4 @@ Une spécification de format décrit le wire exact, les encodages, les limites,
|
||||
## Formats actifs
|
||||
|
||||
- [`KSPWALLET_V1.md`](KSPWALLET_V1.md) — spécification du format natif autonome `.kspwallet` V1. `0.2.5-pre.003` fige l'enveloppe/wire et les transcripts/AAD, `pre.004` ajoute Argon2id/XChaCha20-Poly1305/CSPRNG OS, `pre.005` fixe les payloads plaintext, le profil de création KSP calibré, l'autorité Ed25519 OWNER et le vecteur complet, `pre.006`–`pre.008` matérialisent persistence/administration/transfert, `pre.009` ferme l'audit adversarial/interoperabilité/compliance et `pre.010` synchronise la documentation finale sans modifier le wire V1.
|
||||
- [`KSPWALLET_V2.md`](KSPWALLET_V2.md) — wire binaire natif V2 introduit par `0.2.6-pre.015` puis runtime multi-version matérialisé en `pre.016` : framing canonique KSP, IDs numériques, longueurs big-endian, aucun Base64/compression, domains/transcripts V2 distincts et politique `default != latest`.
|
||||
- [`KSPWALLET_V2.md`](KSPWALLET_V2.md) — wire binaire natif V2 introduit par `0.2.6-pre.015`, runtime multi-version/default V2 matérialisé en `pre.016`, puis migration explicite OWNER-authentifiée V1 -> V2 en `pre.017` : framing canonique KSP, IDs numériques, longueurs big-endian, aucun Base64/compression, domains/transcripts V2 distincts et politique `default != latest`.
|
||||
|
||||
@@ -105,7 +105,7 @@ Une implémentation conforme doit vérifier les bornes **avant** toute allocatio
|
||||
Le début de fichier est strictement :
|
||||
|
||||
| Ordre | Champ | Taille | Valeur / règle |
|
||||
|---:|---|---:|---|
|
||||
|------:|---------------------------|-----------------:|------------------------------------------------|
|
||||
| 1 | `magic` | 9 | ASCII exact `KSPWALLET` |
|
||||
| 2 | `format_version` | 2 | `0x0002` |
|
||||
| 3 | `document_length` | 4 | longueur totale exacte du fichier |
|
||||
@@ -155,7 +155,7 @@ Aucun compteur de slots ou de compartiments n'est nécessaire : leur cardinalit
|
||||
Chaque key slot est encodé ainsi :
|
||||
|
||||
| Champ | Taille | Valeur / règle |
|
||||
|---|---:|---|
|
||||
|--------------------------|---------:|-----------------------------------------|
|
||||
| `role` | 1 | `0x01` OWNER, `0x02` VIEW |
|
||||
| `slot_id` | 16 | identifiant binaire exact |
|
||||
| `kdf_algorithm` | 1 | `0x01` Argon2id |
|
||||
@@ -190,7 +190,7 @@ Toute divergence est invalide avant KDF/déchiffrement.
|
||||
Chaque compartiment est encodé :
|
||||
|
||||
| Champ | Taille | Valeur / règle |
|
||||
|---|---:|---|
|
||||
|---------------------|---------:|------------------------------------------------------|
|
||||
| `kind` | 1 | `0x01` OWNER-CONTROL, `0x02` METADATA, `0x03` SECRET |
|
||||
| `payload_version` | 4 | `1` pour le profil initial V2 |
|
||||
| `algorithm` | 1 | `0x01` XChaCha20-Poly1305 |
|
||||
@@ -215,7 +215,7 @@ Les payloads plaintext V2 conservent le modèle fonctionnel établi en V1 pour c
|
||||
La fin du document est :
|
||||
|
||||
| Champ | Taille | Valeur / règle |
|
||||
|---|---:|---|
|
||||
|-----------------------------|-------:|---------------------------|
|
||||
| `state_signature.algorithm` | 1 | `0x01` Ed25519 |
|
||||
| `state_signature.signature` | 64 | signature detached exacte |
|
||||
|
||||
@@ -378,7 +378,7 @@ Le nonce/ciphertext n'est pas inclus dans son propre AAD.
|
||||
V1 et V2 sont deux formats explicites :
|
||||
|
||||
| Propriété | V1 | V2 |
|
||||
|---|---|---|
|
||||
|--------------------------|------------------------------------|---------------|
|
||||
| enveloppe | JSON UTF-8 | binaire KSP |
|
||||
| champs binaires | Base64url no-pad | bytes directs |
|
||||
| version | `1` | `2` |
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/plans/000-README.md -->
|
||||
<!-- version: 51 -->
|
||||
<!-- version: 52 -->
|
||||
|
||||
# Plans KSP
|
||||
|
||||
@@ -21,7 +21,7 @@ Un plan décrit le périmètre, les décisions déjà acquises, les questions ou
|
||||
- [`010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md`](010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md) — plan historique clôturé de la release stable `0.2.3 — HTTP Transactions`, ouvert par `pre.001`, exécuté jusqu'à `pre.009` puis publié par `rel.001`; il couvre les 11 méthodes, la classification `8 Read / 2 WriteSubmission / 1 Simulation`, `KSP-TRANSPORT-007`, le no-resend et la préparation de `0.2.4`.
|
||||
- [`011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md`](011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md) — plan historique clôturé de la release stable `0.2.4`, ouvert par `pre.001`, exécuté jusqu’à `pre.009`, complété par le fix documentaire Wallet `pre.009-fix.001` puis publié par `rel.001`; il couvre les 10 Blocks + 5 Economics et la compliance finale `52/52 + 14/14` sous `KSP-TRANSPORT-007`.
|
||||
- [`012-V0_2_5_WALLET_FOUNDATION_PLAN.md`](012-V0_2_5_WALLET_FOUNDATION_PLAN.md) — plan historique clôturé de la release stable `0.2.5 — Wallet foundation`, ouvert par `pre.001`, livré jusqu’à `pre.010`, renforcé par `pre.010-fix.001`–`fix.003` pour Dalek 3 et la normalisation Rust/audit structurel, puis publié par `rel.001`; il couvre `.kspwallet` V1, VIEW/OWNER, crypto, persistence, administration, transfer et compliance.
|
||||
- [`013-V0_2_6_WALLET_DESK_PLAN.md`](013-V0_2_6_WALLET_DESK_PLAN.md) — plan actif de `0.2.6 — Wallet Desk`, ouvert par `pre.001`; `pre.002` matérialise le shell Tauri et `pre.003` la composition `std.wallet`/composite ainsi que la préparation des répertoires. Le plan fixe aussi secrets `KSP_SECRET_WALLET_PASS_*`, inventory DataTables/path safety, lifecycle VIEW/OWNER, screen/command/DTO maps, balance HTTP, administration retenue et forecast étendu jusqu’à `pre.018` : `pre.014` polish Bootstrap/splashscreen, `pre.015` wire binaire `.kspwallet` V2 et `pre.016` APIs génériques/versionnées + runtime V2 et `pre.017` migration explicite V1 -> V2 désormais matérialisés ; prochain gate `pre.018` documentation candidate et build Tauri final.
|
||||
- [`013-V0_2_6_WALLET_DESK_PLAN.md`](013-V0_2_6_WALLET_DESK_PLAN.md) — plan actif de `0.2.6 — Wallet Desk`, ouvert par `pre.001`; shell Tauri, composition Config/Wallet/Transport, inventory, lifecycle VIEW/OWNER, administration, import/export et compliance sont acquis. `pre.015`–`pre.017` ajoutent le wire binaire `.kspwallet` V2, les APIs génériques/versionnées, le runtime V2 et la migration explicite V1 -> V2. `pre.018` matérialise désormais la candidate documentaire et le runtime Tauri packagé commun user-writable ; validation opérateur/build final puis `rel.001` restent à fermer.
|
||||
|
||||
Le `pre.001` de chaque release fonctionnelle peut introduire son propre plan détaillé lorsque la release s'ouvre.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md -->
|
||||
<!-- version: 76 -->
|
||||
<!-- version: 77 -->
|
||||
|
||||
# Séquence des releases fonctionnelles KSP
|
||||
|
||||
@@ -449,7 +449,7 @@ pre.018 documentation finale, validations, prompt 0.2.7 et cargo tauri build en
|
||||
rel.001 publication stable 0.2.6
|
||||
```
|
||||
|
||||
`pre.017` est désormais matérialisé : la migration V1 -> V2 est explicite, OWNER-authentifiée et séparée de toute ouverture normale, avec copie no-clobber et remplacement in-place stale-protected. Le prochain gate est `pre.018`.
|
||||
`pre.017` est désormais matérialisé et validé : la migration V1 -> V2 est explicite, OWNER-authentifiée et séparée de toute ouverture normale, avec copie no-clobber et remplacement in-place stale-protected. `pre.018` matérialise la candidate finale : README/USAGE Wallet Desk, documentation/compliance, runtime packagé commun à Config Desk/Wallet Desk avec Config resources + racine user-writable, et prompt `0.2.7`. Le build Wallet Desk et `rel.001` restent les gates finaux.
|
||||
|
||||
La tranche `pre.014` est réservée aux défauts visuels/templating observés en usage réel, notamment les scrollbars occasionnelles du splashscreen ; son contenu précis sera borné à partir du retour opérateur avant implémentation. `ROADMAP.md` reste synthétique et `CHANGELOG.md` n'est synchronisé qu'à la phase documentaire finale.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/plans/013-V0_2_6_WALLET_DESK_PLAN.md -->
|
||||
<!-- version: 34 -->
|
||||
<!-- version: 35 -->
|
||||
|
||||
# Plan `0.2.6` — Wallet Desk
|
||||
|
||||
@@ -771,7 +771,7 @@ 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 |
|
||||
@@ -1741,7 +1741,7 @@ Une future API `_v3` pourra être ajoutée sans transformer automatiquement le d
|
||||
|
||||
### `pre.017` — migration V1 -> V2 + persistence/canaris
|
||||
|
||||
**Statut : matérialisé par `0.2.6-pre.017`.**
|
||||
**Statut : matérialisé par `0.2.6-pre.017` et checkpoint opérateur entièrement vert.**
|
||||
|
||||
La migration est une opération explicite OWNER-authentifiée et reste séparée de toute ouverture normale :
|
||||
|
||||
@@ -1765,23 +1765,41 @@ Invariants retenus :
|
||||
|
||||
Les canaris `pre.017` couvrent migration mémoire, conservation identité/metadata/note IDs, choix du credential VIEW cible, no-clobber, remplacement in-place, tampering, stale-state et exposition API publique. Wallet Desk reste version-neutral et non-migrant : aucune ouverture/inventory du Desk ne déclenche la migration.
|
||||
|
||||
### `pre.018` — documentation finale, candidate build et prompt suivant
|
||||
### `pre.018` — documentation finale, runtime packagé et candidate build
|
||||
|
||||
Dernière tranche prévue avant `rel.001` :
|
||||
**Statut : candidate matérialisée ; validations opérateur et build final encore à exécuter.**
|
||||
|
||||
Cette tranche ferme la stratégie CWD/resources release qui restait ouverte depuis le polish desktop :
|
||||
|
||||
```text
|
||||
README/USAGE Wallet Desk
|
||||
docs/validation 0.2.6 finalisée
|
||||
ROADMAP / functional sequence / CHANGELOG synchronisés
|
||||
stratégie CWD/resources release packagée fermée
|
||||
dépendances et versions finales réauditées
|
||||
validation Rust workspace finale
|
||||
parcours fonctionnel cargo tauri dev final
|
||||
prompt de démarrage 0.2.7 WebSocket finalisé
|
||||
cargo tauri build en toute dernière opération
|
||||
bundle.resources : 4 documents Config + 4 schemas du registre courant
|
||||
Config packagée : seed uniquement si absente
|
||||
Schemas : synchronisés depuis le package courant à chaque lancement
|
||||
.env : jamais embarqué ni seedé
|
||||
racine runtime writable : ProjectDirs commun KSP
|
||||
release Tauri : resource_dir -> prepare_packaged_runtime -> set_current_dir -> AppState::initialize
|
||||
debug Tauri : comportement workspace-root existant conservé
|
||||
```
|
||||
|
||||
Si une correction technique apparaît, utiliser `pre.018-fix.NNN` ou insérer une tranche supplémentaire.
|
||||
Les deux Desks utilisent le même runtime KSP writable et ne dépendent donc plus du checkout source en distribution. Aucun accès filesystem supplémentaire n'est accordé au guest frontend. `ksp-config-lib` reste propriétaire de la préparation des Config/schemas et de la localisation user-writable.
|
||||
|
||||
Livrables candidate :
|
||||
|
||||
```text
|
||||
README/USAGE Wallet Desk créés
|
||||
README/USAGE Config Desk synchronisés sur le runtime packagé
|
||||
docs/validation 0.2.6 finalisée jusqu'au gate opérateur
|
||||
règles KSP packaging/CWD durables
|
||||
ROADMAP / functional sequence / indexes mis à jour globalement
|
||||
CHANGELOG audité mais volontairement inchangé jusqu'à rel.001
|
||||
versions Cargo/package/Tauri alignées sur pre.018
|
||||
prompt de démarrage 0.2.7 WebSocket finalisé
|
||||
canaris resources/packaging ajoutés
|
||||
```
|
||||
|
||||
Le checkpoint `pre.017` de référence est vert : fmt/audit/check/clippy/workspace tests passent, `ksp-wallet-lib` compte 84 tests passés + 1 benchmark ignoré, et le parcours Tauri confirme un import créant réellement un V2 puis un `getBalance` Devnet réussi.
|
||||
|
||||
Après application de `pre.018`, toutes les validations Rust, le parcours `cargo tauri dev`, les éventuels smokes live et audits de dépendances sont exécutés avant le build. `(cd crates/ksp-app-wallet-desk && cargo tauri build)` demeure **l'absolue dernière opération de validation**. Si une correction technique apparaît, utiliser `pre.018-fix.NNN` avant de rejouer le gate depuis le début.
|
||||
|
||||
### `rel.001` — publication stable
|
||||
|
||||
@@ -1955,6 +1973,6 @@ splash : feed #debug-info pleine largeur dans les deux Desks
|
||||
pre.013 gate : le canari ne fige plus une shell_phase destinée à évoluer à chaque tranche
|
||||
```
|
||||
|
||||
Le recalage du CWD n'existe actuellement qu'en `debug_assertions` dans les deux `main.rs`. La candidate finale `pre.018` doit donc vérifier explicitement la stratégie de résolution production/bundle pour `config`, `.env`, `logs` et `wallets` avant le build final ; le comportement release ne doit pas être supposé identique au parcours `cargo tauri dev`.
|
||||
Le recalage du CWD n'existait alors qu'en `debug_assertions` dans les deux `main.rs`. Ce point historique est fermé par `pre.018` : le runtime release résout les resources Tauri puis une racine KSP user-writable via Config avant le bootstrap ; il ne suppose plus le comportement du parcours `cargo tauri dev`.
|
||||
|
||||
Ce fix touche le frontend/build contract et porte donc le signal technique `workspace.package.version = 0.2.6-pre.14.fix.1`. `ROADMAP.md`, `CHANGELOG.md` et `docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md` restent inchangés.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/rules/RULES_KSP.md -->
|
||||
<!-- version: 37 -->
|
||||
<!-- version: 38 -->
|
||||
|
||||
# Règles spécifiques à KSP
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
- **KSP-CONFIG-015** — L'environnement du processus reste read-only. La surface management peut créer/modifier/supprimer uniquement des entrées KSP/KSPB du `.env`; chaque mutation rapporte séparément changement de source persistée, changement effectif courant, shadowing par le process et besoin de reload.
|
||||
- **KSP-CONFIG-016** — Les rapports management ordinaires n'exposent que des valeurs sûres/redacted. L'accès en clair à une valeur d'environnement passe par un appel `reveal_*` explicite; l'authentification/autorisation de l'utilisateur final appartient à l'application et cette révélation n'autorise jamais le secret dans les logs/`Debug`/diagnostics génériques.
|
||||
- **KSP-CONFIG-017** — Les frontières d'ownership Config sont vérifiées par des audits exécutables du workspace : Core/Logging ne dépendent pas de Config, les crates hors `ksp-config-lib` ne lisent pas directement les variables KSP/KSPB via `std::env::var*`/énumération de l'environnement et ne codent pas en dur les noms physiques des fichiers gérés lorsqu'un contrat Config existe.
|
||||
- **KSP-CONFIG-018** — Dans un runtime desktop packagé, `ksp-config-lib` possède la préparation de la racine KSP writable à partir des resources applicatives : les documents Config packagés ne sont seedés que lorsqu'ils sont absents, les JSON Schemas possédés par le package courant sont resynchronisés à chaque lancement, et `.env` n'est jamais embarqué ni seedé. La localisation physique writable est résolue par une primitive plateforme dédiée et n'est pas codée en dur dans les applications.
|
||||
- **KSP-CONFIG-018** — L'inventaire `.env.example` est vérifié automatiquement contre les variables KSP/KSPB concrètes utilisées par les JSON sous `config/` et le code Rust production. Toute clé runtime détectée doit posséder une entrée d'inventaire précédée d'un commentaire explicatif.
|
||||
|
||||
## Programmes et exécution
|
||||
@@ -231,6 +232,7 @@
|
||||
- **KSP-APP-032** — Les interfaces desk KSP n’utilisent pas les dialogues bloquants natifs du navigateur (`window.alert`, `window.confirm`, `window.prompt`) pour les interactions applicatives normales. Les confirmations destructives ou privilégiées utilisent un modal Bootstrap intégré à l’UI, instrumenté par le bridge Logging ; toute exception doit être explicitement justifiée et documentée.
|
||||
- **KSP-APP-033** — Un test d’une application ou d’un manager qui peut modifier un document Config du workspace ne traite jamais les valeurs courantes de ce document comme une fixture immuable. Les tests de valeurs exactes utilisent une fixture isolée ; les tests qui lisent la Config workspace vérifient uniquement des invariants, la validité et la cohérence source → résolution → runtime afin de rester valides après une édition légitime par Config Desk.
|
||||
- **KSP-APP-034** — Pour une application Tauri KSP, npm n’est jamais invoqué directement pour lancer les scripts applicatifs de développement, contrôle ou build. Les seules commandes npm directes servent à installer ou mettre à jour les dépendances déclarées. Le cycle applicatif est crate-local : `(cd crates/<app> && cargo tauri dev)` et `(cd crates/<app> && cargo tauri build)` ; Tauri déclenche lui-même les hooks `beforeDevCommand` / `beforeBuildCommand`, dont le `cwd` reste explicitement la crate. Dans une validation finale, le `cargo tauri build` crate-local est exécuté **en toute dernière opération**, après fmt/audit/check/clippy, tests et parcours fonctionnel.
|
||||
- **KSP-APP-035** — Une application Tauri KSP distribuée ne dépend ni d'un checkout source ni du répertoire courant choisi par l'utilisateur. Les Config/Schemas nécessaires sont embarqués via `bundle.resources`; avant le bootstrap applicatif release, Tauri résout son resource directory, délègue à `ksp-config-lib` la préparation du runtime writable commun puis active cette racine comme current working directory. Le guest frontend n'obtient aucune permission filesystem supplémentaire pour ce bootstrap.
|
||||
|
||||
## Data plane / control plane
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/validation/000-README.md -->
|
||||
<!-- version: 15 -->
|
||||
<!-- version: 16 -->
|
||||
|
||||
# Validations KSP
|
||||
|
||||
@@ -14,9 +14,7 @@ Documents :
|
||||
- [`003-V0_2_1_ONCHAIN_HTTP.md`](003-V0_2_1_ONCHAIN_HTTP.md) — matrice de clôture de `0.2.1 — HTTP Solana foundation`, registry 52+14, résilience, Config -> Transport, quatre canaris et smoke Devnet opt-in.
|
||||
- [`004-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER.md`](004-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER.md) — matrice finale validée de `0.2.2`, 22 wrappers Accounts/Tokens/Cluster, canaries 52+14, graphes Cargo, smoke Devnet Transport pur, smoke historique Config -> Transport et préparation de `0.2.3`.
|
||||
- [`005-V0_2_3_KSP_TRANSPORT_007_RETRO_AUDIT.md`](005-V0_2_3_KSP_TRANSPORT_007_RETRO_AUDIT.md) — réaudit rétroactif de complétude `KSP-TRANSPORT-007` des 37 wrappers HTTP typés livrés de `0.2.1` à `0.2.3-pre.007`, avec preuves Solana/Agave et verdict méthode par méthode.
|
||||
|
||||
- [`006-V0_2_3_HTTP_TRANSACTIONS.md`](006-V0_2_3_HTTP_TRANSACTIONS.md) — matrice finale validée de `0.2.3`, 11 wrappers Transactions, sécurité write/simulation, réaudit 52+14, `KSP-TRANSPORT-007` 37/37, graphes Cargo et deux smokes Devnet passés avant publication stable.
|
||||
- [`007-V0_2_4_HTTP_FINAL_COMPLIANCE.md`](007-V0_2_4_HTTP_FINAL_COMPLIANCE.md) — matrice finale validée de `0.2.4`, inventaire exact 52 current + 14 Deprecated, preuve typed 52/52, audit SIMD final, `KSP-TRANSPORT-007`, workspace complet et deux smokes Devnet passés avant publication stable.
|
||||
- [`008-V0_2_5_WALLET_SECURITY_COMPLIANCE.md`](008-V0_2_5_WALLET_SECURITY_COMPLIANCE.md) — matrice finale validée de la release stable `0.2.5`, threat model V1, canaris adversariaux, reproduction externe des vecteurs, audit de frontières, normalisation Rust/audit structurel, graphes Cargo et checkpoint final `pre.010-fix.003` vert.
|
||||
|
||||
- [`009-V0_2_6_WALLET_DESK_COMPLIANCE.md`](009-V0_2_6_WALLET_DESK_COMPLIANCE.md) — matrice de compliance ouverte en `0.2.6-pre.013`, couvrant composition Config/Wallet/Transport, frontières DTO/IPC/frontend, path/inventory adversarial, capabilities Tauri, dependency firewall, graphes Cargo et smoke Devnet Wallet Desk opt-in.
|
||||
- [`009-V0_2_6_WALLET_DESK_COMPLIANCE.md`](009-V0_2_6_WALLET_DESK_COMPLIANCE.md) — matrice de compliance ouverte en `0.2.6-pre.013`, étendue aux wires V1/V2, à la migration explicite et au runtime Tauri packagé ; `pre.018` y matérialise la candidate finale, le dernier build opérateur restant à enregistrer avant `rel.001`.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/validation/009-V0_2_6_WALLET_DESK_COMPLIANCE.md -->
|
||||
<!-- version: 5 -->
|
||||
<!-- version: 6 -->
|
||||
|
||||
# Validation `0.2.6` — Wallet Desk integration / compliance / security
|
||||
|
||||
@@ -52,7 +52,7 @@ Le checkpoint opérateur `pre.012` du 21 août 2026 est vert : `cargo fmt`, audi
|
||||
## 3. Matrice de frontières
|
||||
|
||||
| Frontière | Contrat `0.2.6` | Preuve durable |
|
||||
|---|---|---|
|
||||
|------------------------------|--------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------|
|
||||
| Frontend -> Config | aucun accès direct env/.env | `release_compliance::backend_keeps_config_wallet_transport_and_logging_ownership_boundaries` + ownership Config |
|
||||
| Frontend -> Wallet secret | passwords uniquement request-only ; jamais de keypair en IPC | `desktop_security` + `release_compliance::response_dtos_keep_secret_key_material_out_of_ipc` |
|
||||
| Frontend -> filesystem | aucun path arbitraire ; picker natif Rust | tests import/export + capability audit |
|
||||
@@ -238,9 +238,9 @@ cargo test --workspace : BLOCKED par un faux positif du canari Logging global
|
||||
|
||||
Le blocage workspace est limité à `ksp-logging-lib/tests/ownership.rs::workspace_crates_do_not_bypass_ksp_logging_facade` : le scanner voit le littéral `tracing::` dans `release_compliance.rs`, alors que ce littéral est la chaîne recherchée par le canari et non un usage de `tracing`. `pre.013-fix.002` supprime ce littéral statique en construisant le motif à l'exécution, sans modifier le scanner global ni relâcher la politique Logging.
|
||||
|
||||
La matrice reste ouverte jusqu'à `pre.018`, après les tranches V2 `pre.015`–`pre.017`; `pre.018` ajoute le parcours fonctionnel final, README/USAGE et le build Tauri exécuté en toute dernière opération.
|
||||
La matrice reste ouverte jusqu'à la preuve opérateur finale de `pre.018`. Les tranches V2 `pre.015`–`pre.017` sont acquises ; `pre.018` matérialise README/USAGE, runtime packagé et candidate, puis le build Tauri est exécuté en toute dernière opération.
|
||||
|
||||
## 11. Intercalation V2 à partir de `pre.015`
|
||||
## 12. Intercalation V2 à partir de `pre.015`
|
||||
|
||||
`pre.015` ne modifie pas encore Wallet Desk : il ajoute dans `ksp-wallet-lib` un codec binaire V2 strict et une fixture canonique. Les gates Desk `pre.013`/`pre.014` restent donc applicables sans changement.
|
||||
|
||||
@@ -256,3 +256,66 @@ V2 nouvellement créé/importé administration complète conservée en V2
|
||||
```
|
||||
|
||||
Les canaris `pre.016` couvrent également le refus croisé des lecteurs explicitement versionnés et le maintien du format V2 après metadata/rotations/disable/recreate/self-rotation VIEW. `pre.017` matérialise ensuite la migration V1 -> V2 explicite : mémoire, copie no-clobber et remplacement in-place stale-protected, avec conservation identité/metadata/note IDs et canaris tampering/state-conflict. Wallet Desk reste non-migrant et continue à ouvrir/inspecter via les APIs génériques.
|
||||
|
||||
## 13. Checkpoint opérateur `pre.017`
|
||||
|
||||
Le checkpoint communiqué le 22 août 2026 ferme les tranches fonctionnelles V2 avant candidate :
|
||||
|
||||
```text
|
||||
cargo fmt --all PASS
|
||||
audit Rust workspace PASS
|
||||
cargo check --workspace PASS
|
||||
cargo clippy --workspace --all-targets PASS, sans warning
|
||||
cargo test --workspace PASS
|
||||
ksp-wallet-lib 84 passés / 1 benchmark ignored
|
||||
Wallet Desk desktop_contract 17/17
|
||||
Wallet Desk desktop_security 12/12
|
||||
Wallet Desk release_compliance 4/4
|
||||
```
|
||||
|
||||
Le parcours Tauri crate-local avec un `KSP_WALLETS_DIRECTORY` isolé confirme ensuite : création du répertoire effectif Config-owned, import Solana CLI JSON, création native `format_version=2`, ouverture d'une session OWNER, inventory du nouveau wallet et `getBalance` Devnet réussi avec un slot non nul. Aucun build production n'est exécuté à ce checkpoint.
|
||||
|
||||
## 14. Candidate `pre.018` — runtime packagé et resources
|
||||
|
||||
`pre.018` ferme le point CWD/resources laissé ouvert par `pre.014-fix.001`. Le runtime release ne dépend plus du checkout source :
|
||||
|
||||
```text
|
||||
Tauri bundle.resources
|
||||
-> documents Config enregistrés + JSON Schemas uniquement
|
||||
-> aucun .env / logs / wallets / secret
|
||||
|
||||
Tauri resource_dir
|
||||
-> ksp-config-lib::prepare_packaged_runtime
|
||||
-> racine KSP user-writable résolue par ProjectDirs
|
||||
-> Config documents seed-only-if-absent
|
||||
-> Schemas package-owned synchronisés à chaque lancement
|
||||
-> set_current_dir(runtime_root) avant AppState::initialize
|
||||
```
|
||||
|
||||
Config Desk et Wallet Desk embarquent le même ensemble de huit resources du registre courant. Le guest frontend ne reçoit aucune permission filesystem supplémentaire. Les Config déjà modifiées restent inchangées lors d'un nouveau lancement packagé ; `.env` n'est jamais embarqué ni seedé. Les chemins relatifs Logging/Wallet restent ancrés sur la racine runtime writable sauf override explicite par Config.
|
||||
|
||||
Les canaris déterministes de la candidate couvrent le seed Config non destructif, la synchronisation des schemas, le rejet d'une destination symlink sur Unix, l'exposition publique du layout packagé, les mappings Tauri des deux Desks et l'absence de ressources mutables/secrètes dans le bundle Wallet Desk.
|
||||
|
||||
## 15. Gate final opérateur `pre.018`
|
||||
|
||||
Le delta candidate doit être validé dans cet ordre :
|
||||
|
||||
```bash
|
||||
cargo fmt --all
|
||||
python3 scripts/audit_rust_workspace_rules.py
|
||||
cargo check --workspace
|
||||
cargo clippy --workspace --all-targets
|
||||
cargo test -p ksp-config-lib
|
||||
cargo test -p ksp-wallet-lib
|
||||
cargo test -p ksp-app-config-desk
|
||||
cargo test -p ksp-app-wallet-desk
|
||||
cargo test --workspace
|
||||
```
|
||||
|
||||
Le parcours fonctionnel `cargo tauri dev`, les éventuels smokes live et tout audit complémentaire retenu sont exécutés **avant** le build. La dernière opération de validation reste :
|
||||
|
||||
```bash
|
||||
(cd crates/ksp-app-wallet-desk && cargo tauri build)
|
||||
```
|
||||
|
||||
`pre.018` ne préjuge pas du résultat de ce build : la preuve opérateur sera enregistrée avant `rel.001`. `CHANGELOG.md` reste volontairement inchangé pendant la prerelease ; l'entrée stable `0.2.6` appartient à la clôture `rel.001` après gate final vert.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: prompts/011-V0_2_6_START_PROMPT.md -->
|
||||
<!-- version: 7 -->
|
||||
<!-- version: 8 -->
|
||||
|
||||
# Prompt de démarrage `0.2.6` — Wallet Desk
|
||||
|
||||
@@ -728,3 +728,11 @@ Après validation du polish `pre.014-fix.001`, la release est volontairement ét
|
||||
`pre.016` matérialise la politique figée en `pre.015` : `DEFAULT_WALLET_FORMAT = V2`, APIs génériques de création/import en V2, lecture/inspection V1/V2 auto-détectée, variantes `_v1`/`_v2` strictes et Wallet Desk consommant uniquement la façade non versionnée. Les handles OWNER/VIEW persistent leurs mutations dans le format natif ouvert ; aucune conversion implicite V1 -> V2 n'est effectuée.
|
||||
|
||||
`pre.017` matérialise la migration explicite OWNER-authentifiée V1 -> V2 : conversion mémoire, copie filesystem no-clobber, remplacement in-place stale-protected, conservation identité/metadata/note IDs, cible VIEW explicite et canaris tampering/state-conflict. Wallet Desk reste version-neutral et aucune ouverture ne migre implicitement. La prochaine tranche est `pre.018`, dédiée à la documentation/candidate finale, au packaging/CWD et au build Tauri en toute dernière opération.
|
||||
|
||||
## Addendum `pre.018` — candidate finale et runtime packagé
|
||||
|
||||
Le checkpoint opérateur `pre.017` est entièrement vert : fmt/audit/check/clippy/workspace tests passent, les canaris migration V1 -> V2 sont verts et le parcours Tauri confirme qu'un import Wallet Desk crée réellement un V2 puis exécute `getBalance` sur Devnet.
|
||||
|
||||
`pre.018` matérialise la candidate finale : README/USAGE Wallet Desk, compliance/documentation synchronisée, versions applicatives alignées, et fermeture du comportement distribué. Les deux Desks embarquent les huit documents Config/schemas enregistrés comme resources Tauri ; en release, `ksp-config-lib` prépare une racine KSP user-writable commune via `ProjectDirs`, seed les Config uniquement lorsqu'elles sont absentes, resynchronise les schemas, n'embarque jamais `.env`, puis le runtime Tauri active cette racine comme CWD avant `AppState::initialize`.
|
||||
|
||||
Après application de `pre.018`, toutes les validations et parcours fonctionnels sont exécutés avant `(cd crates/ksp-app-wallet-desk && cargo tauri build)`, qui reste l'absolue dernière opération. Si ce gate est vert, la seule étape suivante est `0.2.6-rel.001` avec version stable `0.2.6`, mise à jour du `CHANGELOG.md` et tag `v0.2.6`; aucune nouvelle fonctionnalité n'est introduite en `rel.001`.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: prompts/012-V0_2_7_START_PROMPT.md -->
|
||||
<!-- version: 2 -->
|
||||
<!-- version: 3 -->
|
||||
|
||||
# Prompt de démarrage `0.2.7` — WebSocket Solana standard
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
Base attendue : tag stable `v0.2.6`.
|
||||
|
||||
`0.2.6` a validé Wallet Desk puis intégré le wire binaire `.kspwallet` V2, les APIs Wallet génériques/versionnées, la compatibilité V1/V2 et les migrations explicites. `0.2.7` revient donc à la séquence Transport prévue avant l'intercalation temporaire du chantier binaire.
|
||||
`0.2.6` a validé Wallet Desk puis intégré le wire binaire `.kspwallet` V2, les APIs Wallet génériques/versionnées, la compatibilité V1/V2, les migrations explicites et le runtime packagé commun des Desks sans dépendance au checkout source. `0.2.7` revient donc à la séquence Transport prévue avant l'intercalation temporaire du chantier binaire.
|
||||
|
||||
La release à ouvrir est :
|
||||
|
||||
|
||||
Reference in New Issue
Block a user