diff --git a/docs/plans/005-V0_1_3_CONFIG_FOUNDATION_PLAN.md b/docs/plans/005-V0_1_3_CONFIG_FOUNDATION_PLAN.md new file mode 100644 index 0000000..2ca2699 --- /dev/null +++ b/docs/plans/005-V0_1_3_CONFIG_FOUNDATION_PLAN.md @@ -0,0 +1,1246 @@ + + + +# Plan `0.1.3` — Configuration foundation + +## 1. Statut et objectif + +Ce plan est établi par `0.1.3-pre.001`, tranche obligatoire de brainstorming, audit et planification. + +La base auditée est la release stable `v0.1.2`. La release `0.1.3` introduira `ksp-config-lib` comme frontière KSP unique pour : + +- les documents de configuration spécialisés ; +- les compositions propres aux exécutables/applications ; +- la sélection et la résolution des profils ; +- la lecture des variables d'environnement KSP/KSPB et d'un fichier d'environnement géré par Config ; +- la validation syntaxique, structurelle, sémantique et effective ; +- la classification public/interne/secret ; +- les lectures runtime et management explicitement distinctes ; +- les mutations et persistences explicitement autorisées ; +- la construction de settings runtime appartenant à d'autres composants, en commençant par `ksp_logging_lib::LoggingSettings`. + +`0.1.3-pre.001` ne crée pas encore `ksp-config-lib`, n'ajoute aucune dépendance fonctionnelle et ne crée aucun fichier runtime Config. Il fixe d'abord les contrats à implémenter. + +## 2. Audit de la base stable `0.1.2` + +Le workspace stable contient actuellement : + +```text +crates/ksp-core-lib +crates/ksp-logging-lib +``` + +Le manifest racine utilise : + +```text +workspace.package.version = "0.1.2" +edition = "2024" +``` + +Aucune crate Config n'existe encore et aucun répertoire `config/` n'est présent dans la base stable fournie. + +### 2.1 Core disponible + +`ksp-core-lib` fournit déjà les contrats nécessaires à Config : + +```text +ksp_core_lib::Error +ksp_core_lib::ErrorCode +ksp_core_lib::ErrorContext +ksp_core_lib::Result +``` + +Les erreurs propres à Config resteront déclarées dans `ksp-config-lib` avec le domaine `config`. Core ne recevra aucune connaissance métier Config. + +### 2.2 Logging disponible + +`ksp-logging-lib` possède déjà : + +```text +LoggingSettings +LogFilterLevel +SpanEvents +ConsoleSettings +ConsoleOutput +FileSettings +FileRotation +TargetFilter +LoggingGuard +initialize(...) +reinitialize(...) +``` + +Config n'a donc aucune raison de redéfinir ces contrats. Il doit uniquement produire un `LoggingSettings` valide à partir de sa propre configuration effective. + +La direction reste : + +```text +ksp-config-lib -> ksp-core-lib +ksp-config-lib -> ksp-logging-lib +ksp-logging-lib -X-> ksp-config-lib +ksp-core-lib -X-> ksp-config-lib +``` + +### 2.3 Règles déjà fixées + +Les règles actuelles imposent notamment : + +- Rust 2024 ; +- `unsafe_code = "forbid"` ; +- pas de `unwrap`, `expect`, `panic` ou `?` dans le code production ; +- façade publique au crate-root, modules privés, pas de `mod.rs` ; +- tests unitaires sous `unit_tests/` autant que possible et tests d'intégration sous `tests/` ; +- toute dépendance externe centralisée dans `[workspace.dependencies]` ; +- aucune dépendance ajoutée avant un usage réel ; +- `ksp-logging-lib` reste l'unique propriétaire direct de `tracing`, `tracing-subscriber` et `tracing-appender`. + +## 3. Réaudit de la référence historique bot3 + +L'archive `khadhroony-bot3` fournie contient une ancienne `ks-config` et les documents : + +```text +logging.config.json +transport.config.json +listeners.config.json +store.config.json +wallet.config.json +execution.config.json +kb-app-demo-desktop.default.config.json +``` + +Cette base est une référence historique, pas une source de vérité KSP. + +### 3.1 Principes conservés + +Les éléments suivants restent pertinents : + +- documents spécialisés indépendants ; +- valeurs globales hors profils ; +- `default_profile` autonome dans chaque document spécialisé ; +- composition propre à un exécutable ; +- override de profil spécialisé depuis une composition ; +- schémas sous un répertoire dédié ; +- distinction source/runtime/public/diagnostic ; +- classification nominale des variables d'environnement ; +- propagation de la sensibilité la plus forte pour une valeur dérivée ; +- absence de TS-RS dans la bibliothèque Config lorsque le contrat est purement backend ; +- l'application Tauri possède ses DTO et ne sérialise pas aveuglément une configuration runtime complète. + +### 3.2 Éléments explicitement non repris + +KSP ne reprend pas aveuglément : + +- le document runtime monolithique `AppConfig/ProfileConfig` historique ; +- les documents transport/listeners/store/wallet/execution avant l'existence des composants correspondants ; +- une section `application` opaque uniquement pour conserver un ancien runtime ; +- l'interpolation générique de chaînes `${NAME}` / `${NAME:-fallback}` dans n'importe quelle valeur JSON ; +- le chargement d'un fichier `.env` en modifiant globalement l'environnement du processus ; +- un modèle où une valeur effective secrète devient sérialisable puis est masquée après coup. + +La résolution d'environnement KSP sera au contraire fondée sur des bindings explicites entre une clé Config connue et un nom de variable d'environnement connu. + +## 4. Décision de périmètre : `0.1.3` reste une seule release + +Le périmètre peut rester dans `0.1.3` à condition de le borner strictement. + +La première surface fonctionnelle contiendra : + +1. l'infrastructure générique document/profil/composition ; +2. le seul document spécialisé actuellement justifié : Logging ; +3. l'environnement Config KSP/KSPB avec un fichier d'environnement géré ; +4. la classification et les surfaces runtime/diagnostic/management ; +5. la mutation/persistence du document Logging et du fichier d'environnement géré ; +6. l'adapter `ResolvedLoggingConfig -> ksp_logging_lib::LoggingSettings` ; +7. les tests/audits de propriété nécessaires. + +Ne sont pas créés dans `0.1.3` : Transport, Wallet, Store, Execution ou une configuration applicative générale fictive. + +Cette limitation évite d'insérer une release supplémentaire avant `0.1.4`. La séquence reste donc : + +```text +0.1.3 ksp-config-lib +0.1.4 ksp-app-config-desk +``` + +Si une tranche de mutation révèle une contrainte technique majeure non bornable, le plan pourra encore être corrigé par un delta ultérieur sans comprimer artificiellement la release. + +## 5. Matrice de responsabilités + +| Responsabilité | Propriétaire | Consommateurs | Interdit | +|------------------------------------------|---------------------------|------------------------------------------------|-----------------------------------------------------------------| +| Lire les documents Config runtime | `ksp-config-lib` | apps/crates via API Config | lecture directe par les consumers | +| Lire l'environnement applicatif KSP/KSPB | `ksp-config-lib` | apps/crates via contrats Config | `std::env::*` applicatif hors Config | +| Résoudre composition/profils/overrides | `ksp-config-lib` | orchestration | résolution locale dans chaque binaire | +| Valider documents et valeurs effectives | `ksp-config-lib` | orchestration/management | validation divergente par consumer | +| Classer public/interne/secret | `ksp-config-lib` | runtime/management/app adapters | déduction ad hoc dans l'UI | +| Modifier/sauvegarder Config | `ksp-config-lib` | management explicite | écriture directe par l'application | +| Authentifier/autoriser un utilisateur UI | application/orchestration | `ksp-config-lib` reçoit une capacité explicite | prétendre que Config est un sandbox de sécurité intra-processus | +| Posséder `LoggingSettings` | `ksp-logging-lib` | Config construit ces settings | copie des types Logging dans Config | +| Posséder `LoggingGuard` | exécutable/application | lifecycle Logging | singleton Config global | +| Initialiser/recharger Logging | orchestration | Config fournit settings/changement | Logging lisant Config | +| DTO/bindings Tauri | application Tauri | frontend | TS-RS automatique dans Config | + +## 6. Arborescence et nomenclature retenues + +### 6.1 Racine Config et fichiers runtime réels + +Les documents runtime réels appartiennent sous une racine Config explicite. Le layout workspace par défaut est : + +```text +config/ +``` + +`ksp-config-lib` ne doit pas dépendre implicitement du current working directory pour ses opérations internes : une session/locator Config possède un `ConfigRoot`. Un helper workspace pourra construire `ConfigRoot = ./config`, tandis que les tests et applications installées peuvent fournir une autre racine explicite. + +Règles de chemin : + +- les documents gérés appartiennent sous `ConfigRoot` ; +- les chemins relatifs d'une composition sont résolus depuis le répertoire du composite puis normalisés ; +- après résolution, une source gérée ne peut pas sortir de `ConfigRoot` par `..` ou équivalent ; +- un chemin absolu n'est pas accepté dans un document composite ; +- `KSP_ENV_FILE` relatif est résolu depuis `ConfigRoot` et doit rester dans cette racine ; +- les APIs de test/management choisissent une autre racine plutôt que d'introduire une écriture arbitraire hors frontière Config. + +Pour la première surface : + +```text +config/logging.config.json +config/environment.env +``` + +`environment.env` est un fichier runtime local géré par Config et destiné notamment aux valeurs non versionnées/secrètes. Il devra être ignoré par Git lorsqu'il sera créé. Son absence est autorisée tant qu'aucune valeur requise n'en dépend. + +Ce fichier n'est **pas** défini comme un fichier dotenv générique. Il utilise une grammaire KSP versionnée et volontairement bornée afin d'éviter toute interpolation ou exécution implicite. La première ligne sémantique est l’en-tête de format réservé : + +```text +# ksp-env-format: 1 +``` + +Le parser reconnaît cet en-tête avant le traitement des commentaires ordinaires ; il doit être présent exactement une fois et annoncer une version supportée. + +Grammaire initiale : + +- UTF-8 uniquement ; +- lignes vides autorisées ; +- après l’en-tête de format, commentaire uniquement lorsque le premier caractère non blanc est `#` ; +- affectation `NAME=VALUE` ; +- `NAME` respecte `[A-Z_][A-Z0-9_]*` puis doit appartenir à `KSP_*` ou `KSPB_*` ; +- une valeur non quotée est littérale après trim externe ; +- une valeur nécessitant espaces de bord, newline, guillemets ou escapes utilise une chaîne JSON entre doubles guillemets ; `serde_json` fournit alors l'encodage/décodage de la chaîne ; +- aucune expansion `$VAR`/`${VAR}`, aucune substitution shell, aucun `export`, aucune commande et aucune continuation implicite ; +- `#` après `=` appartient à la valeur, ce n'est pas un commentaire inline ; +- les doublons de nom sont refusés au lieu d'appliquer une règle first/last-wins ; +- les écritures Config produisent une forme canonique avec header de version, noms triés et valeurs encodées comme chaînes JSON. + +Les commentaires libres d'un fichier édité manuellement ne font pas partie du contrat de conservation lors d'une sauvegarde management : la conservation est sémantique, pas byte-for-byte. + +### 6.2 Schémas + +```text +config/schemas/logging.config.schema.json +config/schemas/composition.config.schema.json +``` + +Les schémas utilisent JSON Schema draft 2020-12. Ils sont auto-contenus dans la première surface : pas de résolution HTTP et pas de `$ref` externe nécessaire au runtime. + +### 6.3 Exemples + +```text +config/examples/example.logging.config.json +config/examples/example.composition.config.json +config/examples/example.environment.env +``` + +Aucun secret réel ne figure dans les exemples. + +### 6.4 Compositions runtime + +Une composition concrète appartient au binaire/application qui la consomme et suit : + +```text +config/.default.config.json +``` + +Aucune composition runtime concrète n'est créée dans `0.1.3`, car aucun exécutable nécessitant Config n'existe encore dans le workspace. La première composition réelle est prévue avec `ksp-app-config-desk` en `0.1.4`. + +Le contrat générique de composition est néanmoins implémenté et testé dans `0.1.3` via schéma, exemples et fixtures de tests. + +## 7. Contrat commun des documents JSON + +Chaque document Config JSON KSP commence par : + +```json +{ + "format_version": 1 +} +``` + +`format_version` est obligatoire et permet à Config de refuser explicitement une génération qu'il ne sait pas lire ou réécrire. + +Politique retenue : + +- le format initial est `1` ; +- une version inconnue est refusée, jamais réécrite silencieusement ; +- `additionalProperties: false` est utilisé lorsque le contrat est fermé ; +- les documents JSON source peuvent dériver `Serialize`/`Deserialize` parce que la persistence fait partie de leur contrat ; +- une projection runtime résolue susceptible de contenir des secrets ne dérive pas automatiquement `Serialize` ou `Debug` ; +- la sauvegarde JSON produit un format canonique lisible avec newline final ; +- la conservation byte-for-byte des espaces n'est pas un contrat ; la conservation sémantique de la version et des champs l'est. + +## 8. Premier document spécialisé : Logging + +`logging.config.json` reste le seul document spécialisé créé dans la première surface car Logging est le seul composant runtime déjà développé qui nécessite une configuration. + +Structure conceptuelle retenue : + +```json +{ + "format_version": 1, + "logs_directory": "logs", + "default_profile": "default", + "profiles": [ + { + "name": "default", + "default_filter": "info", + "span_events": "new_and_close", + "console": { + "output": "stdout" + }, + "file": { + "file_name_prefix": "ksp", + "rotation": "daily" + }, + "target_filters": [] + } + ] +} +``` + +Décisions : + +- `logs_directory` est global, hors profils ; +- `default_profile` est global, autonome, et doit référencer un profil existant ; +- les noms de profils sont uniques ; +- `file` ne duplique pas `logs_directory` ; Config construit le `FileSettings.directory` final à partir de la racine globale ; +- `console: null` ou absence de console signifie sortie console désactivée ; +- `file: null` ou absence de file signifie sortie fichier désactivée ; +- `target_filters` conserve l'ordre du document ; +- les targets restent des prefixes KSP acceptés par `LoggingSettings::validate()` ; +- Config valide sa propre syntaxe/document puis appelle aussi la validation publique Logging après conversion. + +## 9. Contrat de composition générique + +Le format de composition n'énumère pas tous les futurs domaines dans une structure rigide. Il référence des identifiants de documents enregistrés par Config. + +Structure conceptuelle : + +```json +{ + "format_version": 1, + "owner_namespace": "ksp", + "default_profile": "default", + "documents": { + "logging": "logging.config.json" + }, + "profiles": [ + { + "name": "default", + "document_profiles": {} + }, + { + "name": "diagnostic", + "document_profiles": { + "logging": "diagnostic" + } + } + ] +} +``` + +Décisions : + +- `owner_namespace` est obligatoire et vaut `ksp` ou `kspb` ; il détermine le selector de profil de composition autorisé sans modifier la propriété des documents spécialisés inclus ; +- `default_profile` du composite sélectionne un profil de composition par défaut ; le nom `active_profile` n'est pas conservé dans le fichier source, car « actif » est un état runtime ; +- `documents` associe un identifiant de document KSP connu à un chemin ; +- un chemin relatif est résolu relativement au répertoire contenant la composition puis doit rester sous `ConfigRoot` ; +- les chemins absolus et les traversées hors `ConfigRoot` sont refusés ; +- un document référencé est lu uniquement par Config ; +- `document_profiles` peut remplacer le profil d'un document spécialisé ; +- l'absence d'override utilise le `default_profile` propre au document spécialisé ; +- un override vers un profil inexistant est une erreur avant construction de la configuration effective ; +- une clé d'override absente de `documents` est une erreur ; +- un identifiant de document inconnu de la version courante de Config est refusé au lieu d'être ignoré ; +- la composition n'offre pas de generic JSON patch de champs spécialisés : les valeurs restent possédées par leur document spécialisé. + +Dans `0.1.3`, le seul identifiant de document enregistré est : + +```text +logging +``` + +## 10. Sélection des profils + +Trois niveaux sont distingués : + +1. profil de composition ; +2. profil d'un document spécialisé ; +3. overrides de valeurs par environnement. + +### 10.1 Sans composition + +Pour un document spécialisé chargé directement : + +```text +profil explicitement demandé par API + sinon document.default_profile +``` + +### 10.2 Avec composition + +Pour la composition : + +```text +profil de composition explicitement demandé par API + sinon selector d'environnement possédé par le namespace du composite + sinon composition.default_profile +``` + +Selectors réservés : + +```text +KSP_CONFIG_PROFILE -> composition KSP +KSPB_CONFIG_PROFILE -> future composition KSPB +``` + +`KSPB_CONFIG_PROFILE` est fixé par le contrat mais n'a aucun consumer réel en `0.1.3`. Config n'applique jamais simultanément les deux selectors : `composition.owner_namespace` choisit lequel est admissible. + +Pour ce selector, la precedence est : + +```text +profil explicite API +> variable du processus +> entrée environment.env +> composition.default_profile +``` + +Puis, pour chaque document : + +```text +composition.profile.document_profiles[document] + sinon document.default_profile +``` + +Le selector d'environnement du profil de composition est un contrôle de sélection, distinct des overrides de valeurs décrits plus bas. + +## 11. Résolution complète et provenance + +L'ordre normatif retenu est : + +```text +1. locator Config / composition +2. lecture + parsing du document source +3. validation schema/source +4. résolution de la composition +5. sélection du profil de composition +6. sélection du profil de chaque document +7. fusion globals + profil spécialisé +8. application des bindings d'environnement explicites +9. validation de la valeur effective +10. conversion vers le contrat runtime propriétaire +``` + +Pour une clé effective, Config doit pouvoir conserver une provenance bornée : + +```text +DocumentGlobal +DocumentProfile { profile } +EnvironmentFile { variable } +ProcessEnvironment { variable } +``` + +Cette provenance sert aux diagnostics et au management, sans inclure la valeur secrète elle-même. + +## 12. Environnement KSP/KSPB + +### 12.1 Namespaces + +Les seuls namespaces applicatifs reconnus sont : + +```text +KSP_SECRET_* +KSP_PUBLIC_* +KSP_* + +KSPB_SECRET_* +KSPB_PUBLIC_* +KSPB_* +``` + +Ordre de sensibilité, du plus fort au plus faible : + +```text +Secret > Internal > Public +``` + +L'ordre de reconnaissance nominale reste `*_SECRET_*`, puis `*_PUBLIC_*`, puis le namespace général `KSP_*`/`KSPB_*`, afin que le préfixe général ne masque jamais les deux classes explicites. + +Concrètement : + +- `KSP_SECRET_*` / `KSPB_SECRET_*` -> `Secret` ; +- `KSP_PUBLIC_*` / `KSPB_PUBLIC_*` -> `Public` ; +- autre `KSP_*` / `KSPB_*` -> `Internal` ; +- un nom hors namespace n'est pas une variable applicative gérée par Config. + +### 12.2 Pas de mapping automatique par transformation de nom + +Config n'infère pas qu'une clé JSON quelconque possède automatiquement un override à partir de son chemin. + +Chaque override est un binding déclaré explicitement par Config : + +```text +document + clé effective + variable + type + sensibilité +``` + +Un binding dont le namespace nominal contredit la sensibilité du champ est refusé dans les tests/audits de la crate. + +Cette politique évite : + +- les collisions de noms ; +- les overrides accidentels ; +- les conversions de type implicites ; +- la fuite d'un champ secret sous un préfixe public ; +- l'interpolation arbitraire de secrets dans des chaînes opaques. + +### 12.3 Sources d'environnement et precedence + +Config construit une vue d'environnement sans modifier l'environnement du processus : + +```text +valeur du processus + > valeur de config/environment.env + > valeur du document/profil +``` + +Config ne parcourt pas globalement toutes les variables du processus. Il lit uniquement les noms exacts enregistrés dans ses descriptors/bindings ainsi que ses variables bootstrap. Une valeur process non UTF-8 pour un binding attendu est une erreur d'override sans inclure la valeur dans le diagnostic. + +Le fichier par défaut peut être remplacé par le bootstrap interne : + +```text +KSP_ENV_FILE +``` + +Ordre de sélection du fichier : + +```text +chemin explicite fourni à l'API Config +> KSP_ENV_FILE du processus +> config/environment.env +``` + +`KSP_ENV_FILE` est `Internal`, est lu par Config uniquement depuis l'environnement du processus et n'est pas lu depuis le fichier qu'il sert précisément à sélectionner. Sa valeur relative est résolue depuis `ConfigRoot`; une valeur qui sortirait de cette racine est refusée. + +Le fichier géré n'est pas un stockage arbitraire de noms. Une mutation publique de management ne peut créer/modifier/supprimer qu'une variable enregistrée par un descriptor Config et marquée writable. Un nom namespacé mais inconnu dans `environment.env` est refusé comme erreur de document au lieu d'être silencieusement ignoré. + +### 12.4 Variables de contrôle Config + +Les variables de contrôle initiales sont distinctes des bindings de valeurs Logging : + +| Variable | Classe | Source admise | Writable par Management | Rôle | +|-----------------------|----------|------------------------------|-----------------------------------------------|-------------------------------------------------------------------| +| `KSP_ENV_FILE` | Internal | process uniquement | non | sélectionner le fichier d'environnement géré | +| `KSP_CONFIG_PROFILE` | Internal | process ou `environment.env` | oui | sélectionner le profil d'une composition `owner_namespace = ksp` | +| `KSPB_CONFIG_PROFILE` | Internal | process ou `environment.env` | oui, lorsque le support KSPB devient consommé | sélectionner le profil d'une composition `owner_namespace = kspb` | + +Ces noms sont des descriptors Config enregistrés, pas des exceptions lues ad hoc par les applications. + +### 12.5 Rust 2024 et écriture de l'environnement + +En Rust 2024, `std::env::set_var` et `std::env::remove_var` sont `unsafe`. KSP interdit `unsafe`. + +Décision : + +- `ksp-config-lib` peut lire l'environnement du processus ; +- `ksp-config-lib` ne modifie jamais l'environnement global du processus ; +- la surface de management peut modifier atomiquement `config/environment.env` ou un fichier explicitement sélectionné ; +- une variable du processus continue à dominer la valeur persistée et peut donc empêcher une mutation de devenir effective immédiatement ; +- le résultat de mutation doit le signaler explicitement. + +### 12.6 Bindings Logging initiaux + +Première allowlist candidate : + +| Clé effective | Variable | Classe | Type | +|---------------------------------|--------------------------------|----------|----------------------| +| `logging.logs_directory` | `KSP_LOGS_DIRECTORY` | internal | path/string non vide | +| `logging.default_filter` | `KSP_LOGGING_DEFAULT_FILTER` | internal | enum log level | +| `logging.span_events` | `KSP_LOGGING_SPAN_EVENTS` | internal | enum span events | +| `logging.console` | `KSP_LOGGING_CONSOLE` | internal | `off |stdout|stderr` | +| `logging.file.enabled` | `KSP_LOGGING_FILE_ENABLED` | internal | bool | +| `logging.file.file_name_prefix` | `KSP_LOGGING_FILE_NAME_PREFIX` | internal | string non vide | +| `logging.file.rotation` | `KSP_LOGGING_FILE_ROTATION` | internal | `never |hourly|daily` | + +`target_filters` n'a pas d'override d'environnement initial : une liste structurée reste dans le document Logging tant qu'un besoin concret ne justifie pas un encodage dédié. + +Les noms exacts de cette table sont fixés par le plan et pourront uniquement être corrigés avant implémentation par un delta explicite. + +## 13. Sensibilité et surfaces d'accès + +### 13.1 Trois classes + +```text +Public +Internal +Secret +``` + +Une valeur dérivée de plusieurs inputs prend la sensibilité la plus forte. + +### 13.2 Runtime + +Un consumer runtime obtient uniquement les contrats nécessaires à sa responsabilité. + +Il ne reçoit pas automatiquement : + +- le document source complet ; +- toutes les variables d'environnement ; +- une map générique de secrets ; +- une projection sérialisable de toute la configuration effective. + +Pour Logging, le consumer final reçoit un `ksp_logging_lib::LoggingSettings`. + +Un futur composant ayant besoin d'un secret recevra une surface typed Config explicitement ajoutée avec ce composant ; `0.1.3` ne crée pas un getter arbitraire `get_secret(name)` pour anticiper des besoins inconnus. + +### 13.3 Diagnostic ordinaire + +Par défaut : + +- `Public` : valeur exposable si la projection demandée la prévoit ; +- `Internal` : utilisable par les contrats runtime typed et par Management, mais sa valeur n'appartient pas à une projection publique générique ; un diagnostic explicitement demandé en mode debug peut l'exposer uniquement lorsque le consumer autorise cette surface ; +- `Secret` : jamais la valeur dans les diagnostics, uniquement état `configured/missing` et provenance non sensible. + +La surface diagnostic sûre est le défaut. Le mode debug ne transforme jamais un `Secret` en valeur diagnostic et n'est pas un substitut à `ConfigManagement`. + +Les erreurs Config ne doivent pas incorporer une valeur secrète rejetée dans leur message ou contexte. + +### 13.4 Management privilégié + +Une application de management doit pouvoir consulter et modifier un secret réel. + +La bibliothèque exposera donc une surface distincte de management, conceptuellement : + +```text +ConfigRuntime +ConfigDiagnostics +ConfigManagement +``` + +`ConfigManagement` peut : + +- lire la valeur source réelle d'un champ secret ; +- lire la valeur réelle d'une entrée d'environnement gérée ; +- modifier/supprimer une valeur autorisée ; +- sauvegarder un document connu ; +- demander une nouvelle résolution effective. + +Cette séparation est une barrière d'API contre les fuites accidentelles, pas un sandbox contre du code Rust hostile dans le même processus. L'authentification et l'autorisation de l'utilisateur final appartiennent à l'application/orchestration. L'application devra fournir explicitement l'autorisation/capacité attendue par la surface management au lieu d'utiliser les APIs runtime ordinaires. + +### 13.5 Frontière Tauri future + +`ksp-config-lib` ne dépend pas de Tauri ni de TS-RS. + +En `0.1.4`, `ksp-app-config-desk` possédera : + +- les commandes Tauri ; +- les DTO publics ; +- les DTO diagnostics ; +- les DTO/commandes privilégiés permettant, après autorisation applicative, d'afficher ou modifier des secrets. + +L'application ne lira jamais directement JSON ou environnement pour implémenter ces commandes. + +## 14. Validation + +La validation est volontairement multi-étapes. + +### 14.1 Document source + +```text +filesystem/path +-> UTF-8 +-> JSON syntax +-> format_version +-> JSON Schema +-> typed deserialize +-> invariants sémantiques du document +``` + +### 14.2 Composition + +Vérifier notamment : + +- `default_profile` existe ; +- noms de profils uniques ; +- identifiants de documents connus ; +- sources lisibles ; +- override de profil vers un profil existant ; +- aucune clé `document_profiles` sans source correspondante. + +### 14.3 Environnement + +Pour chaque binding appliqué : + +- nom exact ; +- source utilisée ; +- type parsable ; +- enum/bornes valides ; +- sensibilité cohérente ; +- aucune valeur secrète dans le diagnostic d'échec. + +### 14.4 Configuration effective + +Après overrides : + +- invariants Config ; +- invariants propres au document ; +- conversion vers le contrat du composant ; +- validation publique du composant propriétaire lorsque disponible. + +Pour Logging : + +```text +ResolvedLoggingConfig +-> LoggingSettings +-> LoggingSettings::validate() +``` + +## 15. Erreurs Config + +Les codes sont possédés par `ksp-config-lib` sous le domaine : + +```text +config +``` + +Candidats fixés pour être introduits uniquement lorsqu'ils deviennent nécessaires : + +```text +config.document_not_found +config.document_read_failed +config.invalid_utf8 +config.invalid_json +config.unsupported_format_version +config.schema_validation_failed +config.invalid_document +config.profile_not_found +config.unknown_document_kind +config.invalid_composition +config.path_outside_root +config.symlink_write_denied +config.invalid_environment_name +config.invalid_environment_file +config.unsupported_environment_format +config.duplicate_environment_variable +config.unknown_environment_variable +config.invalid_environment_override +config.invalid_effective_value +config.management_access_denied +config.persistence_failed +``` + +Les erreurs externes utiles sont conservées avec `Error::with_source(...)` lorsque possible. + +Contexte sûr candidat : + +```text +path +document_kind +profile +field +variable +source +operation +``` + +Une valeur secrète n'est jamais ajoutée à `ErrorContext`. + +## 16. Mutation et persistence + +### 16.1 Mutation source, pas mutation du snapshot effectif + +Une mutation modifie une source possédée par Config : + +- document JSON spécialisé ; +- fichier d'environnement géré. + +Elle ne modifie pas directement un objet `LoggingSettings` actif et ne prétend pas que la nouvelle valeur est immédiatement appliquée au runtime. + +### 16.2 Documents modifiables en `0.1.3` + +Surface initiale : + +```text +logging.config.json +composition fixtures/tests via API générique +config/environment.env +``` + +Aucune API d'écriture arbitraire de fichier n'est exposée. + +La composition runtime réelle n'existant pas encore, sa persistence est testée sur fixtures/exemples ; elle sera utilisée concrètement en `0.1.4`. + +### 16.3 API de mutation + +Pas de JSON Pointer générique public pour écrire n'importe quel champ. + +Les stratégies retenues sont : + +- remplacement/sauvegarde d'un document source typed après validation complète ; +- helpers typed pour les opérations fréquentes si les tests/applications en démontrent le besoin ; +- `set/remove` explicites uniquement pour un descriptor d'environnement enregistré et marqué writable ; le namespace, le type et la sensibilité sont validés avant persistence. + +### 16.4 Atomicité + +Garantie requise : + +```text +ancien fichier complet +OU +nouveau fichier complet +jamais un fichier destination partiellement écrit +``` + +La persistence doit utiliser un fichier temporaire dans le même répertoire et un remplacement atomique ; aucun fallback `truncate puis écrire` et aucun `delete destination puis rename` n'est accepté. + +Dans la première surface, une destination de mutation qui est elle-même un lien symbolique est refusée avant écriture. Config ne choisit ni de remplacer silencieusement le symlink, ni de suivre implicitement sa cible potentiellement hors `ConfigRoot`. La lecture peut être traitée séparément, mais une source writable doit satisfaire la politique de chemin avant commit. + +La dépendance candidate `atomic-write-file` est retenue pour cette responsabilité si son audit au moment de l'introduction confirme toujours son adéquation. Elle fournit précisément un commit de remplacement atomique sur Unix, Windows et WASI. + +La sauvegarde suit : + +```text +candidate typed +-> validation complète +-> sérialisation canonique +-> écriture temporaire +-> flush/fsync selon le backend retenu +-> commit atomique +-> relecture/résolution de contrôle si nécessaire +``` + +Les détails génériques de durabilité des ACL/xattrs/timestamps ne sont pas promis par `0.1.3` ; la garantie porte d'abord sur l'absence de contenu partiel et la conservation de l'ancien contenu en cas d'échec avant commit. + +`environment.env` mérite toutefois une politique spécifique parce qu'il peut contenir des secrets : + +- il reste non versionné ; +- sur Unix, sa création/réécriture doit conserver ou imposer un mode owner-only équivalent à `0600` lorsque l'API plateforme le permet ; +- une permission Unix plus large est un diagnostic de sécurité et doit être corrigée par la surface management avant d'écrire de nouveaux secrets ; +- aucune promesse de chiffrement au repos n'est faite ; +- la stratégie Windows/ACL est auditée au `pre.007` et documentée sans prétendre à une garantie non vérifiée. + +### 16.5 Desired vs effective + +Une mutation retourne un résultat distinguant au minimum : + +```text +source_changed +effective_changed +shadowed_by_process_environment +reload_required +``` + +Exemple : modifier `KSP_LOGS_DIRECTORY` dans `environment.env` alors qu'une valeur `KSP_LOGS_DIRECTORY` existe dans le processus doit réussir comme persistence source mais signaler que la valeur effective reste shadowed par le processus. + +## 17. Relation Config -> Logging + +### 17.1 Conversion + +`ksp-config-lib` possède le mapping de son document Logging vers les types publics Logging : + +```text +logging.config.json +-> ResolvedLoggingConfig +-> ksp_logging_lib::LoggingSettings +``` + +Aucune copie structurelle de `LoggingSettings` n'est maintenue comme contrat runtime parallèle. + +### 17.2 Initialisation + +Config n'initialise pas automatiquement Logging au simple chargement d'un document. + +L'orchestration fait : + +```text +Config resolve +-> LoggingSettings +-> ksp_logging_lib::initialize(...) +-> conserve LoggingGuard +``` + +### 17.3 Hot reload + +Après mutation/relecture : + +```text +Config resolve +-> nouveaux LoggingSettings +-> comparaison/changement +-> orchestration appelle ksp_logging_lib::reinitialize(&mut guard, ...) +``` + +`ksp-config-lib` peut fournir un `ChangeReport` indiquant que Logging est affecté, mais ne possède pas le `LoggingGuard`. + +### 17.4 Propriété du guard + +La règle est rendue concrète par étape : + +- dans `0.1.3`, le harness/test d’intégration qui exercera `initialize` / `reinitialize` possède localement le `LoggingGuard` et sa session Config ; +- dans la première application réelle prévue en `0.1.4`, l’état backend de `ksp-app-config-desk` possédera le `LoggingGuard` et la session Config nécessaires à son orchestration ; +- `ksp-config-lib` ne stocke jamais le `LoggingGuard` et n’introduit aucun singleton global Config ; +- Config peut émettre ses propres événements via `ksp-logging-lib`, sans exiger que Logging ait déjà été initialisé. + +Target racine Config : + +```text +ksp-config-lib +``` + +## 18. Dépendances externes candidates + +Aucune dépendance n'est ajoutée dans `pre.001`. + +Audit de génération effectué le 2026-08-14 ; les versions exactes seront revérifiées au delta qui les introduit réellement. + +### 18.1 `serde` + +Usage réel prévu : + +- `Deserialize` des documents source typed ; +- `Serialize` des documents source modifiables ; +- derives uniquement sur les contrats qui doivent réellement traverser cette frontière. + +Génération candidate observée : + +```text +^1.0 +``` + +### 18.2 `serde_json` + +Usage : parsing JSON, valeur brute pour validation schema, sérialisation canonique. + +Génération candidate : + +```text +^1.0 +``` + +### 18.3 `jsonschema` + +Usage : validation des schémas JSON source avant conversion effective. + +Politique candidate : + +```text +default-features = false +``` + +La première surface n'a besoin ni de résolution HTTP, ni de TLS, ni d'async resolver, car ses schémas sont locaux et auto-contenus. + +Génération actuelle observée en août 2026 : + +```text +^0.49 +``` + +### 18.4 Parser `environment.env` possédé par KSP + +Aucune dépendance dotenv n'est retenue. + +L'audit de `dotenvy 0.15.7` montre que son iterator ne modifie certes pas l'environnement du processus, mais son parseur effectue des substitutions de variables. Cela contredit le contrat KSP sans interpolation implicite. + +La grammaire KSP bornée de `environment.env` est suffisamment petite pour être parsée dans `ksp-config-lib` avec la bibliothèque standard et `serde_json` déjà nécessaire pour l'encodage sûr des valeurs quotées. Ce parser n'essaie pas d'émuler Bash ou un standard dotenv complet. + +### 18.5 `atomic-write-file` + +Usage : persistence atomique des fichiers Config lorsque la tranche mutation est ouverte. + +Génération candidate observée : + +```text +^0.3 +``` + +### 18.6 Dépendances non retenues initialement + +Ne pas introduire sans besoin : + +```text +tokio +notify +figment +config +clap +ts-rs +tauri +anyhow +thiserror +``` + +Le chargement Config initial est une I/O filesystem locale, bornée et synchrone ; `0.1.3` n'impose donc pas un runtime async à ses consumers. + +## 19. Surface Rust candidate de `ksp-config-lib` + +La structure exacte reste ajustable au développement, mais les responsabilités prévues sont : + +```text +src/lib.rs +src/error.rs +src/document.rs +src/composition.rs +src/environment.rs +src/sensitivity.rs +src/logging.rs +src/diagnostics.rs +src/management.rs +src/persistence.rs +``` + +Les modules restent privés ; l'API nécessaire est réexportée au crate-root. + +Types conceptuels à faire émerger uniquement au besoin : + +```text +ConfigDocumentKind +ConfigSource +CompositionDocument +CompositionProfile +EnvironmentSensitivity +EnvironmentSource +EnvironmentBinding +ConfigValueOrigin +ConfigChangeReport +ConfigManagementAccess +LoggingConfigDocument +LoggingProfileConfig +ResolvedLoggingConfig +``` + +Cette liste n'est pas une obligation de créer tous les types dans `pre.002`. + +## 20. Audits/tests à introduire + +### 20.1 Ownership + +Tests/audits empêchant : + +- `std::env::var`, `var_os`, `vars`, `set_var`, `remove_var` dans les crates KSP hors `ksp-config-lib` pour les variables applicatives KSP/KSPB ; +- lecture/écriture directe de `config/*.config.json` par une autre crate ; +- dépendance directe `tracing*` depuis Config ; +- dépendance inverse Logging -> Config ; +- TS-RS/Tauri dans Config sans décision explicite. + +L'audit ne doit pas interdire les usages système légitimes de `std::env` qui ne concernent pas la configuration applicative, par exemple des métadonnées de build/test ; il doit donc être suffisamment ciblé pour éviter un grep faux-positif simpliste. + +### 20.2 Documents/profils + +Tester : + +- `format_version` ; +- default profile présent/absent ; +- noms dupliqués ; +- composition sans override ; +- override valide/invalide ; +- chemins relatifs ; +- rejet chemin absolu/traversée hors `ConfigRoot` ; +- document kind inconnu ; +- invalid JSON/schema/semantic ; +- global hors profil. + +### 20.3 Environnement + +Tester : + +- process > environment.env > document ; +- parsing bool/enum/path ; +- binding inconnu non appliqué ; +- namespaces KSP/KSPB ; +- secret/public/internal ; +- aucune valeur secrète dans `Debug`, erreur ou diagnostic générique ; +- `KSP_ENV_FILE` bootstrap ; +- absence de `set_var/remove_var` en production. + +Les tests qui nécessitent de contrôler l'environnement du processus doivent éviter les races inter-tests, notamment par processus enfant ou sérialisation explicitement bornée de la fixture de test, sans introduire `unsafe` dans le code KSP. + +### 20.4 Persistence + +Tester : + +- candidate invalide n'altère pas le fichier existant ; +- échec avant commit conserve l'ancien contenu ; +- destination symlink refusée ; +- source writable hors `ConfigRoot` refusée ; +- succès produit un document relisible/validable ; +- newline final ; +- unsupported format refusé ; +- environnement shadowed correctement rapporté. + +### 20.5 Logging adapter + +Tester : + +- chaque enum Config -> enum Logging ; +- console off/stdout/stderr ; +- file on/off ; +- `logs_directory` global ; +- target filters ; +- env overrides ; +- `LoggingSettings::validate()` ; +- changement Config ne reconfigure rien implicitement ; le guard reste orchestration-owned. + +## 21. Découpage souple des prereleases + +### `0.1.3-pre.001` — audit + brainstorming + plan + +- audit stable `0.1.2` ; +- audit historique ciblé ; +- décisions de ce document ; +- version d'ouverture ; +- aucune crate/dépendance fonctionnelle Config. + +### `0.1.3-pre.002` — crate foundation + erreurs + modèles source + +- créer `ksp-config-lib` ; +- ajouter uniquement les dépendances réellement consommées par cette tranche ; +- contrats communs document/version ; +- document Logging typed ; +- erreurs Config nécessaires ; +- tests unitaires/intégration initiaux. + +### `0.1.3-pre.003` — JSON Schema + fichiers Config Logging + +- créer `config/`, `config/schemas/`, `config/examples/` ; +- schéma Logging ; +- runtime `logging.config.json` minimal sûr ; +- exemple Logging ; +- pipeline parse/schema/semantic ; +- validation du `default_profile`. + +### `0.1.3-pre.004` — composition + résolution profils + +- contrat generic composition ; +- schéma/exemple ; +- locators et chemins relatifs ; +- profils de composition et overrides spécialisés ; +- provenance document/global/profile. + +### `0.1.3-pre.005` — environnement + sensibilité + +- namespaces KSP/KSPB ; +- `environment.env` + exemple + parser KSP strict sans interpolation ; +- process/env-file precedence ; +- bindings/descriptors explicites Logging ; +- surfaces diagnostic/redaction ; +- audits ownership env. + +### `0.1.3-pre.006` — Logging adapter + lifecycle integration + +- conversion vers `LoggingSettings` ; +- validations croisées Config/Logging ; +- change report ; +- démonstration initialize/reinitialize en test sans transfert de propriété du `LoggingGuard`. + +### `0.1.3-pre.007` — management + persistence atomique + +- surface management explicitement séparée ; +- lecture de secrets autorisée par contrat management ; +- mutation typed Logging ; +- set/remove environment file ; +- atomic write ; +- desired/effective/shadowing report. + +### `0.1.3-pre.008` — ownership audits + robustesse + +- tests de non-divulgation ; +- tests invalid documents/env ; +- audit des lectures directes Config/env ; +- graphe dépendances/features ; +- corrections de surface publique. + +### `0.1.3-pre.009` — clôture + +- validations finales ; +- documentation durable ; +- cleanup/archivage requis uniquement ; +- TODO fermés ou reportés explicitement ; +- prompt `0.1.4 — ksp-app-config-desk` ; +- préparation `rel.001` puis tag stable après validation utilisateur. + +Le découpage reste souple : une tranche trop large est scindée ; une tranche devenue inutile est supprimée par correction explicite du plan. + +## 22. Hors scope confirmé + +- application desktop Config dans `0.1.3` ; +- Tauri/TS-RS dans `ksp-config-lib` ; +- configuration Transport/Store/Wallet/Execution avant leurs composants ; +- watcher filesystem ; +- reload automatique générique ; +- service de configuration distribué ; +- secrets manager distant ; +- chiffrement maison du fichier d'environnement ; +- modification de l'environnement global du processus ; +- interpolation arbitraire `${...}` dans les chaînes JSON ; +- getter générique de secrets par nom ; +- JSON patch arbitraire public ; +- sauvegarde de fichiers hors frontière Config ; +- RPC/WS/provider ; +- Program/decoder/execution ; +- workers/jobs/pipelines ; +- trading/ML. + +## 23. Validations finales attendues pour `0.1.3` + +Au minimum : + +```bash +cargo fmt --all +cargo check --workspace +cargo clippy --workspace --all-targets +cargo test --workspace +cargo tree -p ksp-config-lib +cargo tree -p ksp-config-lib -d +cargo tree -p ksp-config-lib -e features +cargo tree -p ksp-config-lib -e normal +cargo tree -p ksp-config-lib -e dev +``` + +Tout script d'audit réellement présent au moment de la clôture est également exécuté. + +Une commande non exécutée n'est jamais déclarée réussie. + +## 24. Critères de sortie du `pre.001` + +Le plan ferme les questions de cadrage initiales : + +- premier document spécialisé : `logging.config.json` uniquement ; +- pas de document applicatif général artificiel ; +- `logs_directory` global hors profils ; +- `default_profile` autonome dans les documents et compositions ; +- composition generic par identifiants de documents + overrides de profils ; +- chemins relatifs de sources résolus depuis le répertoire du composite ; +- environnement par bindings explicites, sans interpolation générique ; +- process env > environment file > document ; +- `composition.owner_namespace` fixe `ksp|kspb` et donc le selector `KSP_CONFIG_PROFILE` ou `KSPB_CONFIG_PROFILE` ; +- `KSP_ENV_FILE` est un bootstrap process-only avec priorité inférieure à un chemin explicite API ; +- `environment.env` utilise la grammaire KSP v1 stricte, sans interpolation, doublons ni noms non enregistrés ; +- `ConfigRoot` borne les sources gérées et les mutations ; +- aucune mutation du process env à cause de Rust 2024 + `unsafe` interdit ; +- management de `environment.env` prévu ; +- classification `Public/Internal/Secret` ; +- lecture de secrets autorisée seulement via surface management/runtime typed explicite ; +- DTO Tauri possédés par l'application future ; +- Config construit `LoggingSettings`, orchestration possède `LoggingGuard` ; +- persistence atomique obligatoire ; +- `0.1.3` reste une seule release bornée ; +- prereleases `pre.002` à `pre.009` dimensionnées. + +Les seules questions laissées volontairement au delta qui introduit une dépendance sont sa version patch exacte et l'audit final de ses features/transitives au jour de l'ajout.