# Plan `0.1.3` — Configuration foundation ## 1. Statut et objectif Ce plan a été établi par `0.1.3-pre.001`, puis corrigé par `0.1.3-pre.001-fix.001` avant tout développement fonctionnel de Config. La base auditée reste la release stable `v0.1.2`. La release `0.1.3` introduira `ksp-config-lib` comme **propriétaire unique KSP de la configuration applicative**. À terme, les autres crates et applications KSP ne doivent pas : - lire directement les documents JSON de configuration ; - valider elles-mêmes ces documents contre leurs schémas ; - résoudre elles-mêmes les profils ou les compositions ; - lire directement les variables applicatives `KSP_*` / `KSPB_*` par `std::env::*` ; - charger directement le fichier `.env` ; - interpréter elles-mêmes les expressions `${KSP_...}` ; - écrire directement les documents Config ou le `.env` géré par KSP. Elles passent par les contrats publics de `ksp-config-lib`. `ksp-config-lib` doit fournir le moteur commun nécessaire à : - la lecture des documents spécialisés JSON ; - la validation JSON Schema ; - les paramètres globaux hors profils ; - `default_profile` ; - les profils ; - les compositions propres aux exécutables/applications ; - la lecture des variables provenant du processus et d'un `.env` ; - la résolution des placeholders `${NAME}` et `${NAME:-fallback}` dans les valeurs de configuration ; - la classification `Public` / `Internal` / `Secret` ; - la conservation de la provenance et d'une représentation sûre/redacted des valeurs résolues ; - les diagnostics de variables manquantes ; - la manipulation et la persistence autorisées des documents JSON ; - la création/modification/suppression des entrées du `.env` ; - la construction des contrats runtime nécessaires aux consommateurs, en commençant par `ksp_logging_lib::LoggingSettings`. `0.1.3-pre.001-fix.001` reste une tranche documentaire de conception. Il ne crée pas encore `ksp-config-lib`, ne crée aucun fichier runtime Config et n'ajoute aucune dépendance fonctionnelle. ## 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 runtime `config/` n'est présent dans la base stable fournie. ### 2.1 Core disponible `ksp-core-lib` fournit déjà : ```text ksp_core_lib::Error ksp_core_lib::ErrorCode ksp_core_lib::ErrorContext ksp_core_lib::Result ``` Les codes propres à Config restent possédés par `ksp-config-lib`, sous le domaine Config. Core ne reçoit 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 ne redéfinit pas ces contrats. Il lit et résout sa configuration Logging, puis construit explicitement un `ksp_logging_lib::LoggingSettings`. La direction de dépendance 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 hors `src` selon la convention du dépôt ; - dépendances externes communes sous `[workspace.dependencies]` puis `.workspace = true` ; - aucune dépendance ajoutée avant son usage réel ; - `ksp-logging-lib` reste le seul propriétaire KSP direct de `tracing`, `tracing-subscriber` et `tracing-appender`. ## 3. Réaudit de la référence historique bot3 La référence historique bot3 utilisait déjà : - plusieurs documents spécialisés ; - un `.env` à la racine ; - des placeholders comme `${KS_LOGS_DIRECTORY:-logs}` ; - des URLs composées contenant `${KS_SECRET_HELIUS_API_KEY}` ; - des compositions applicatives ; - une classification de sensibilité dérivée des placeholders. Cette référence reste utile, mais KSP ne copie pas ses structures ni ses APIs aveuglément. ### 3.1 Principes conservés et renforcés KSP conserve : - documents spécialisés indépendants ; - valeurs globales hors profils lorsqu'elles ne varient pas ; - `default_profile` autonome ; - composition propre à un exécutable/application ; - sélection d'un profil spécialisé depuis la composition ; - `.env` séparé des documents JSON ; - priorité de l'environnement réel du processus sur `.env` ; - fallback déclaré au point d'usage via `${NAME:-fallback}` ; - classification nominale des variables ; - propagation de la sensibilité la plus forte dans une valeur composée ; - distinction entre valeur réelle et représentation diagnostic sûre ; - DTO Tauri possédés par l'application et non par `ksp-config-lib`. ### 3.2 Corrections par rapport au plan `pre.001` initial Le plan initial de `pre.001` avait retenu à tort : - `config/environment.env` au lieu d'un vrai `.env` ; - une grammaire d'environnement KSP spéciale ; - l'interdiction de l'interpolation `${...}` ; - des bindings d'environnement prédéclarés clé Config -> variable ; - une lecture limitée aux variables enregistrées par ces bindings. Ces décisions sont annulées. La règle corrigée est : > Une référence `${KSP_...}` ou `${KSPB_...}` présente dans une valeur JSON est elle-même une déclaration explicite d'usage de cette variable par le document. Config peut également exposer une API explicite de lecture d'une variable par nom avec fallback optionnel pour les rares usages qui ne proviennent pas d'un document JSON, mais les consumers ne lisent jamais directement `std::env`. ## 4. Décision de périmètre : `0.1.3` reste une seule release Le périmètre reste compatible avec une seule release si la première implémentation concrète reste bornée. La release construit le moteur générique nécessaire à : 1. documents JSON spécialisés ; 2. schémas ; 3. paramètres globaux ; 4. profils et `default_profile` ; 5. compositions ; 6. `.env` + environnement du processus ; 7. interpolation/fallback ; 8. provenance/sensibilité/redaction ; 9. mutation/persistence ; 10. adaptation Logging. Mais le seul document spécialisé runtime créé et exercé concrètement en `0.1.3` est : ```text config/std.logging.json ``` Aucun `std.store.json`, `std.wallet.json`, `std.onchain-transport.json` ou autre fichier futur n'est créé prématurément. La séquence reste : ```text 0.1.3 ksp-config-lib 0.1.4 ksp-app-config-desk ``` ## 5. Matrice de responsabilités | Responsabilité | Propriétaire | Consommateurs | Interdit | |----------------------------------------------------------|--------------------------------------|------------------------------------|-----------------------------------------------------------| | Lire un document Config JSON | `ksp-config-lib` | crates/apps via API Config | lecture directe par consumer | | Valider JSON Schema | `ksp-config-lib` | orchestration/management | validation divergente dans chaque crate | | Résoudre globals/profils/compositions | `ksp-config-lib` | orchestration | résolution locale dans un binaire | | Lire `KSP_*` / `KSPB_*` du processus | `ksp-config-lib` | crates/apps via API Config | `std::env::var*` applicatif hors Config | | Lire `.env` | `ksp-config-lib` | crates/apps via API Config | loader dotenv direct hors Config | | Résoudre `${...}` et fallback | `ksp-config-lib` | tous les consumers | interpolation locale dans les consumers | | Classer sensibilité et construire une valeur sûre | `ksp-config-lib` | runtime/logging/diagnostics | redaction ad hoc dans chaque crate | | Modifier/sauvegarder JSON Config | `ksp-config-lib` | management explicite | écriture directe par application | | Créer/modifier/supprimer une entrée `.env` | `ksp-config-lib` | management explicite | édition directe par application | | Modifier l'environnement externe du shell/systemd/parent | propriétaire externe de ce processus | Config le lit seulement | prétendre qu'un child process peut administrer son parent | | Posséder `LoggingSettings` | `ksp-logging-lib` | Config le construit | copie du type dans Config | | Posséder `LoggingGuard` | orchestration/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 ### 6.1 Documents runtime Les vrais documents JSON runtime appartiennent sous : ```text config/ ``` Nomenclature retenue pour les documents spécialisés/unitaires : ```text config/std..json ``` Premier fichier concret : ```text config/std.logging.json ``` Exemples futurs, uniquement lorsqu'ils deviennent nécessaires : ```text config/std.store.json config/std.wallet.json config/std.onchain-transport.json ``` ### 6.2 Compositions Les compositions propres à un exécutable/application utilisent : ```text config/composite..json ``` Exemple futur : ```text config/composite.ksp-app-wallet-desk.json ``` Aucun composite runtime fictif n'est créé avant l'existence d'un consumer concret. Le moteur et le schéma génériques peuvent néanmoins être testés dans `0.1.3` par fixtures/examples. ### 6.3 Schémas Les schémas appartiennent sous : ```text config/schemas/ ``` Première surface candidate : ```text config/schemas/std.logging.schema.json config/schemas/composite.schema.json ``` La nomenclature d'un futur document suit la même famille : ```text std.store.json std.store.schema.json ``` ### 6.4 Exemples Les exemples appartiennent sous : ```text config/examples/ ``` Exemples candidats : ```text config/examples/std.logging.example.json config/examples/composite.example.json ``` Les exemples ne contiennent aucun vrai secret. ### 6.5 `.env` Le fichier d'environnement local par défaut est le fichier conventionnel : ```text .env ``` à la racine runtime/workspace fournie à Config. Il reste ignoré par Git. Un éventuel `.env.example` peut être versionné plus tard lorsqu'une première variable concrète doit être documentée. La racine runtime et les chemins Config doivent être fournis explicitement à la session/locator Config ; `ksp-config-lib` ne doit pas rendre son comportement dépendant d'un current working directory implicite. ## 7. Contrat commun des documents JSON Chaque document JSON KSP possède une version de format : ```json { "format_version": 1 } ``` Politique : - `format_version` obligatoire ; - version inconnue refusée ; - JSON syntaxiquement invalide refusé ; - validation par le schéma correspondant avant utilisation runtime ; - invariants sémantiques supplémentaires après validation schema ; - document source modifiable représenté par un type sérialisable ; - configuration effective contenant potentiellement des secrets non sérialisable aveuglément ; - persistence JSON avec format stable/lisible et newline final ; - aucun secret ajouté implicitement dans un message d'erreur ou un `Debug`. Les placeholders d'environnement sont autorisés uniquement dans les **valeurs string** du JSON. Ils ne sont pas interprétés dans les noms de propriétés. Le schéma source doit donc autoriser explicitement une valeur littérale ou une expression Config lorsqu'un champ est substituable. ## 8. Premier document spécialisé : `std.logging.json` Logging est le seul composant runtime existant qui justifie aujourd'hui un document Config réel. Structure conceptuelle candidate : ```json { "format_version": 1, "logs_directory": "${KSP_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 ; - son fallback est exprimé dans le document au point d'usage ; - `default_profile` est global et référence un profil existant ; - les noms de profils sont uniques ; - `file` ne duplique pas le répertoire global ; - console/file peuvent être désactivés selon le schéma final ; - `target_filters` conserve l'ordre source ; - Config valide le document puis construit `LoggingSettings` ; - `LoggingSettings::validate()` reste l'ultime validation du contrat Logging. `std.logging.json` sert de premier test réel du moteur générique, mais son modèle ne doit pas enfermer Config dans Logging. ## 9. Paramètres globaux et profils Un document spécialisé distingue : ```text paramètres globaux + default_profile + profiles[] ``` Une valeur qui ne varie pas avec le profil reste globale. Le résultat effectif d'un document spécialisé est conceptuellement : ```text globals + selected profile + environment substitutions contained in those values ``` Le profil ne recopie pas artificiellement les globals. Sans composition : ```text profil explicitement demandé par API sinon document.default_profile ``` Un `default_profile` absent peut être autorisé uniquement pour un type de document dont le contrat le prévoit explicitement. Pour `std.logging.json`, il est requis. ## 10. Contrat de composition générique Une composition assemble plusieurs documents spécialisés sans dupliquer leur contenu. Structure conceptuelle : ```json { "format_version": 1, "default_profile": "default", "documents": { "logging": { "source": "std.logging.json" } }, "profiles": [ { "name": "default", "documents": { "logging": { "profile": "default" } } } ] } ``` Principes : - la composition référence des documents spécialisés ; - chaque document conserve ses globals ; - une composition peut choisir le profil d'un document ; - sans choix explicite, le `default_profile` du document est utilisé ; - le composite ne recopie pas les paramètres spécialisés ; - un profil composite assemble donc les **paramètres globaux** du document et le **profil sélectionné** de ce document ; - un document référencé est toujours lu et résolu par Config ; - profil/document inexistant = erreur ; - aucun JSON patch générique n'est introduit en `0.1.3` sans besoin concret. Cette structure répond au besoin « un composite pioche dans les documents et leurs profils/paramètres » sans dupliquer les valeurs. Les références cross-document à une propriété individuelle ne sont pas ouvertes tant qu'un cas concret ne les exige pas. ## 11. Modèle d'environnement ### 11.1 Namespaces Les variables applicatives admises sont : ```text KSP_SECRET_* KSP_PUBLIC_* KSP_* KSPB_SECRET_* KSPB_PUBLIC_* KSPB_* ``` Classification : ```text KSP_SECRET_* / KSPB_SECRET_* -> Secret KSP_PUBLIC_* / KSPB_PUBLIC_* -> Public autre KSP_* / KSPB_* -> Internal ``` Ordre de sensibilité : ```text Secret > Internal > Public ``` Un nom hors namespace KSP/KSPB n'est pas une variable applicative gérée par le moteur Config générique, sauf éventuel bootstrap technique explicitement documenté plus tard. ### 11.2 Sources et priorité Pour une variable `NAME`, la résolution est : ```text 1. environnement réel du processus 2. .env 3. fallback déclaré au point d'usage 4. missing ``` Donc : ```bash export KSP_PUBLIC_FOO=console ``` est prioritaire sur : ```dotenv KSP_PUBLIC_FOO=dotenv ``` et les deux sont prioritaires sur : ```text ${KSP_PUBLIC_FOO:-fallback} ``` ### 11.3 Sémantique du fallback Syntaxes initiales : ```text ${KSP_VAR} ${KSP_VAR:-fallback} ``` Dans KSP, `:-` signifie dans ce contrat : > utiliser le fallback seulement si la variable n'existe ni dans l'environnement du processus ni dans `.env`. Une variable explicitement définie avec une chaîne vide est considérée comme **définie**. KSP ne promet donc pas de reproduire toutes les subtilités d'expansion POSIX malgré une syntaxe volontairement familière. Le fallback appartient à l'usage dans le document, pas au `.env`. ### 11.4 Interpolation dans une chaîne composée Exemple : ```json { "url": "https://mainnet.helius-rpc.com/?api-key=${KSP_SECRET_HELIUS_API_KEY}" } ``` Le resolver doit pouvoir produire : ```text real: https://mainnet.helius-rpc.com/?api-key=abcdef123456 safe: https://mainnet.helius-rpc.com/?api-key=******** ``` Plusieurs placeholders peuvent apparaître dans une même chaîne. La sensibilité de la chaîne résolue est la sensibilité la plus forte de tous les placeholders utilisés. Si le placeholder est `KSP_SECRET_*`, le fragment substitué est masqué dans la représentation sûre **même lorsque la valeur vient du fallback**. ### 11.5 `.env` n'est pas le lieu des fallbacks Config Le `.env` sert de source locale de valeurs : ```dotenv KSP_SECRET_HELIUS_API_KEY=... KSP_PUBLIC_FOO=bar ``` La première version KSP ne dépend pas d'une expansion récursive de variables à l'intérieur du `.env`. Les fallbacks et compositions sont résolus dans les documents Config via `${...}`. Le parser `.env` retenu doit au minimum gérer proprement : - lignes vides ; - commentaires ; - `NAME=VALUE` ; - valeurs usuelles non quotées ; - valeurs quotées nécessaires aux espaces/caractères spéciaux ; - noms KSP/KSPB ; - erreurs de syntaxe diagnostics sans fuite de secret. La grammaire exacte et la stratégie de round-trip sont fixées au delta qui implémente le parser, mais aucun loader ne doit écraser les variables déjà présentes dans le processus. ## 12. Référence d'environnement = déclaration d'usage Le plan ne maintient plus de table statique du type : ```text logging.logs_directory -> KSP_LOGS_DIRECTORY ``` La déclaration : ```json "logs_directory": "${KSP_LOGS_DIRECTORY:-logs}" ``` est suffisante pour dire à Config : - quelle variable est utilisée ; - où elle est utilisée ; - quel fallback est prévu ; - quelle sensibilité s'applique d'après son nom ; - quelle provenance doit être reportée après résolution. Un document peut donc introduire plus tard : ```text ${KSP_SECRET_POSTGRES_MAINNET_URL} ${KSP_SECRET_HELIUS_API_KEY} ${KSP_WALLETS_DIRECTORY:-wallets} ``` sans ajouter une allowlist centrale spécifique à chaque domaine. Config doit néanmoins valider que le nom référencé appartient bien aux namespaces autorisés. ## 13. API directe de variables d'environnement Certains besoins futurs peuvent demander une variable sans qu'elle provienne d'un champ JSON. Cette lecture reste possédée par Config et peut être modélisée conceptuellement par une requête : ```text name fallback: Option ``` avec exactement la même priorité : ```text process > .env > fallback ``` Ainsi aucune autre crate n'a de raison d'appeler `std::env::var(...)` pour une variable applicative KSP/KSPB. Cette API directe ne devient pas un getter arbitraire permettant d'énumérer tous les secrets. Elle résout un nom explicitement demandé et applique les mêmes règles de sensibilité/diagnostic. ## 14. Provenance et résultat résolu Une valeur effective ne doit pas être réduite trop tôt à un `String` nu lorsque sa provenance ou sa sensibilité sont nécessaires. Conceptuellement, Config doit pouvoir représenter : ```text ResolvedValue value valeur réelle destinée au runtime safe_value représentation sûre destinée aux logs/diagnostics sensitivity Public | Internal | Secret provenance source(s) ayant participé à la résolution ``` Pour une chaîne composée, la provenance peut contenir plusieurs segments/références. Sources candidates : ```text DocumentLiteral EnvironmentProcess { name } EnvironmentDotEnv { name } EnvironmentFallback { name } ``` Un type contenant une valeur secrète ne doit pas dériver un `Debug` qui révèle `value`. Son `Debug`/`Display` par défaut doit utiliser la représentation sûre ou ne pas être implémenté. L'accès à la valeur réelle doit être explicite dans l'API. ## 15. Secret, public, internal et redaction ### 15.1 `Public` Une valeur dérivée uniquement de `KSP_PUBLIC_*`/`KSPB_PUBLIC_*` peut être exposée dans une projection publique lorsque le contrat applicatif le prévoit. ### 15.2 `Internal` Une valeur `KSP_*`/`KSPB_*` non public/secret est utilisable par le runtime. Son exposition générique externe n'est pas automatique ; elle peut être montrée dans un diagnostic debug explicitement prévu. ### 15.3 `Secret` Une valeur provenant d'un `KSP_SECRET_*`/`KSPB_SECRET_*` : - doit être accessible en clair au runtime légitime qui en a besoin ; - doit être accessible en clair à une surface de management explicitement privilégiée lorsqu'elle doit l'afficher/modifier ; - ne doit jamais apparaître en clair dans les logs Config ; - ne doit jamais apparaître en clair dans un message d'erreur ordinaire ; - ne doit pas être exposée par un `Debug` générique ; - doit être remplacée par une représentation telle que `********` dans une chaîne destinée au logging/diagnostic. Une URL contenant un secret reste donc utilisable réellement par un futur transport tout en offrant une chaîne sûre pour le logging. ### 15.4 Limite de la garantie `ksp-config-lib` fournit les types et représentations empêchant les fuites accidentelles dans le chemin normal. Il ne peut pas empêcher un consumer qui demande explicitement la valeur réelle puis choisit volontairement de la logger. Les règles KSP doivent donc imposer aux consumers d'utiliser la représentation sûre dans les logs. `ksp-logging-lib` ne scanne toujours pas les messages à la recherche de secrets. ## 16. Variables manquantes et warning obligatoire Cas : ```json { "url": "${KSP_PUBLIC_RPC_URL}" } ``` Si `KSP_PUBLIC_RPC_URL` : - n'existe pas dans le processus ; - n'existe pas dans `.env` ; - n'a pas de fallback ; alors Config doit au minimum : 1. produire un diagnostic structuré `missing environment variable without fallback` ; 2. émettre un `warn` sous le target `ksp-config-lib` lorsque Logging est disponible ; 3. inclure le nom de variable, le document et le chemin JSON ; 4. ne jamais inclure une valeur secrète ; 5. considérer la résolution effective comme incomplète. Pour une configuration runtime qui exige une valeur effective complète, cette situation devient une erreur de résolution et empêche la construction du contrat runtime. La lecture/édition du document source reste toutefois possible dans une application de management afin que l'utilisateur puisse corriger la configuration. Exemple de diagnostic sûr : ```text target = ksp-config-lib level = warn variable = KSP_SECRET_HELIUS_API_KEY document = config/std.onchain-transport.json path = $.profiles[0].url reason = environment variable is missing and no fallback is declared ``` Le nom de la variable n'est pas secret ; sa valeur éventuelle l'est. ## 17. Validation en plusieurs phases ### 17.1 Document source Pipeline : ```text path -> read UTF-8 -> JSON syntax -> format_version -> JSON Schema source -> typed source document -> invariants sémantiques source ``` Le schéma valide le **fichier tel qu'il est écrit**, placeholders compris. ### 17.2 Sélection profil/composition Puis : ```text select composition profile if any -> select each specialized document profile -> combine document globals + selected profile ``` ### 17.3 Résolution environnement Puis, pour chaque chaîne substituable : ```text parse placeholders -> process lookup -> .env lookup -> fallback -> missing diagnostic -> build real + safe value + provenance + sensitivity ``` ### 17.4 Validation effective Après résolution : - aucun placeholder requis ne doit rester non résolu pour un runtime complet ; - les contraintes métier effectives sont vérifiées ; - la conversion vers le contrat du composant est exécutée ; - le composant propriétaire valide son propre contrat public lorsque disponible. Pour Logging : ```text std.logging.json -> schema/source validation -> selected profile -> env resolution -> ResolvedLoggingConfig -> LoggingSettings -> LoggingSettings::validate() ``` Cette séparation évite de confondre « JSON source valide » et « configuration runtime utilisable ». ## 18. Lecture et mutation du `.env` ### 18.1 Lecture Config charge le `.env` sans remplacer les valeurs déjà présentes dans l'environnement du processus. Il construit sa propre vue : ```text ProcessEnvironment DotEnv Fallback ``` et choisit la source effective selon la priorité définie. ### 18.2 Mutation persistante La surface management de Config doit pouvoir : ```text create env entry update env entry remove env entry read env entry ``` sur le `.env` sélectionné. Une modification du `.env` ne prétend pas modifier : - le shell parent ; - une unité systemd ; - Docker/Kubernetes ; - l'environnement d'un autre processus. Si une variable est aussi définie dans le processus, la modification du `.env` réussit mais la valeur effective reste celle du processus. Le résultat de mutation doit donc signaler : ```text source_changed effective_changed shadowed_by_process_environment reload_required ``` ### 18.3 Rust 2024 En Rust 2024, la mutation globale du process par `std::env::set_var/remove_var` impose des contraintes `unsafe`; KSP interdit `unsafe`. La première surface KSP traite donc : - l'environnement du processus comme source **read-only** ; - `.env` comme source persistante **read/write** possédée par Config. Cette distinction correspond aussi à la réalité opérationnelle : un processus enfant ne peut pas réécrire l'environnement de son shell parent ou d'un service manager déjà lancé. ## 19. Mutation et persistence des documents JSON ### 19.1 Surface initiale Dans `0.1.3`, le document réellement modifiable est : ```text config/std.logging.json ``` Les fixtures de composition peuvent exercer le moteur générique sans créer un composite runtime fictif. ### 19.2 API Config ne fournit pas une primitive publique « écrire n'importe quel JSON à n'importe quel chemin ». La mutation porte sur un document Config connu : ```text load typed source -> modify allowed fields -> validate candidate -> serialize -> persist atomically ``` Une API management peut fournir des helpers plus ergonomiques, mais elle passe toujours par la validation Config. ### 19.3 Atomicité Garantie requise : ```text ancien fichier complet OU nouveau fichier complet jamais un fichier destination partiellement écrit ``` Même garantie pour `.env`. Un échec de validation ou d'écriture avant commit conserve l'ancien fichier utilisable. La stratégie exacte d'écriture atomique et la dépendance éventuelle sont auditées au delta qui implémente cette surface. ### 19.4 Chemins Les documents sous `config/` sont bornés par un `ConfigRoot` explicite. Les compositions ne doivent pas pouvoir sortir de cette frontière par un chemin absolu ou `..` non autorisé. Le `.env` est une source distincte, localisée par un chemin de session Config explicite avec défaut workspace/runtime `.env`. ## 20. Surface runtime, diagnostic et management Trois intentions doivent être distinguées : ```text ConfigRuntime ConfigDiagnostics ConfigManagement ``` Les noms Rust exacts pourront évoluer, mais la séparation est normative. ### 20.1 Runtime Un consumer demande la configuration dont il a besoin. Il peut obtenir une valeur réelle nécessaire au fonctionnement et sa représentation sûre lorsqu'elle peut être loggée. Il ne reçoit pas automatiquement une map de tous les secrets ou toute la configuration du projet. ### 20.2 Diagnostics La surface diagnostic ne révèle pas les secrets. Elle expose notamment : - configured/missing ; - provenance ; - source shadowed ; - safe value ; - document/path ; - erreurs de validation. ### 20.3 Management privilégié Une application telle que `ksp-app-config-desk` doit pouvoir, via Config : - lire un document source ; - lire ses valeurs effectives ; - modifier et sauvegarder le document ; - inspecter les entrées `.env` ; - créer/modifier/supprimer une entrée `.env` ; - révéler explicitement une valeur `Secret` pour affichage/édition légitime ; - voir qu'une valeur `.env` est shadowed par le process ; - relancer la résolution après modification. Cette surface privilégiée ne rend pas les secrets autorisés dans les logs. L'exception concerne la **visualisation/édition volontaire dans l'UI**, pas le logging. L'authentification/autorisation de l'utilisateur final appartient à l'application ; Config fournit une frontière d'API évitant l'exposition accidentelle. ## 21. 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 : - commandes Tauri ; - DTO publics ; - DTO diagnostics ; - DTO management privilégiés ; - logique d'autorisation UI si nécessaire. L'application n'utilise ni `std::env`, ni parser dotenv, ni lecture JSON directe pour contourner Config. ## 22. Relation Config -> Logging ### 22.1 Conversion ```text config/std.logging.json -> résolution globale/profil/env -> ResolvedLoggingConfig -> ksp_logging_lib::LoggingSettings ``` ### 22.2 Lifecycle Config n'appelle pas automatiquement `initialize` au chargement. L'orchestration fait : ```text Config resolve -> LoggingSettings -> ksp_logging_lib::initialize(...) -> conserve LoggingGuard ``` Puis après modification : ```text Config resolve -> nouveaux LoggingSettings -> orchestration décide -> ksp_logging_lib::reinitialize(&mut guard, ...) ``` Config peut produire un `ChangeReport`, mais ne possède jamais `LoggingGuard`. ### 22.3 Propriété du guard - dans `0.1.3`, le harness d'intégration possède localement Config + `LoggingGuard` ; - dans `0.1.4`, l'état backend de `ksp-app-config-desk` les possède ; - aucun singleton global Config n'est introduit. Target Config : ```text ksp-config-lib ``` Les warnings de variables manquantes ou autres diagnostics utiles sont réémis sous ce target. ## 23. Erreurs Config candidates Les codes exacts sont ajoutés seulement lorsqu'ils deviennent nécessaires. Familles candidates : ```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.invalid_composition config.path_outside_root config.invalid_environment_name config.dotenv_read_failed config.dotenv_parse_failed config.dotenv_write_failed config.environment_variable_missing config.environment_value_invalid config.placeholder_invalid config.placeholder_unresolved config.secret_access_denied config.mutation_not_allowed config.persistence_failed config.effective_validation_failed ``` Une erreur liée à `KSP_SECRET_*` peut contenir le **nom** de variable et son origine, mais jamais sa valeur réelle. ## 24. Dépendances externes candidates Aucune dépendance n'est ajoutée dans `pre.001-fix.001`. Les besoins prévisibles sont : - `serde` : source typed sérialisable/désérialisable ; - `serde_json` : JSON ; - moteur JSON Schema ; - éventuellement une primitive d'écriture atomique ; - éventuellement une bibliothèque dotenv **uniquement si** elle permet la lecture sans mutation globale du processus et si sa sémantique est compatible avec le contrat KSP. Les versions courantes et les features sont vérifiées uniquement au delta qui introduit réellement la dépendance, puis déclarées sous `[workspace.dependencies]` avec la contrainte de génération retenue par les règles KSP. ### 24.1 Parser dotenv Aucune dépendance dotenv n'est décidée dans le plan. Le choix doit respecter : - process env non écrasé ; - accès aux couples clé/valeur pour construire la vue Config ; - diagnostics contrôlables ; - possibilité de gérer la persistence `.env` ; - absence de comportement implicite contraire à la résolution KSP. Si aucune bibliothèque ne convient proprement, un parser borné peut être possédé par Config. Le but n'est pas d'émuler un shell complet. ### 24.2 Placeholder resolver La syntaxe `${NAME}` / `${NAME:-fallback}` dans les documents JSON est une responsabilité KSP, même si un parser dotenv tiers est utilisé pour `.env`. Elle ne doit pas être déléguée à une expansion opaque qui ferait perdre provenance, sensibilité et représentation redacted par segment. ## 25. Surface Rust conceptuelle Structure interne candidate : ```text src/lib.rs src/error.rs src/document.rs src/schema.rs src/profile.rs src/composition.rs src/environment.rs src/placeholder.rs src/sensitivity.rs src/resolved.rs src/logging.rs src/diagnostics.rs src/management.rs src/persistence.rs ``` Les modules restent privés ; l'API utile est réexportée au crate-root. Types conceptuels à faire émerger au besoin : ```text ConfigRoot ConfigSession ConfigDocumentKind ConfigSource CompositionDocument CompositionProfile EnvironmentSensitivity EnvironmentSource EnvironmentRequest EnvironmentReference ResolvedValue ConfigValueOrigin ConfigDiagnostic ConfigChangeReport ConfigManagementAccess LoggingConfigDocument LoggingProfileConfig ResolvedLoggingConfig ``` Cette liste n'impose pas de tout créer dans `pre.002`. ## 26. Audits et tests à introduire ### 26.1 Ownership Empêcher les accès applicatifs directs hors Config : - `std::env::var`, `var_os`, `vars` pour `KSP_*`/`KSPB_*` ; - loaders dotenv directs ; - lecture/écriture directe des documents `config/std.*.json` et `config/composite.*.json` ; - dépendance directe `tracing*` depuis Config ; - dépendance inverse Logging -> Config ; - Tauri/TS-RS dans Config sans décision explicite. L'audit doit être ciblé afin de ne pas interdire des usages système de `std::env` sans rapport avec la configuration applicative. ### 26.2 Documents/schemas Tester : - JSON invalide ; - schema invalide ; - format version absent/inconnu ; - globals hors profils ; - `default_profile` ; - profils dupliqués ; - profil absent ; - composition valide/invalide ; - chemin hors `ConfigRoot`. ### 26.3 Environnement Tester : ```text process > .env > fallback ``` ainsi que : - variable process seule ; - variable `.env` seule ; - process shadowing `.env` ; - fallback seul ; - chaîne vide définie ne déclenche pas fallback ; - variable manquante sans fallback -> diagnostic + warning + résolution runtime incomplète ; - namespace invalide ; - plusieurs placeholders dans une chaîne ; - placeholder malformé ; - `.env` invalide ; - commentaires/quotes du `.env` selon grammaire retenue. Les tests de process env doivent éviter les races inter-tests, par isolation de processus ou autre stratégie sûre sans `unsafe` KSP. ### 26.4 Secrets Canaries obligatoires : ```text SECRET-CANARY ``` Tester qu'elles n'apparaissent jamais dans : - `Debug` ; - warning Config ; - Error/ErrorContext ; - diagnostics génériques ; - safe value ; - logs de tests d'intégration. Tester aussi qu'une valeur réelle reste accessible par le contrat runtime/management explicitement autorisé. Exemple composé : ```text https://provider.invalid/?api-key=${KSP_SECRET_TEST_KEY}&network=${KSP_PUBLIC_NETWORK:-mainnet} ``` avec résultat sûr attendu du type : ```text https://provider.invalid/?api-key=********&network=mainnet ``` ### 26.5 Persistence Tester : - candidate JSON invalide n'altère pas le fichier ; - candidate `.env` invalide n'altère pas le fichier ; - create/update/remove `.env` ; - mutation shadowed par process rapportée ; - succès relisible ; - failure avant commit conserve ancien contenu ; - chemins gérés respectés. ### 26.6 Logging adapter Tester : - `logs_directory` global avec fallback ; - sélection de profil ; - conversion de chaque enum ; - console/file ; - target filters ; - `LoggingSettings::validate()` ; - changement Config sans reconfiguration implicite ; - `LoggingGuard` reste orchestration-owned. ## 27. Découpage souple des prereleases ### `0.1.3-pre.001` + `pre.001-fix.001` — audit, brainstorming et plan corrigé - audit `v0.1.2` ; - audit historique ciblé ; - choix du propriétaire unique Config ; - correction vers `.env` conventionnel ; - interpolation `${...}` + fallback ; - modèle real/safe/provenance/sensitivity ; - aucune crate/dépendance fonctionnelle Config. ### `0.1.3-pre.002` — fondation de crate + contrats source - créer `ksp-config-lib` ; - dépendances minimales réellement utilisées ; - erreurs initiales ; - `ConfigRoot`/session/locator ; - contrat commun document/version ; - premiers types `std.logging` source ; - tests initiaux. ### `0.1.3-pre.003` — JSON Schema + `std.logging.json` - `config/`, `config/schemas/`, `config/examples/` ; - `std.logging.schema.json` ; - runtime `config/std.logging.json` ; - exemple Logging ; - pipeline parse/schema/semantic ; - `default_profile` et globals. ### `0.1.3-pre.004` — profils + composition - résolution de profils spécialisés ; - contrat composite générique ; - schema/example composite ; - sélection de profils documentaires ; - provenance document/global/profile ; - aucun composite runtime fictif obligatoire. ### `0.1.3-pre.005` — `.env` + resolver `${...}` - lecture process env ; - lecture `.env` sans écrasement process ; - priorité process > `.env` > fallback ; - `${NAME}` / `${NAME:-fallback}` ; - missing diagnostics + warning ; - namespaces KSP/KSPB ; - tests d'isolation process env. ### `0.1.3-pre.006` — sensibilité + valeurs real/safe + Logging adapter - `Public/Internal/Secret` ; - propagation par chaîne composée ; - redaction par segment ; - contrat `ResolvedValue` ou équivalent ; - adaptation `ResolvedLoggingConfig -> LoggingSettings` ; - tests canary de non-divulgation ; - démonstration `initialize/reinitialize` avec guard orchestration-owned. ### `0.1.3-pre.007` — management + persistence JSON/.env - lecture management ; - reveal secret explicite ; - mutation typed de `std.logging.json` ; - create/update/remove `.env` ; - persistence atomique ; - desired/effective/shadow report. ### `0.1.3-pre.008` — ownership audits + robustesse - audits interdisant les accès Config/env directs ailleurs ; - invalid documents/env/placeholders ; - graphe dépendances/features ; - corrections de surface publique ; - robustesse des diagnostics sans fuite. ### `0.1.3-pre.009` — clôture - validations finales ; - documentation durable ; - cleanup/archivage requis ; - TODO fermés ou reportés ; - prompt `0.1.4 — ksp-app-config-desk` ; - préparation `rel.001` puis tag stable après validation utilisateur. Le découpage reste souple et peut être corrigé par delta explicite. ## 28. Hors scope confirmé - application desktop Config dans `0.1.3` ; - Tauri/TS-RS dans `ksp-config-lib` ; - documents Store/Wallet/Transport/Execution avant leurs composants ; - watcher filesystem générique ; - reload automatique de tous les fichiers ; - service distribué de configuration ; - secrets manager distant ; - chiffrement maison de `.env` ; - modification du shell parent/systemd/Docker par Config ; - exposition générique de tous les secrets ; - redaction automatique de messages arbitraires par `ksp-logging-lib` ; - JSON patch arbitraire public ; - RPC/WS/provider ; - Program/decoder/execution ; - workers/jobs/pipelines ; - trading/ML. ## 29. 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 ``` Exécuter aussi tout script d'audit réellement présent au moment de la clôture. Une commande non exécutée n'est jamais déclarée réussie. ## 30. Critères de validation du plan avant `pre.002` Le `pre.001-fix.001` est validable lorsque les décisions suivantes sont acceptées : - `ksp-config-lib` est l'unique manager KSP des documents Config et variables applicatives ; - premier document réel : `config/std.logging.json` ; - futurs documents : `config/std..json` ; - futurs composites : `config/composite..json` ; - JSON validé par schema avant usage ; - globals hors profils ; - `default_profile` autonome ; - composite assemble documents + globals + profils sans recopier les valeurs ; - `.env` conventionnel à la racine runtime/workspace ; - environnement du processus > `.env` > fallback ; - fallback déclaré par `${KSP_VAR:-fallback}` ; - fallback utilisé seulement lorsque la variable est absente ; une chaîne vide compte comme définie ; - `${KSP_VAR}` manquant sans fallback produit diagnostic + warning et empêche une résolution runtime complète ; - une référence `${KSP_...}`/`${KSPB_...}` est elle-même la déclaration d'usage, sans table de bindings centrale ; - Config peut aussi résoudre explicitement une variable demandée par API ; - `.env` est read/write via Config ; process env est read-only ; - les autres crates n'appellent pas `std::env::var(...)` pour les variables applicatives ; - `Secret` reste disponible au runtime légitime mais dispose toujours d'une représentation sûre/redacted ; - une chaîne composée conserve valeur réelle + valeur sûre + provenance + sensibilité ; - `ksp-app-config-desk` pourra révéler/modifier les secrets via une surface management privilégiée ; - même cette application ne logge jamais les secrets ; - Config construit `LoggingSettings` et l'orchestration possède `LoggingGuard` ; - persistence JSON et `.env` atomique ; - `0.1.3` reste une release unique bornée ; - aucune implémentation `pre.002` ne commence avant validation utilisateur de ce plan corrigé.