89 KiB
Plan 0.1.3 — Configuration foundation
1. Statut et objectif
Ce plan a été établi par 0.1.3-pre.001, corrigé par 0.1.3-pre.001-fix.001/.002/.003, puis exécuté par petites tranches. pre.002 a livré le bootstrap Config, pre.003 le registre file_id, pre.004 les contrats publics multi-output de Logging, pre.005 le runtime multi-sink sur niveau/target/formats, pre.006 le routing structuré domain, pre.007 le moteur JSON/JSON Schema, pre.008 la résolution des globals/profils/default_profile, pre.009 les compositions génériques par file_id, pre.010 le snapshot process + .env et le resolver ${...}, pre.011 la sensibilité et les représentations real/safe/provenance, puis pre.012 l'adapter Config -> Logging. pre.013 a livré management + persistence JSON/.env et son fix.001 a corrigé la syntaxe du warning de cleanup. pre.014 livre maintenant les ownership audits exécutables et la robustesse de frontière. La prochaine tranche est pre.015 pour la clôture.
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_*parstd::env::*; - charger directement le fichier
.env; - interpréter elles-mêmes les expressions
${KSP_...}; - écrire directement les documents Config ou le
.envgéré par KSP.
Elles passent par les contrats publics de ksp-config-lib.
ksp-config-lib doit fournir le moteur commun nécessaire à :
- un registre logique interne
file_id -> filenamepour tous les fichiers Config/schemas connus, avec identifiants uniques et mappings par défaut surchargeables au bootstrap ; - deux chemins bootstrap non récursifs (
cfgpathetschemapath) possédés par Config, avec défauts codés en dur et surcharge uniquement par argument/paramètre de démarrage ; - 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.003 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 :
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 runtime config/ n'est présent dans la base stable fournie.
2.1 Core disponible
ksp-core-lib fournit déjà :
ksp_core_lib::Error
ksp_core_lib::ErrorCode
ksp_core_lib::ErrorContext
ksp_core_lib::Result<T>
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à :
LoggingSettings
LogFilterLevel
SpanEvents
ConsoleSettings
ConsoleOutput
FileSettings
FileRotation
TargetFilter
LoggingGuard
initialize(...)
reinitialize(...)
L'audit réel du code v0.1.2 montre cependant que cette surface est plus étroite que la configuration Logging historique de bot3 :
- une seule sortie console optionnelle ;
- un seul sink fichier optionnel ;
- rotation fichier
never/hourly/daily; - filtre par défaut + overrides par préfixe de target ;
- formatter actuellement fixé par Logging ;
- pas de sélection
console_ansipublique ; - pas de plusieurs fichiers simultanés ;
- pas de format
human/compact/pretty/jsonsélectionnable par sink ; - pas de routage d'un sink par combinaison
target/domain/level; domainreste aujourd'hui un champ structuré émis par le caller mais n'est pas un sélecteur public de routing.
La référence bot3 possédait déjà plusieurs sinks console/fichier, des niveaux et targets par sink, des formats, ANSI et rotation. KSP ne doit donc pas figer un std.logging.json qui constituerait une régression fonctionnelle par rapport à cette surface utile.
Décision :
- Config ne réimplémente jamais le routing Logging ;
ksp-logging-libreste propriétaire deLoggingSettings, du subscriber, des layers, des writers et du lifecycleinitialize/reinitialize;- avant gel de
std.logging.schema.json, la surface publique de Logging doit être complétée de manière bornée pour représenter les sinks/routings retenus par ce plan ; - cette complétion ne change ni la direction de dépendance ni la propriété du lifecycle Logging et n'introduit aucune lecture Config dans Logging.
Config lit/résout le document puis construit uniquement des contrats publics ksp_logging_lib::* suffisamment expressifs.
La direction de dépendance 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,panicou?dans le code production ; - façade publique au crate-root, modules privés, pas de
mod.rs; - tests unitaires hors
srcselon 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-libreste le seul propriétaire KSP direct detracing,tracing-subscriberettracing-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 ;
- un document Logging avec
logs_directory,default_profileet une liste de profils nommés ; - plusieurs sorties Logging par profil, chacune avec identifiant, activation, sink console/fichier, niveau, chemin, rotation, format, ANSI et liste de targets ;
- plusieurs fichiers simultanés permettant notamment de séparer debug/info/error et des targets spécialisées.
Cette référence reste utile, mais KSP ne copie pas ses structures ni ses APIs aveuglément. En particulier, KSP utilise maintenant un target propriétaire égal au nom Cargo de la crate et des champs structurés comme domain; le nouveau modèle doit donc permettre un routing plus propre par target et/ou domain sans ressusciter artificiellement les anciens pseudo-targets hiérarchiques.
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_profileautonome ;- composition propre à un exécutable/application ;
- sélection d'un profil spécialisé depuis la composition ;
.envséparé des documents JSON ;.env.exampleversionné et maintenu au fil de l'apparition des variables runtime ;- 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; - richesse de configuration Logging au moins équivalente aux besoins utiles de bot3, sans recopier ses limites de conception.
3.2 Corrections par rapport au plan pre.001 initial
Le plan initial de pre.001 avait retenu à tort :
config/environment.envau 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 ;
- un exemple
std.logging.jsonréduit à une console + un fichier, alors que cette surface ne représente pas les besoins Logging déjà connus.
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.
Le nom physique d'un fichier Config ou schema n'est pas non plus son identité logique : toute référence inter-document passe par un file_id stable connu du registre Config.
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 l'implémentation reste découpée et si la complétion Logging est strictement bornée à la représentation/routage nécessaire au premier document Config réel.
La release construit le moteur générique nécessaire à :
- bootstrap Config non récursif (
cfgpath,schemapath) ; - registre logique des fichiers
file_id -> filenameet overrides de mapping au démarrage ; - documents JSON spécialisés ;
- schémas ;
- paramètres globaux ;
- profils et
default_profile; - compositions ;
.env+ environnement du processus ;- interpolation/fallback ;
- provenance/sensibilité/redaction ;
- mutation/persistence ;
- adaptation Logging sans perte de capacités nécessaires.
Mais le seul document spécialisé runtime créé et exercé concrètement en 0.1.3 est :
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 :
0.1.3 ksp-config-lib + complétion bornée du contrat Logging nécessaire
0.1.4 ksp-app-config-desk
La complétion Logging n'ouvre ni une nouvelle façade ni une dépendance inverse : elle enrichit uniquement les contrats publics déjà possédés par ksp-logging-lib afin que Config puisse exprimer sans régression le document Logging.
5. Matrice de responsabilités
| Responsabilité | Propriétaire | Consommateurs | Interdit |
|---|---|---|---|
Définir les file_id connus et leur mapping par défaut |
ksp-config-lib |
bootstrap Config | noms physiques codés dans les consumers |
Résoudre file_id -> filename |
ksp-config-lib |
session Config | référence inter-document par filename |
Interpréter --cfgpath, --schemapath et overrides de mapping KSP |
ksp-config-lib |
applications qui transmettent argv/options | parser ces options différemment dans chaque binaire |
| 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 les settings/sinks/routing Logging | ksp-logging-lib |
Config construit les contrats publics | logique de routing dupliquée 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. Registre de fichiers, bootstrap, arborescence et nomenclature
6.1 Identité logique : file_id
ksp-config-lib possède un registre des fichiers qu'il sait interpréter. Un fichier n'est jamais identifié par son nom physique dans les contrats inter-documentaires.
Chaque entrée possède au minimum :
file_id unique
kind = config_document | schema
filename par défaut
racine = cfgpath | schemapath
schema_file_id éventuel pour un document Config
Les file_id sont uniques dans un même namespace global Config, y compris entre documents et schemas. Préfixes retenus :
cfg.std.logging
schema.std.logging
schema.composite
cfg.composite.<consumer>
Premier mapping codé dans ksp-config-lib :
cfg.std.logging -> std.logging.json
schema.std.logging -> std.logging.schema.json
Le descriptor cfg.std.logging référence logiquement schema.std.logging; il ne contient pas le nom physique du schema. Le futur schema.composite n'est ajouté au registre qu'avec la tranche composite qui en a réellement besoin.
Un futur consumer pourra introduire par le registre KSP :
cfg.composite.ksp-app-wallet-desk -> composite.ksp-app-wallet-desk.json
avec validation par schema.composite tant qu'un schema plus spécialisé n'est pas nécessaire.
Un override change uniquement le filename résolu, jamais le file_id. Ainsi un composite continue de référencer cfg.std.logging même si l'utilisateur sélectionne my-logging.local.json au démarrage.
Par défaut, un override CLI d'un file_id inconnu est refusé : il ne doit pas permettre de contourner le type/schema que Config sait gérer.
6.2 Bootstrap non récursif : cfgpath et schemapath
Deux valeurs seulement doivent exister avant toute lecture de document Config :
DEFAULT_CFG_PATH = "config"
DEFAULT_SCHEMA_PATH = "config/schemas"
Ces défauts sont codés en dur dans ksp-config-lib.
Ils ne peuvent pas être définis ou remplacés par :
std.*.json
composite.*.json
.env
KSP_*
KSPB_*
sinon Config aurait besoin de charger Config pour savoir où charger Config.
Ils peuvent changer uniquement au bootstrap par :
--cfgpath=/path/to/configs
--schemapath=/path/to/schemas
ou par l'équivalent programmatique explicite de ConfigBootstrapOptions lorsqu'un caller embarque Config sans CLI.
ksp-config-lib doit posséder l'interprétation de ces options. Un binaire peut lui transmettre les arguments bruts ou construire explicitement le type public prévu, mais ne doit pas réimplémenter leur sémantique.
Les deux chemins sont indépendants : remplacer cfgpath ne remplace pas implicitement schemapath.
6.3 Override des mappings physiques au bootstrap
Le registre utilise ses filenames par défaut sauf surcharge explicite au démarrage.
Syntaxe CLI retenue pour le plan, répétable :
--filemap=<file_id>=<filename>
Exemples :
--filemap=cfg.std.logging=my.logging.json
--filemap=schema.std.logging=my.logging.schema.json
--filemap=cfg.composite.ksp-app-wallet-desk=wallet-desk.local.json
Équivalent conceptuel programmatique :
ConfigBootstrapOptions
.with_file_mapping(file_id, filename)
Garanties :
- dernier override explicite d'un même
file_idgagne ou duplication est refusée selon la règle qui sera figée avec le parser CLI ; - filename absolu interdit dans un mapping : la racine reste possédée par
cfgpath/schemapath; - traversal hors racine (
..) refusé ; - le mapping final est consultable via diagnostics/management sans exposer de secret ;
- les compositions référencent des
file_id, jamais les filenames résolus.
6.4 Documents runtime
Les vrais documents JSON runtime appartiennent par défaut sous :
config/
Nomenclature retenue pour les documents spécialisés/unitaires :
std.<domain>.json
Premier fichier concret :
config/std.logging.json
Exemples futurs, uniquement lorsqu'ils deviennent nécessaires :
config/std.store.json
config/std.wallet.json
config/std.onchain-transport.json
6.5 Compositions
Les compositions propres à un exécutable/application utilisent par défaut :
config/composite.<consumer>.json
Exemple futur :
config/composite.ksp-app-wallet-desk.json
Leur identité logique reste cfg.composite.<consumer>; le filename peut être remplacé par --filemap sans modifier leur contenu logique.
Aucun composite runtime fictif n'est créé avant l'existence d'un consumer concret. Le moteur et le schéma génériques sont exercés par config/examples/composite.example.json et par un descriptor de test privé à ksp-config-lib.
Contrat générique livré en pre.009 :
{
"format_version": 1,
"default_profile": "local",
"profiles": [
{
"profile_id": "local",
"documents": [
{
"component_id": "logging",
"file_id": "cfg.std.logging",
"profile_id": "local_dev"
}
]
}
]
}
component_id est local au profil composite et unique dans celui-ci. file_id référence exclusivement un document standard cfg.std.* connu du registre. profile_id est optionnel : absent, il conserve le default_profile autonome du document référencé ; présent, il sélectionne explicitement ce profil au nom du composite. Les composites ne référencent jamais les filenames physiques et ne s'imbriquent pas dans cette première surface.
6.6 Schémas
Les schémas appartiennent par défaut sous :
config/schemas/
Surface actuelle :
config/schemas/std.logging.schema.json
config/schemas/composite.schema.json
Chaque schema possède également un file_id et son filename peut être surchargé indépendamment :
schema.std.logging
schema.composite
Le choix du schema applicable provient du descriptor Config, pas d'un filename construit par convention à chaque lecture.
6.7 Exemples
Les exemples appartiennent sous :
config/examples/
Exemples candidats :
config/examples/std.logging.example.json
config/examples/composite.example.json
Les exemples ne contiennent aucun vrai secret.
6.8 .env
Le fichier d'environnement local par défaut reste le fichier conventionnel :
.env
à la racine de lancement du processus (./.env) capturée au bootstrap. 0.1.3 n'introduit pas de troisième path configurable pour .env; il n'est ni un substitut de cfgpath ni de schemapath.
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.
7. Contrat commun des documents JSON
Chaque document JSON KSP est chargé à partir de son file_id résolu par le registre, puis possède une version de format :
{
"format_version": 1
}
Politique :
format_versionobligatoire ;- version inconnue refusée ;
- JSON syntaxiquement invalide refusé ;
- validation par le schema référencé par le descriptor du
file_idavant 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.
Le modèle ne doit pas être réduit à la surface actuelle « une console + un fichier » de LoggingSettings 0.1.2. Il doit conserver les besoins déjà connus : plusieurs profils, console configurable, plusieurs sorties fichier simultanées et routing par target/domain/niveau.
Structure conceptuelle candidate révisée :
{
"format_version": 1,
"logs_directory": "${KSP_LOGS_DIRECTORY:-logs}",
"default_profile": "local_dev",
"profiles": [
{
"profile_id": "local_dev",
"default_filter": "warn",
"span_events": "new_and_close",
"console": {
"enabled": true,
"output": "stderr",
"ansi": true,
"format": "compact",
"filter": {
"level": "debug",
"targets": ["*"],
"domains": ["*"]
}
},
"files": [
{
"output_id": "file.all.debug",
"enabled": true,
"path": "debug.log",
"rotation": "daily",
"format": "human",
"ansi": false,
"filter": {
"level": "debug",
"targets": ["*"],
"domains": ["*"]
}
},
{
"output_id": "file.config.error",
"enabled": true,
"path": "config/error.jsonl",
"rotation": "daily",
"format": "json",
"ansi": false,
"filter": {
"level": "error",
"targets": ["ksp-config-lib"],
"domains": ["config"]
}
}
],
"target_filters": []
}
]
}
Ce JSON reste conceptuel jusqu'à validation du contrat public Logging correspondant. Il fixe néanmoins les capacités attendues.
Décisions :
logs_directoryest global, hors profils ;- son fallback est exprimé dans le document au point d'usage ;
- après interpolation,
logs_directorypeut être absolu ou relatif ; un chemin relatif est interprété relativement au current working directory du processus qui initialise Logging, et non automatiquement relativement au répertoire du binaire ; - un
KSP_LOGS_DIRECTORYexplicitement présent mais invalide n'active jamais le fallback : le fallback reste réservé à l'absence de la variable et l'adapter Config -> Logging doit retourner un diagnostic de configuration effective invalide ; default_profileest global et référence unprofile_idexistant ;profile_idest obligatoire et unique dans le document ;default_filtergouverne le comportement général du profil ;- la console est un sink spécialisé avec
enabled,output,ansi,formatet son filtre ; files[]peut contenir zéro, un ou plusieurs sinks fichier ;- chaque sink fichier possède un
output_idunique dans le profil ; - le nom
output_idest volontairement distinct dufile_idglobal de Config afin de ne pas confondre identité d'un document Config et identité d'une sortie Logging ; pathd'un sink fichier est relatif àlogs_directorysauf contrat futur explicite ;- chaque sink peut filtrer au minimum par niveau et par target ;
- KSP doit également pouvoir sélectionner par
domainpuisque les macros KSP utilisent déjà ce champ structuré ; - wildcard
*signifie « tous » dans la dimension concernée ; rotationdoit au moins conservernever/hourly/daily; d'autres politiques ne sont ajoutées qu'après vérification du backend ;formatdoit au moins couvrir les formats utiles retenus (human,compact,pretty,json) si Logging peut les offrir proprement ;ansidoit être configurable pour la console et doit resterfalse/sans séquences persistées pour les fichiers ;target_filtersreste disponible pour la politique générale de takeover/overrides et n'est pas remplacé par les filtres de sink ;- Config valide le document puis construit des settings/sinks publics Logging ;
- la validation propre à Logging reste l'ultime validation de son contrat runtime.
8.1 Écart ksp-logging-lib 0.1.2 à fermer
| Capacité | 0.1.2 |
Requise par std.logging.json |
|---|---|---|
| filtre global | oui | oui |
| overrides par target | oui | oui |
| lifecycle spans | oui | oui |
| console stdout/stderr | oui | oui |
| console enabled | via Option |
oui explicite |
| console ANSI configurable | non | oui |
| format console configurable | non | oui |
| plusieurs fichiers | non | oui |
| rotation par fichier | un seul fichier | oui par sink |
| format par fichier | non | oui |
| filtre par sink/target | non | oui |
| filtre par sink/domain | non | oui |
| filtre par sink/niveau | non indépendant | oui |
| hot reload transactionnel | oui | à conserver |
| non-blocking/guards/drop counters | oui | à conserver et généraliser par sinks |
Ce tableau est un gap identifié, pas une invitation à déplacer Logging dans Config. La tranche qui le ferme modifie ksp-logging-lib uniquement dans son domaine propriétaire.
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 :
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 :
globals
+ selected profile
+ environment substitutions contained in those values
Le profil ne recopie pas artificiellement les globals.
Sans composition :
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 et les référence par file_id logique, jamais par filename.
Structure conceptuelle :
{
"format_version": 1,
"default_profile": "default",
"documents": {
"logging": {
"file_id": "cfg.std.logging"
}
},
"profiles": [
{
"profile_id": "default",
"documents": {
"logging": {
"profile_id": "local_dev"
}
}
}
]
}
Principes :
- la composition référence des documents spécialisés par
file_id; - le registre résout ensuite ce
file_idvers le filename effectif ; - un
--filemap=cfg.std.logging=...remplace donc le fichier physique sans modifier le composite ; - chaque document conserve ses globals ;
- une composition peut choisir le profil d'un document ;
- sans choix explicite, le
default_profiledu 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, validé et résolu par Config ;
file_id/profil inexistant = erreur ;- aucun JSON patch générique n'est introduit en
0.1.3sans besoin concret.
Cette structure répond au besoin « un composite pioche dans les documents et leurs profils/paramètres » tout en rendant les filenames remplaçables au bootstrap.
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 :
KSP_SECRET_*
KSP_PUBLIC_*
KSP_*
KSPB_SECRET_*
KSPB_PUBLIC_*
KSPB_*
Classification :
KSP_SECRET_* / KSPB_SECRET_* -> Secret
KSP_PUBLIC_* / KSPB_PUBLIC_* -> Public
autre KSP_* / KSPB_* -> Internal
Ordre de sensibilité :
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 :
1. environnement réel du processus
2. .env
3. fallback déclaré au point d'usage
4. missing
Donc :
export KSP_PUBLIC_FOO=console
est prioritaire sur :
KSP_PUBLIC_FOO=dotenv
et les deux sont prioritaires sur :
${KSP_PUBLIC_FOO:-fallback}
11.3 Sémantique du fallback
Syntaxes initiales :
${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 :
{
"url": "https://mainnet.helius-rpc.com/?api-key=${KSP_SECRET_HELIUS_API_KEY}"
}
Le resolver doit pouvoir produire :
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 :
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 :
logging.logs_directory -> KSP_LOGS_DIRECTORY
La déclaration :
"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 :
${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 :
name
fallback: Option<String>
avec exactement la même priorité :
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 :
ResolvedValue<T>
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 :
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
Debuggé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 :
{
"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 :
- produire un diagnostic structuré
missing environment variable without fallback; - émettre un
warnsous le targetksp-config-liblorsque Logging est disponible ; - inclure le nom de variable, le document et le chemin JSON ;
- ne jamais inclure une valeur secrète ;
- 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 :
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 :
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 :
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 :
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 :
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 :
ProcessEnvironment
DotEnv
Fallback
et choisit la source effective selon la priorité définie.
18.2 Mutation persistante
La surface management de Config doit pouvoir :
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 :
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 ;
.envcomme 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 :
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 :
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 :
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 Config sont bornés par le cfgpath effectif et les schemas par le schemapath effectif.
Un mapping file_id -> filename ne peut pas transformer un filename en chemin absolu ni sortir de sa racine par ...
Les compositions ne fournissent aucun chemin physique : elles référencent uniquement des file_id.
Le .env est une source distincte fixée à ./.env pour 0.1.3; il ne sert pas à relocaliser cfgpath ou schemapath.
20. Surface runtime, diagnostic et management
Trois intentions doivent être distinguées :
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
Secretpour affichage/édition légitime ; - voir qu'une valeur
.envest 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
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 :
Config resolve
-> LoggingSettings
-> ksp_logging_lib::initialize(...)
-> conserve LoggingGuard
Puis après modification :
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 deksp-app-config-deskles possède ; - aucun singleton global Config n'est introduit.
Target Config :
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 :
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.invalid_bootstrap_option
config.invalid_config_path
config.invalid_schema_path
config.unknown_file_id
config.duplicate_file_id
config.invalid_file_mapping
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.002.
Le choix de base est désormais clair :
serdepour les structures source typées et la serialization/deserialization ;serde_jsonpour le JSON source, les valeurs intermédiaires et la persistence ;jsonschemapour la validation JSON Schema ;- éventuellement une primitive d'écriture atomique lorsque la tranche de persistence l'introduit ;
- é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.
Vérification effectuée pendant pre.001-fix.002 le 15 août 2026 :
serde 1.0.229 -> génération candidate ^1.0
serde_json 1.0.151 -> génération candidate ^1.0
jsonschema 0.49.6 -> génération candidate ^0.49
Ces versions ne sont pas ajoutées par ce fix documentaire. Elles doivent être revérifiées au delta qui introduit réellement chaque dépendance, conformément aux règles Cargo KSP.
jsonschema est retenu comme moteur candidat principal plutôt qu'une validation maison. Son API actuelle sait construire des validators réutilisables et gérer les drafts JSON Schema ; std.logging.schema.json devra déclarer explicitement le draft KSP retenu.
Les dépendances externes communes sont déclarées uniquement sous [workspace.dependencies], puis consommées avec .workspace = true.
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 :
src/lib.rs
src/error.rs
src/bootstrap.rs
src/file_registry.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 :
ConfigBootstrapOptions
ConfigPath
SchemaPath
ConfigFileId
ConfigFileKind
ConfigFileDescriptor
ConfigFileRegistry
ResolvedConfigFile
ConfigSession
ConfigDocumentKind
ConfigSource
CompositionDocument
CompositionProfile
EnvironmentSensitivity
EnvironmentSource
EnvironmentRequest
EnvironmentReference
ResolvedValue<T>
ConfigValueOrigin
ConfigDiagnostic
ConfigChangeReport
ConfigManagementAccess
LoggingConfigDocument
LoggingProfileConfig
LoggingOutputConfig
LoggingOutputFilterConfig
ResolvedLoggingConfig
Le registre doit exposer au minimum :
lookup(file_id)
resolve_filename(file_id)
override_filename(file_id, filename)
avec validation du kind/racine et refus d'un file_id inconnu.
Cette liste n'impose pas de tout créer dans pre.002. Le découpage des prereleases doit respecter la règle KSP de petites tranches : une prerelease estimée au-delà d'environ 15–20 minutes de travail effectif doit être scindée avant ou pendant son exécution plutôt que comprimée artificiellement.
26. Audits et tests à introduire
26.1 Ownership
Empêcher les accès applicatifs directs hors Config :
std::env::var,var_os,varspourKSP_*/KSPB_*;- loaders dotenv directs ;
- lecture/écriture directe des documents
config/std.*.jsonetconfig/composite.*.json; - filenames Config/schema codés dans les consumers au lieu d'un
file_id; - parsing divergent de
--cfgpath,--schemapathou--filemaphors de la surface Config prévue ; - 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 Bootstrap/registre/documents/schemas
Tester :
- défauts
configetconfig/schemas; --cfgpathseul n'altère passchemapath;--schemapathseul n'altère pascfgpath;- aucune variable env/
.env/JSON ne peut changer ces deux paths ; - lookup d'un
file_idconnu ; file_idinconnu refusé ;- override de filename connu ;
- override de schema filename indépendant ;
- filename absolu/traversal refusé ;
- composite inchangé lorsque le mapping physique est remplacé ;
- JSON invalide ;
- schema invalide ;
- format version absent/inconnu ;
- globals hors profils ;
default_profile;- profils dupliqués ;
- profil absent ;
- composition valide/invalide.
26.3 Environnement
Tester :
process > .env > fallback
ainsi que :
- variable process seule ;
- variable
.envseule ; - 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é ;
.envinvalide ;- commentaires/quotes du
.envselon 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 :
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é :
https://provider.invalid/?api-key=${KSP_SECRET_TEST_KEY}&network=${KSP_PUBLIC_NETWORK:-mainnet}
avec résultat sûr attendu du type :
https://provider.invalid/?api-key=********&network=mainnet
26.5 Persistence
Tester :
- candidate JSON invalide n'altère pas le fichier ;
- candidate
.envinvalide 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 et non-régression
Tester :
logs_directoryglobal avec fallback ;- unicité des
profile_id; - sélection de profil ;
- console enabled/output/ANSI/format ;
- zéro/un/plusieurs sinks fichier ;
- unicité des
output_id; - rotation/format de chaque fichier ;
- routing par target ;
- routing par domain ;
- niveau par sink ;
- target filters globaux ;
- combinaison wildcard/spécialisation ;
- hot reload conservé ;
- writers/guards/drop counters conservés ou généralisés proprement ;
- validation publique Logging ;
- changement Config sans reconfiguration implicite ;
LoggingGuardreste orchestration-owned ;- canary démontrant qu'aucun comportement utile déjà offert par la configuration bot3 n'est perdu silencieusement.
27. Découpage souple des prereleases
Le découpage ci-dessous applique explicitement la règle KSP de granularité : chaque tranche vise un objectif cohérent réalisable en environ 15–20 minutes de travail effectif. Si l'audit ou l'implémentation montre qu'une tranche dépasse sensiblement ce budget, elle doit être scindée par un delta explicite plutôt que compressée.
0.1.3-pre.001 + pre.001-fix.001/.002/.003 — audit, brainstorming et plan corrigé
- audit
v0.1.2; - audit historique ciblé ;
- choix du propriétaire unique Config ;
- correction vers
.envconventionnel ; - interpolation
${...}+ fallback ; - modèle real/safe/provenance/sensitivity ;
- registre
file_id -> filename+ schemas ; - bootstrap
cfgpath/schemapathnon récursif ; - audit de non-régression Logging et modèle
std.logging.jsonenrichi ; - choix
serde/serde_json/jsonschemacomme base candidate ; - regranularisation du développement pour respecter le budget des petites tranches ;
- aucune crate/dépendance fonctionnelle Config ajoutée.
0.1.3-pre.002 — crate Config + bootstrap des racines
Objectif unique : créer la crate et fixer comment Config trouve ses deux racines avant toute lecture de document.
- créer
ksp-config-lib; - dépendances KSP minimales réellement utilisées ;
- erreurs Config initiales strictement nécessaires ;
ConfigBootstrapOptions;- défauts hardcodés
config/config/schemas; - parsing/contrat
--cfgpathet--schemapath; - validation des deux paths ;
- tests unitaires du bootstrap ;
- aucun registre
file_iddans cette tranche.
Implémentation candidate livrée par pre.002 :
ksp-config-libentre dans le workspace avecksp-core-libcomme seule dépendance actuellement utilisée ;ksp-logging-libreste une dépendance architecturale future de Config mais n'est pas ajoutée tant qu'aucun événement Config ne l'utilise réellement ;DEFAULT_CFG_PATH = "config"etDEFAULT_SCHEMA_PATH = "config/schemas"sont codés dans Config ;ConfigBootstrapOptions::defaults()valide les deux défauts ;ConfigBootstrapOptions::from_paths(...)fournit l'équivalent programmatique explicite sans dépendre des chemins par défaut ;ConfigBootstrapOptions::from_args(...)accepte--cfgpath=value,--cfgpath value,--schemapath=valueet--schemapath value, ignore les arguments étrangers et applique le dernier override explicite d'un même path ;- un path inexistant est accepté à ce stade, un path vide ou un path existant non répertoire est refusé ;
- le registre
file_id,--filemap, JSON, schemas, profils et environnement restent strictement hors depre.002.
Validation utilisateur acquise après pre.002-fix.001 : cargo fmt --all, cargo check --workspace, cargo clippy --workspace --all-targets, cargo test --workspace et les trois vues cargo tree demandées sont propres. La seule alerte initiale missing_docs du test d'intégration a été corrigée par pre.002-fix.001 puis revalidée sans warning.
0.1.3-pre.003 — registre logique file_id -> filename
Objectif unique : rendre les noms physiques remplaçables sans modifier l'identité logique des fichiers.
ConfigFileId;ConfigFileKind;ConfigFileDescriptor;ConfigFileRegistry;- mappings par défaut des documents/schemas connus ;
- parsing/contrat répétable
--filemap=<file_id>=<filename>; - lookup/résolution/override du filename ;
- validation d'unicité, kind/racine, filename relatif et traversal ;
- tests du registre et des overrides ;
- aucune lecture JSON/schema encore nécessaire.
Implémentation candidate livrée par pre.003 :
ConfigFileIdpossède l'identité logique stable et valide une nomenclature ASCII minuscule segmentée par.;ConfigFileKind::{Config, Schema}détermine exclusivement la racine physique utilisée ;ConfigFileDescriptorconservefile_id, kind et filename relatif ;ConfigFileRegistry::defaults()enregistre uniquement les deux fichiers actuellement connus :cfg.std.logging -> std.logging.jsonetschema.std.logging -> std.logging.schema.json;- un descriptor Config doit utiliser un
file_idcfg.*, un descriptor Schema unfile_idschema.*; ConfigFileRegistry::from_args(...)possède le parsing répétable--filemap=<file_id>=<filename>, ignore les arguments étrangers et applique le dernier override d'un même ID ;- un override ne peut pas créer un ID inconnu : il remplace uniquement le filename d'un descriptor possédé par Config ;
ConfigFileRegistry::with_filename_override(...)fournit le même mécanisme par API programmatique ;- les filenames doivent rester relatifs à leur root, sans composant
./.., préfixe ou racine absolue ; les sous-répertoires relatifs normaux restent autorisés ; ConfigFileRegistry::resolve_path(...)joint le filename àcfgpathouschemapathselon le kind, sans lire le fichier ;- l'unicité des
file_id, les erreurs d'ID/mapping et les overrides config/schema sont couverts par tests unitaires et API publique ; - aucune dépendance externe, lecture JSON, validation schema ou
.envn'est introduite par cette tranche.
Validation utilisateur acquise pour pre.003 : cargo fmt --all, cargo check --workspace, cargo clippy --workspace --all-targets et cargo test --workspace sont propres, avec 21 tests unitaires Config et 4 tests d'API publique Config réussis.
0.1.3-pre.004 — ksp-logging-lib : modèle public multi-output
Objectif unique : compléter les contrats/settings publics de Logging sans encore implémenter tout le routing runtime.
- représenter une console configurable ;
- représenter zéro/un/plusieurs outputs fichier ;
output_idunique ;- level/filter par output ;
- sélection target et domain ;
- format/ANSI/rotation selon la nature du sink ;
- validation publique des settings ;
- préserver la façade/lifecycle existants ;
- aucun accès Config depuis Logging ;
- tests des contrats/settings.
Cette tranche ne doit pas être gonflée par le runtime multi-sink complet. Si même le modèle public dépasse le budget, il est scindé avant de poursuivre.
Implémentation candidate livrée par pre.004 :
LogFormat::{Human, Compact, Pretty, Json}devient le contrat de format public ;OutputFilterporte un niveau et des listes explicites de selectors target/domain, avec*comme wildcard exclusif d'une dimension ;- les selectors target restent bornés aux targets KSP (
ksp-*) ; ConsoleSettingsporte explicitementenabled,output,ansi,formatetfilter;FileSettingsporteoutput_id,enabled, destination actuelledirectory/file_name_prefix, rotation, format, ANSI et filtre ;- les
output_idfichier sont uniques, ASCII minuscules segmentables par., et distincts desfile_idConfig ; - les fichiers persistants refusent
ansi = true; LoggingSettingsreprésente désormaisfiles: Vec<FileSettings>au lieu d'un seul fichier optionnel, tout en conservantdefault_filter,TargetFilter,SpanEventset la console ;validate()valide le modèle multi-output indépendamment du backend runtime ;- les helpers
ConsoleSettings::stdout/stderr()conservent un profil compatible avec le runtime historique ; - jusqu'à
pre.005,initialize/reinitializerefusent explicitement les nouvelles capacités qu'ils ne savent pas encore appliquer (plusieurs fichiers actifs, formats enrichis, ANSI console ou routing par output) plutôt que de les ignorer silencieusement ; - aucune dépendance Config n'entre dans Logging et aucune dépendance externe supplémentaire n'est ajoutée.
pre.004 a été validée par l'utilisateur le 2026-08-15 avec cargo fmt --all, cargo check --workspace, cargo clippy --workspace --all-targets, cargo test --workspace et les trois vues cargo tree -p ksp-logging-lib demandées, sans warning ni échec.
0.1.3-pre.005 — ksp-logging-lib : runtime multi-sink + routing metadata
Objectif unique : activer derrière les contrats de pre.004 les capacités runtime bornées aux métadonnées directement disponibles par output.
- 0..N fichiers simultanés ;
- console indépendante ;
- routing/filter par output sur level + target ;
- rotation/format
Human/Compact/Pretty/Json; - ANSI console configurable, jamais persisté dans les fichiers ;
- writers non bloquants et guards indépendants ;
- compteurs agrégés conservés + compteur cumulatif fichier par
output_id; - conserver subscriber unique et takeover global KSP ;
- conserver
initialize/reinitializetransactionnel ; - conserver le comportement de saturation ;
- tests multi-sink, formats, routing metadata et hot reload.
Décision de scission prise pendant l'implémentation : domain est un champ structuré et ne doit être ni assimilé au target ni filtré approximativement au niveau du writer. pre.005 refuse donc explicitement tout output actif avec domains != ["*"].
Implémentation candidate de pre.005 :
- chaque formatter possède son writer routé par
OutputFilter.level+OutputFilter.targets; - la politique globale
default_filter+TargetFilterreste composée devant tout le groupe de sinks ; - les layers reloadables ne deviennent pas des
Filtered, afin de préserver la composition/hot reload stabilisée en0.1.2; tracing-subscriberactive ses featuresjsonetansipour les formats publics déjà décidés enpre.004;LoggingGuard::dropped_file_lines(output_id)expose le compteur cumulatif d'un fichier à travers les reloads ;DroppedLinesconserve la vue agrégée console/fichier ;- JSON + ANSI console est refusé comme combinaison incohérente ;
- aucune dépendance Config n'entre dans Logging.
pre.005 a d'abord révélé une assertion d'intégration JSON incorrecte : le message de test contenait lui-même une séquence ANSI que le formatter JSON sérialisait comme donnée échappée. pre.005-fix.001 a séparé le test JSON du test de stripping ANSI sans modifier le runtime. L'utilisateur a ensuite validé cargo fmt --all, cargo check --workspace, cargo clippy --workspace --all-targets, cargo test --workspace et les trois vues cargo tree -p ksp-logging-lib sans warning ni échec.
0.1.3-pre.006 — ksp-logging-lib : routing structuré domain
Objectif unique : fermer proprement la dimension OutputFilter.domains[] avant que Config ne fige le schema Logging.
Implémentation candidate livrée par pre.006 :
domainreste un champ structuré distinct du target et n'est jamais converti en pseudo-target ;- une couche interne
DomainContextLayercapture ledomaineffectif avant les formatters de sortie ; - un event portant directement
domainremplace le domain hérité pour cet event ; - un event sans
domainhérite du domain effectif de son span ; - un span portant
domaindéfinit son domain effectif ; - un span sans
domainhérite du domain effectif de son parent au moment de sa création ; - les lifecycle events
NEW/ENTER/EXIT/CLOSEutilisent le domain effectif du span concerné ; domains = ["*"]reste l'absence de restriction et accepte aussi les events/spans sans domain ;- un selector domain nommé correspond par préfixe et ne sélectionne pas une entrée sans domain ;
- le writer applique conjointement
level,targetet le domain effectif ; - la politique globale de takeover KSP, le subscriber unique, le hot reload transactionnel et les writers non bloquants restent inchangés ;
- le contexte domain est thread-local interne au dispatch Logging et n'entre pas dans les contrats publics ;
- les tests couvrent domain direct, absence de domain, héritage parent/enfant, override d'event, override de span enfant, lifecycle et trois sinks domain distincts.
Aucune dépendance externe ou feature Cargo supplémentaire n'est nécessaire pour cette tranche. pre.007 peut donc construire std.logging.schema.json sur une surface Logging qui représente et exécute réellement les trois dimensions de routing retenues : level, target et domain.
La validation utilisateur de pre.006 est acquise : fmt/check/clippy/test et les trois graphes Cargo demandés sont propres.
0.1.3-pre.007 — JSON/JSON Schema + premier document Logging
Tranche livrée :
serde ^1.0,serde_json ^1.0etjsonschema ^0.49sont déclarés au workspace puis consommés avec.workspace = trueparksp-config-lib; les versions observées au moment de l'ajout sont respectivement1.0.229,1.0.151et0.49.9;jsonschemaest consommé avecdefault-features = false:0.1.3n'a besoin d'aucune récupération HTTP/file de références externes pour le schema Logging autonome ;ConfigFileDescriptorpossède désormais l'association logique optionnelleschema_file_id;cfg.std.loggingréférenceschema.std.logging;- le registre vérifie qu'un schema référencé existe réellement et possède
ConfigFileKind::Schema; ConfigDocumentEnginecentralise la lecture des JSON enregistrés, la validation du document schema contre son meta-schema, la validation de l'instance puis les invariants sémantiques propres au document ;- les diagnostics distinguent lecture impossible, syntaxe JSON invalide, schema invalide, échec de validation schema et invalidité sémantique ;
config/std.logging.json,config/schemas/std.logging.schema.jsonetconfig/examples/std.logging.example.jsonconstituent la première surface Config réelle ;- le schema est JSON Schema Draft 2020-12 et reflète la surface Logging stabilisée : filtre global, lifecycle spans, console, multi-fichiers,
output_id, rotation, formats, ANSI et routinglevel/target/domain; - le document source contient déjà
logs_directory,default_profileetprofiles[], maispre.007ne résout encore aucun profil et ne vérifie pas encore l'unicité desprofile_idni quedefault_profileréférence un profil existant ; ces responsabilités restent explicitement àpre.008; - la validation sémantique de base couvre notamment les
output_idfichier, chemins relatifs souslogs_directory, ANSI fichier interdit, incompatibilité ANSI+JSON console, selectors wildcard/target KSP et unicité desoutput_iddans un profil ; - aucune interpolation
${...}n'est encore exécutée :${KSP_LOGS_DIRECTORY:-logs}reste une string source schema-valide jusqu'au resolver depre.010.
Aucune dépendance à ksp-logging-lib n'est encore nécessaire dans Config : le mapping vers LoggingSettings reste réservé à pre.012.
0.1.3-pre.008 — globals + profils + default_profile
Tranche livrée :
- tout document standard possédant
default_profile+profilesest soumis au contrat générique de profils Config ; - les
profile_iddoivent être uniques dans le document ; default_profilereste une propriété globale autonome et doit référencer exactement unprofile_idexistant ;ConfigDocumentEngine::load_resolved_profile(file_id, requested_profile)sélectionne soit le profil par défaut (None), soit un profil explicite (Some(profile_id)) ;- une sélection explicite inconnue retourne
config.profile_not_foundsans rendre le document source invalide ; ResolvedConfigProfileconserve lefile_id, le path source, leprofile_idsélectionné, la source de sélection, les globals, le profil et une vue effective ;- les globals sont toutes les propriétés top-level hors clés réservées
default_profileetprofiles; - la vue effective fusionne les globals puis les propriétés du profil sélectionné ; une clé du profil, si elle existe aussi globalement dans un futur schema, est déterministement prioritaire ;
ConfigValueOrigin::{Global, Profile}conserve la provenance top-level de chaque clé effective ;- aucune interpolation
${...}, aucun override composite et aucune sensibilité ne sont encore appliqués à cette vue ; ces couches seront ajoutées sans faire perdre la provenance déjà portée ; - les tests couvrent le profil par défaut commité, la sélection explicite, la provenance global/profile, un profil demandé absent, les
profile_iddupliqués et undefault_profileorphelin.
La validation utilisateur de pre.007 est acquise : fmt/check/clippy/test passent, le moteur Config compte 28 tests unitaires + 5 tests publics et jsonschema avec default-features = false n'introduit pas de stack HTTP/TLS. Le seul doublon signalé par cargo tree -d est syn 2/syn 3, transitif via l'écosystème jsonschema et sans duplication de crate runtime KSP à corriger dans cette tranche.
0.1.3-pre.009 — compositions génériques par file_id
Tranche livrée :
schema.compositeest enregistré par défaut versconfig/schemas/composite.schema.jsonet reste surchargeable par--filemap;- aucun
cfg.composite.<consumer>n’est enregistré tant qu’un consumer concret n’existe pas ; config/examples/composite.example.jsonexerce le contrat sans devenir une source runtime implicite ;- un composite utilise
format_version,default_profileetprofiles[]comme les documents standards profilés ; - chaque profil composite contient
documents[]aveccomponent_id,file_idetprofile_idoptionnel ; component_idest unique dans un profil composite ;- les références ciblent exclusivement des
cfg.std.*déjà enregistrés, jamais des filenames ni d’autres composites ; profile_idabsent laisse le document référencé choisir sondefault_profileautonome ;profile_idprésent impose ce profil et marque sa source de sélectionConfigProfileSelectionSource::Composite;ResolvedConfigCompositeconserve le composite sélectionné et sesResolvedCompositeComponent, chacun portant leResolvedConfigProfilecomplet du document référencé ;- les provenances
Global/Profiledu document spécialisé sont donc conservées sous la composition ; ConfigDocumentEngine::load_resolved_composite(file_id, requested_profile)est prêt pour les futurs descriptorscfg.composite.<consumer>;- les tests utilisent un descriptor composite privé à la crate pointant vers l’exemple versionné afin de valider la résolution complète sans créer un composite runtime fictif.
La validation utilisateur de pre.009-fix.001 est acquise : fmt/check/clippy/test passent, les 39 tests unitaires Config et 7 tests publics passent, et le graphe de dépendances reste conforme avec le seul doublon transitif syn 2/syn 3 déjà connu via jsonschema.
0.1.3-pre.010 — .env + process env + resolver ${...}
Tranche livrée :
ConfigEnvironment::load()capture les variables KSP/KSPB du processus puis lit./.envsans modifier l'environnement externe ;- l'absence de
.envest valide et équivaut à une source locale vide ; les autres erreurs de lecture sont distinctes ; - priorité effective process >
.env> fallback, y compris le cas d'une chaîne vide explicitement définie ; ConfigEnvironmentValueexpose la valeur réelle et sa sourceProcess/DotEnv/Fallbacksans implémentationDebugafin de ne pas créer une fuite accidentelle avant le contrat de redaction depre.011;${NAME}et${NAME:-fallback}sont résolus dans les strings, maps et valeurs JSON récursives ; plusieurs placeholders sont supportés ;- le fallback reste littéral et n'est pas récursivement interprété dans cette tranche ;
- une variable manquante sans fallback retourne
config.environment_variable_missinget émet un warning viaksp-logging-libavec targetksp-config-lib, sans valeur dans le diagnostic ; - seuls les namespaces
KSP_*etKSPB_*sont acceptés par l'API Config ; - le parser
.envsupporte commentaires,export, valeurs non quotées/simplement/doublement quotées et refuse les doublons KSP ambigus ; - les tests de priorité process utilisent des sources injectées/itérateurs synthétiques : ils ne mutent jamais le vrai environnement du processus, ce qui évite
unsafeen Rust 2024 ; ResolvedConfigProfile::resolve_effective_environment()résout la vue effective sans modifier le profil source ni sa provenance Global/Profile ;ksp-config-libdépend désormais réellement deksp-logging-libpour ses warnings Config ; la dépendance inverse reste interdite ;.env.exampleest créé à la racine avecKSP_LOGS_DIRECTORY, seule variable runtime actuellement utilisée ;- la règle durable impose désormais d'ajouter toute nouvelle variable runtime KSP/KSPB à
.env.example, avec commentaire d'usage, dans le même delta que sa première utilisation ; .gitignorepossédait déjà la règle correcte.env,.env.*,!.env.example; aucun changement n'est nécessaire.
La validation utilisateur de pre.010-fix.001 est acquise : fmt/check/clippy/test passent, les 53 tests unitaires Config et 8 tests publics passent, et le graphe Cargo reste conforme avec le doublon transitif syn 2/syn 3 déjà connu via jsonschema.
0.1.3-pre.011 — sensibilité + valeurs real/safe/provenance
Tranche livrée :
ConfigSensitivity::{Public, Internal, Secret}dérive la classification directement des namespaces KSP/KSPB et conserve l'ordreSecret > Internal > Public;ConfigEnvironmentValueconserve désormais valeur réelle, valeur sûre, sensibilité, source et provenance ; sonDebugutilise uniquement la valeur sûre ;ResolvedConfigTextconserve la chaîne réelle, sa représentation sûre, la sensibilité la plus forte et une provenance ordonnée ;- les segments issus de
KSP_SECRET_*/KSPB_SECRET_*sont remplacés par********dans la représentation sûre, y compris lorsqu'ils viennent d'un fallback ; - les chaînes composées propagent la sensibilité la plus forte des placeholders utilisés ; une chaîne sans placeholder est
Internal; ConfigValueProvenancedistingueDocumentLiteral,EnvironmentProcess,EnvironmentDotEnvetEnvironmentFallbacksans embarquer les valeurs elles-mêmes ;ResolvedConfigJsonconserve arbres réel/sûr, sensibilité agrégée et provenance indexée par JSON Pointer ;- les APIs existantes
resolve_text,resolve_jsonetresolve_effective_environmentrestent compatibles et retournent uniquement les valeurs réelles ; les variantes*_detailedexposent le contrat enrichi ; - la provenance Global/Profile déjà portée par
ResolvedConfigProfilereste distincte et inchangée ;resolve_effective_environment_detailedajoute la provenance de la couche environnement ; - des canaries de tests vérifient qu'une valeur secrète process, fallback ou imbriquée dans un JSON n'apparait pas dans
safe_valueni dansDebug; - aucune nouvelle dépendance Cargo et aucune nouvelle variable d'environnement ne sont introduites dans cette tranche.
0.1.3-pre.012 — adapter Config -> Logging
Tranche livrée :
ResolvedLoggingConfigconservefile_id, source path, profil sélectionné, source de sélection, arbre effectif détaillé real/safe/provenance, root Logging résolu etksp_logging_lib::LoggingSettings;ConfigDocumentEngine::load_resolved_logging_config(requested_profile, environment)chargecfg.std.logging, sélectionne le profil, applique process >.env> fallback puis convertit explicitement tous les contrats Logging ;- mapping complet de
LogFilterLevel,SpanEvents,ConsoleOutput,LogFormat,FileRotation,OutputFilter,TargetFilter, console et multi-fichiers ; files[].pathest séparé en directory relatif + file-name prefix pourFileSettings, sous unlogs_directorycommun ;logs_directoryabsolu est conservé ; relatif, il est ancré surstd::env::current_dir()au moment de l'adaptation ; un root inexistant est accepté pour permettre à Logging de le créer ; un path existant non-directory ou impossible à inspecter est rejeté ;- une valeur explicite
KSP_LOGS_DIRECTORY=vide est une valeur présente et produitconfig.effective_config_invalid: aucun fallback silencieux verslogs; - les
files[].pathsont revalidés après interpolation et doivent rester relatifs sans./../root/prefix, empêchant une variable d'environnement de faire sortir un sink du root Logging ; - l'adapter rejette toute configuration Logging effective dont la sensibilité agrégée est
Secret; Logging n'a aucun besoin fonctionnel de secrets et ses diagnostics filesystem ne doivent jamais recevoir de valeur secrète ; - les erreurs de validation finales de
LoggingSettingssont encapsulées par Config avec uniquement le code Logging stable, sans recopier les contextes runtime susceptibles de contenir des valeurs effectives ; ResolvedLoggingConfigpossède unDebugmanuel qui expose l'arbre effectif via sa représentation sûre et n'affiche ni les settings réels ni le root réel séparément ;- un test démontre que les settings issus de Config peuvent réellement
initializepuisreinitializeksp-logging-lib; leLoggingGuardreste détenu par l'orchestration/test, jamais stocké comme singleton Config ; - l'initialisation Logging crée les sous-répertoires de sinks configurés lorsqu'ils n'existent pas ;
- aucune nouvelle variable d'environnement, aucune modification de
.env.example, aucune nouvelle dépendance Cargo.
La validation utilisateur de pre.011-fix.001 est acquise le 2026-08-16 : fmt/check/clippy/test passent, ksp-config-lib compte 61 tests unitaires + 9 tests publics, et le graphe conserve uniquement le doublon transitif syn 2/syn 3 déjà connu via jsonschema; ksp-logging-lib -d reste sans doublon.
0.1.3-pre.013 — management + persistence JSON/.env
Livré :
ConfigManagementcomme façade explicite de management, sans dépendance Tauri ;- lecture source brute d'un document enregistré par
file_id, même si le document est schema-invalide, afin de permettre sa correction sans ouvrir un path arbitraire ; - contrat source typé
LoggingConfigDocument+ sous-structures pourstd.logging.json, avec mutation en mémoire puis validation schema/sémantique complète avant commit ; - serialization JSON lisible avec newline final ;
- persistence atomique par fichier temporaire dans le même répertoire +
sync_all+ rename ; - preservation des permissions existantes et mode
0600pour un.envnouvellement créé sur Unix ; environment_report()exposant desired.env, effective process/.env, sensitivity, source et shadowing uniquement sous forme sûre/redacted ;reveal_effective_environment_value()/reveal_dotenv_value()comme opt-in explicite au réel pour une UI management autorisée ;- create/update/remove
.envpour les namespaces KSP/KSPB seulement, sans aucune mutation du process env ; ConfigEnvironmentChangeReportavecsource_changed,effective_changed,shadowed_by_process_environment,reload_required;- preservation des lignes/commentaires
.envnon ciblés et encodage sûr des valeurs modifiées ; - un
.envsyntaxiquement invalide ou un candidate JSON invalide est refusé avant commit et conserve le fichier précédent ; - aucune nouvelle dépendance ni variable d'environnement.
La validation utilisateur de pre.012 est acquise le 2026-08-16 : fmt/check/clippy/test passent, ksp-config-lib compte 70 tests unitaires + 10 tests publics, ksp-logging-lib 34 tests unitaires + ses intégrations, et son graphe -d reste sans doublon. Le seul doublon Config reste le syn 2/syn 3 transitif déjà connu via jsonschema.
0.1.3-pre.014 — ownership audits + robustesse
Livré :
- nouveau test d'intégration
tests/ownership.rsexécuté parcargo test --workspace; - audit de direction garantissant que
ksp-core-libetksp-logging-libne dépendent pas deksp-config-lib; - audit des crates hors Config interdisant les lectures directes littérales
KSP_*/KSPB_*viastd::env::var/var_osainsi que l'énumération directevars/vars_os; - audit interdisant hors Config les noms physiques gérés
.env,std.logging.json,std.logging.schema.jsonetcomposite.schema.json, afin que les consumers utilisent les contrats/constantes/file_id Config ; - audit automatique de toutes les variables KSP/KSPB concrètes présentes dans les JSON de
config/et le code Rust production ; - comparaison de cet inventaire avec
.env.example, en exigeant une assignment versionnée (active ou commentée) et un commentaire explicatif précédent ; - réaudit manuel de la matrice existante : documents/schema invalides,
.envinvalide/dupliqué, placeholders invalides/manquants, file mappings invalides, profils/composites invalides, chemins Logging effectifs, secrets/redaction et persistence atomique sont déjà couverts par les 80 tests unitaires acquis enpre.013-fix.001; - aucune correction supplémentaire de surface publique n'est nécessaire dans cette tranche ; la granularité
ConfigEnvironment/ConfigDocumentEngine/ConfigManagement/ adapter Logging reste cohérente ; - aucune nouvelle dépendance ni variable d'environnement.
La validation utilisateur de pre.013-fix.001 est acquise le 2026-08-16 : fmt/check/clippy/test passent, ksp-config-lib compte 80 tests unitaires + 11 tests publics, ksp-logging-lib 34 tests unitaires + toutes ses intégrations, ksp-logging-lib -d est vide et le seul doublon Config reste syn 2/syn 3 transitif via jsonschema.
0.1.3-pre.015 — 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.001puis tag stable après validation utilisateur.
Le découpage reste souple. Tout dépassement du budget de tranche est corrigé par une nouvelle scission explicite ; le nombre de prereleases n'est pas une cible à minimiser.
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 :
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.003 est validable lorsque les décisions suivantes sont acceptées :
ksp-config-libest l'unique manager KSP des documents Config et variables applicatives ;- Config possède un registre logique de fichiers avec
file_idglobalement unique ; - les documents et schemas connus disposent d'un mapping par défaut
file_id -> filename; - les composites référencent les documents par
file_id, jamais par filename ; - les mappings physiques peuvent être remplacés au bootstrap, y compris pour les schemas ;
cfgpathetschemapathont les défauts hardcodésconfigetconfig/schemas;- ces deux paths ne peuvent être remplacés ni par JSON, ni
.env, niKSP_*/KSPB_*; - ils ne changent qu'au bootstrap via
--cfgpath/--schemapathou options programmatiques explicites ; --filemap=<file_id>=<filename>est le mécanisme de sélection d'un filename alternatif ;- premier document réel :
config/std.logging.jsonidentifié parcfg.std.logging; - futurs documents :
config/std.<domain>.json; - futurs composites :
config/composite.<consumer>.json; - JSON validé par
jsonschemacontre le schema associé au descriptor dufile_id; serde+serde_jsonconstituent la base de parsing/manipulation/persistence ;- globals hors profils ;
default_profileautonome ;- profils identifiés par
profile_idunique ; - composite assemble documents + globals + profils sans recopier les valeurs ;
.envconventionnel./.envà la racine de lancement, sans troisième path Config dans0.1.3;- 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 ;
.envest read/write via Config ; process env est read-only ;- les autres crates n'appellent pas
std::env::var(...)pour les variables applicatives ; Secretreste 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-deskpourra révéler/modifier les secrets via une surface management privilégiée ;- même cette application ne logge jamais les secrets ;
std.logging.jsonsupporte conceptuellement une console configurable et plusieurs sinks fichier avec identifiants uniques, niveaux, targets, domains, rotation, format et ANSI selon la nature du sink ;- l'écart de capacités de
ksp-logging-lib 0.1.2est reconnu et sera fermé dans Logging, pas contourné par Config ; - le lifecycle/ownership Logging
initialize/reinitialize/LoggingGuardreste inchangé ; - persistence JSON et
.envatomique ; 0.1.3reste une release unique bornée avec clôture prévisionnelle désormais enpre.015, sous réserve de nouvelles scissions explicites si une tranche dépasse le budget 15–20 minutes ;pre.002est limitée à la création de la crate et au bootstrapcfgpath/schemapath; le registrefile_idcommence enpre.003;- les évolutions
ksp-logging-libsont explicites enpre.004(contrats/settings),pre.005(runtime multi-sink + level/target/formats) puispre.006(routing structuré domain), avant gel du schema Logging ; - chaque prerelease vise environ 15–20 minutes de travail effectif et doit être scindée si ce budget devient manifestement irréaliste ;
- aucune implémentation
pre.002ne commence avant validation utilisateur de ce plan corrigé.