Files
khadhroony-solana-project/docs/plans/005-V0_1_3_CONFIG_FOUNDATION_PLAN.md
2026-08-15 00:12:27 +02:00

45 KiB
Raw Blame History

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 :

crates/ksp-core-lib
crates/ksp-logging-lib

Le manifest racine utilise :

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 :

ksp_core_lib::Error
ksp_core_lib::ErrorCode
ksp_core_lib::ErrorContext
ksp_core_lib::Result<T>

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à :

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 :

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 :

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 :

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 :

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 :

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 len-tête de format réservé :

# 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 len-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

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

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 :

config/<executable>.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 :

{
  "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 :

{
  "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 :

{
  "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 :

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 :

profil explicitement demandé par API
    sinon document.default_profile

10.2 Avec composition

Pour la composition :

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 :

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 :

profil explicite API
> variable du processus
> entrée environment.env
> composition.default_profile

Puis, pour chaque document :

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 :

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 :

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 :

KSP_SECRET_*
KSP_PUBLIC_*
KSP_*

KSPB_SECRET_*
KSPB_PUBLIC_*
KSPB_*

Ordre de sensibilité, du plus fort au plus faible :

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 :

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 :

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 :

KSP_ENV_FILE

Ordre de sélection du fichier :

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
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

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

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 :

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

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 :

ResolvedLoggingConfig
-> LoggingSettings
-> LoggingSettings::validate()

15. Erreurs Config

Les codes sont possédés par ksp-config-lib sous le domaine :

config

Candidats fixés pour être introduits uniquement lorsqu'ils deviennent nécessaires :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

Config resolve
-> LoggingSettings
-> ksp_logging_lib::initialize(...)
-> conserve LoggingGuard

17.3 Hot reload

Après mutation/relecture :

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 dinté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 nintroduit 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 :

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 :

^1.0

18.2 serde_json

Usage : parsing JSON, valeur brute pour validation schema, sérialisation canonique.

Génération candidate :

^1.0

18.3 jsonschema

Usage : validation des schémas JSON source avant conversion effective.

Politique candidate :

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 :

^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 :

^0.3

18.6 Dépendances non retenues initialement

Ne pas introduire sans besoin :

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 :

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 :

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 :

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.