v0.1.3-pre.015

This commit is contained in:
2026-08-16 07:52:59 +02:00
parent 92982667ac
commit 64136c99ad
14 changed files with 876 additions and 25 deletions

View File

@@ -0,0 +1,82 @@
<!-- file: crates/ksp-config-lib/README.md -->
<!-- version: 1 -->
# ksp-config-lib
`ksp-config-lib` est le propriétaire unique de la configuration applicative KSP.
La crate centralise les documents JSON, leurs schemas, les profils et compositions, les variables d'environnement `KSP_*` / `KSPB_*`, le fichier `.env`, la résolution effective et les mutations persistantes explicitement autorisées.
## Responsabilités
`ksp-config-lib` possède :
- le bootstrap non récursif `config/` / `config/schemas/` et les overrides `--cfgpath` / `--schemapath` ;
- le registre logique `file_id -> filename` et les overrides `--filemap=<file_id>=<filename>` ;
- la lecture JSON et la validation JSON Schema Draft 2020-12 ;
- les invariants sémantiques KSP des documents connus ;
- les globals, `default_profile`, profils nommés et leur provenance ;
- les compositions génériques par `file_id`, sans dépendance à un filename physique ;
- le snapshot des variables process KSP/KSPB et la lecture de `./.env` ;
- la priorité `process > .env > fallback > missing` ;
- les placeholders `${NAME}` et `${NAME:-fallback}` ;
- la classification `Public`, `Internal`, `Secret` ;
- les représentations réelle et sûre/redacted ainsi que la provenance des valeurs résolues ;
- l'adapter du document Logging effectif vers `ksp_logging_lib::LoggingSettings` ;
- la surface de management pour inspecter les sources, modifier `std.logging.json`, consulter les rapports d'environnement, révéler explicitement une valeur réelle et modifier `.env` ;
- les écritures atomiques JSON/`.env` et la protection des permissions `.env` ;
- les audits workspace empêchant les bypass d'ownership Config et les oublis dans `.env.example`.
## Ressources gérées dans `0.1.3`
Le registre par défaut connaît :
```text
cfg.std.logging -> config/std.logging.json
schema.std.logging -> config/schemas/std.logging.schema.json
schema.composite -> config/schemas/composite.schema.json
```
`config/examples/composite.example.json` démontre le format composite sans créer de composite runtime fictif.
Le fichier local d'environnement est :
```text
./.env
```
Il n'est ni versionné ni livré. Le dépôt maintient `/.env.example` comme inventaire versionné des variables runtime utilisées. Toute nouvelle variable KSP/KSPB concrète doit y être ajoutée avec un commentaire d'usage dans le même delta que sa première utilisation.
## Frontières
Les autres crates et applications KSP ne doivent pas :
- lire directement les variables applicatives `KSP_*` / `KSPB_*` ;
- parser ou écrire directement `.env` ;
- ouvrir directement les documents Config connus par leur filename physique ;
- réimplémenter la sélection de profils, les compositions ou les placeholders ;
- reconstruire elles-mêmes la configuration Logging depuis le JSON.
`ksp-config-lib` dépend de `ksp-core-lib` pour `Error`/`Result` et de `ksp-logging-lib` pour les événements Config utiles et le contrat `LoggingSettings`.
La dépendance inverse est interdite : `ksp-core-lib` et `ksp-logging-lib` ne dépendent pas de Config.
Config ne possède pas le `LoggingGuard`. L'application ou le service qui orchestre le runtime construit la configuration effective puis possède le lifecycle `ksp_logging_lib::initialize/reinitialize`.
Tauri et les DTO TS-RS restent hors de cette crate. La future `ksp-app-config-desk` doit rester une frontière applicative mince au-dessus des APIs Config.
## Secrets
Un secret reste accessible au runtime ou au management lorsqu'un consumer autorisé en a réellement besoin, mais les vues ordinaires utilisent la représentation sûre.
Les méthodes `reveal_*` constituent un opt-in explicite au réel. L'authentification/autorisation de l'utilisateur humain appartient à l'application appelante et les valeurs retournées par ces méthodes ne doivent jamais être journalisées.
Le document Logging refuse les valeurs de sensibilité `Secret` dans sa configuration effective.
## Documentation
- [`USAGE.md`](USAGE.md) — construction du moteur, résolution runtime et management ;
- [`TODO.md`](TODO.md) — points explicitement différés après `0.1.3` ;
- [`../../docs/plans/005-V0_1_3_CONFIG_FOUNDATION_PLAN.md`](../../docs/plans/005-V0_1_3_CONFIG_FOUNDATION_PLAN.md) — plan historique détaillé de la fondation Config ;
- [`../../config/std.logging.json`](../../config/std.logging.json) — premier document standard concret ;
- [`../../.env.example`](../../.env.example) — inventaire versionné des variables d'environnement runtime.

View File

@@ -0,0 +1,38 @@
<!-- file: crates/ksp-config-lib/TODO.md -->
<!-- version: 1 -->
# TODO ksp-config-lib
## État de clôture `0.1.3`
Aucun TODO fonctionnel bloquant n'est ouvert pour la fondation Config `0.1.3`.
Les responsabilités prévues pour cette release sont implémentées et couvertes par les tests : bootstrap, registre `file_id`, JSON/JSON Schema, profils, composites, environnement process/`.env`, placeholders, sensibilité/provenance, adapter Logging, management/persistence et audits d'ownership.
## Reporté explicitement à `0.1.4`
La validation applicative desktop appartient à `ksp-app-config-desk` :
- frontière Tauri et DTO TS-RS applicatifs ;
- affichage des sources et diagnostics Config ;
- sélection/inspection des profils ;
- affichage desired/effective/shadow des variables ;
- actions explicites de reveal de secrets avec contrôle d'autorisation côté application ;
- édition/sauvegarde de `std.logging.json` via `ConfigManagement` ;
- édition de `.env` via `ConfigManagement` ;
- orchestration réelle `Config -> LoggingSettings -> initialize/reinitialize` avec `LoggingGuard` possédé par l'application ;
- validation UX des erreurs de source invalide, des modifications non effectives car masquées par le process et des besoins de reload.
Ces points ne nécessitent pas de duplication de logique dans `ksp-config-lib`; toute lacune réelle révélée par l'application ouvrira un delta Config explicite.
## Futur, uniquement au besoin
Les capacités suivantes sont différées jusqu'à l'apparition de composants réels :
- nouveaux documents `std.<domain>.json` et schemas associés ;
- descriptors `cfg.composite.<consumer>` pour de vrais consumers ;
- contrats typés de management supplémentaires pour les nouveaux documents ;
- watcher filesystem/reload automatique si une application ou un service démontre le besoin ;
- intégration éventuelle d'un secrets manager externe.
Ne pas introduire par anticipation un JSON patch arbitraire, un watcher générique, un service distribué de configuration ou un chiffrement maison de `.env`.

View File

@@ -0,0 +1,184 @@
<!-- file: crates/ksp-config-lib/USAGE.md -->
<!-- version: 1 -->
# Utilisation de ksp-config-lib
## 1. Bootstrap et moteur documentaire
Config doit interpréter ses propres arguments de bootstrap avant toute lecture de document :
```rust
let args: std::vec::Vec<std::ffi::OsString> = std::env::args_os().collect();
let bootstrap = match ksp_config_lib::ConfigBootstrapOptions::from_args(args.as_slice()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let registry = match ksp_config_lib::ConfigFileRegistry::from_args(args.as_slice()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let engine = ksp_config_lib::ConfigDocumentEngine::new(bootstrap, registry);
```
Les arguments compris par Config sont :
```text
--cfgpath=/path/to/config
--schemapath=/path/to/schemas
--filemap=cfg.std.logging=my-logging.json
```
`cfgpath` et `schemapath` ne sont jamais lus depuis JSON, `.env` ou une variable KSP : cette règle évite un bootstrap récursif.
## 2. Charger et valider un document connu
Les consumers utilisent un `file_id` logique :
```rust
let file_id = match ksp_config_lib::ConfigFileId::new(ksp_config_lib::FILE_ID_STD_LOGGING) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let document = engine.load_validated_document(&file_id);
```
Le moteur résout le path physique via le registre, charge le schema associé, valide le schema lui-même, valide l'instance puis applique les invariants sémantiques KSP.
## 3. Environnement effectif
`ConfigEnvironment::load()` capture les variables process KSP/KSPB et lit `./.env` :
```rust
let environment = match ksp_config_lib::ConfigEnvironment::load() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
```
La priorité est :
```text
process environment > .env > fallback > missing
```
Une chaîne vide explicitement présente est une valeur définie ; elle ne provoque pas l'utilisation du fallback.
Exemples de placeholders :
```text
${KSP_LOGS_DIRECTORY}
${KSP_LOGS_DIRECTORY:-logs}
```
Pour conserver la sensibilité et la provenance, préférer les variantes détaillées :
```rust
let resolved = environment.resolve_text_detailed("${KSP_SECRET_EXAMPLE}");
```
`ResolvedConfigText` / `ResolvedConfigJson` séparent valeur réelle et valeur sûre. Une représentation `Debug` ne doit pas révéler le réel d'un secret.
## 4. Construire Logging depuis Config
Le chemin normal consiste à charger le profil Logging, résoudre l'environnement puis construire directement le contrat Logging public :
```rust
let resolved = match engine.load_resolved_logging_config(std::option::Option::None, &environment) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let settings = resolved.into_settings();
let initialized = ksp_logging_lib::initialize(&settings);
let mut logging_guard = match initialized {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
```
L'application/service possède `logging_guard`. Config ne conserve pas de singleton Logging.
Un `logs_directory` relatif est ancré sur le current working directory du processus. Un path absolu est conservé. Une valeur explicite invalide produit une erreur effective : elle ne retombe pas silencieusement sur le fallback du placeholder.
Les `files[].path` restent relatifs sous le root Logging, y compris après interpolation.
## 5. Profils et composites
Pour un document standard profilé :
```rust
let profile = engine.load_resolved_profile(&file_id, std::option::Option::None);
```
`None` utilise le `default_profile`; `Some("profile_id")` impose un profil explicite.
Un composite référence les documents par `file_id`, jamais par filename. `load_resolved_composite(...)` conserve chaque `ResolvedConfigProfile` composant et sa provenance plutôt que d'aplatir plusieurs domaines dans une map ambiguë.
## 6. Management de `std.logging.json`
Une application de management construit la façade à partir d'un moteur :
```rust
let management = ksp_config_lib::ConfigManagement::new(engine);
let document = match management.load_logging_document() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
```
Le type `LoggingConfigDocument` et ses sous-structures exposent des setters/mutators typés. Après modification, la sauvegarde :
```rust
let saved = management.save_logging_document(&document);
```
valide le candidat complet avant toute substitution du fichier. Un candidat invalide ne remplace pas la source existante.
`read_source(file_id)` reste disponible pour une UI de réparation : il peut lire le texte brut d'un document enregistré même lorsque son JSON ou son schema est invalide. Il n'ouvre pas un path arbitraire.
## 7. Management de `.env`
Les rapports ordinaires sont sûrs :
```rust
let report = management.environment_report();
```
Ils distinguent notamment valeur souhaitée `.env`, valeur effective, source et shadowing process sans exposer un secret réel.
L'accès au réel est volontairement explicite :
```rust
let effective = management.reveal_effective_environment_value("KSP_SECRET_EXAMPLE");
let persisted = management.reveal_dotenv_value("KSP_SECRET_EXAMPLE");
```
Une application doit contrôler l'autorisation de l'utilisateur avant ces appels et ne jamais journaliser les valeurs retournées.
Les mutations persistantes utilisent :
```rust
let changed = management.set_dotenv_value("KSP_LOGS_DIRECTORY", "logs");
let removed = management.remove_dotenv_value("KSP_LOGS_DIRECTORY");
```
Elles n'altèrent jamais l'environnement hérité du processus. Une valeur process peut donc masquer une modification `.env`; `ConfigEnvironmentChangeReport` distingue `source_changed`, `effective_changed`, `shadowed_by_process_environment` et `reload_required`.
Sur Unix, un nouveau `.env` est créé avec des permissions privées `0600`; les permissions existantes sont préservées lors des remplacements atomiques.
## 8. `.env.example`
`/.env.example` est l'inventaire versionné. `/.env` reste local et ignoré.
Toute nouvelle variable runtime concrète `KSP_*` / `KSPB_*` introduite dans le code ou les documents Config doit être ajoutée à `.env.example` avec un commentaire expliquant son usage. Les audits `ksp-config-lib/tests/ownership.rs` font échouer `cargo test` lorsqu'une clé concrète est oubliée.
## 9. Frontière Tauri
Une application Tauri doit appeler les APIs ci-dessus via ses commandes/DTO applicatifs. Elle ne lit ni JSON ni `.env` directement et ne résout jamais elle-même les placeholders.
Les valeurs `Secret` ne doivent pas être incluses par défaut dans les DTO publics. Une action UI explicitement autorisée peut appeler une méthode `reveal_*` et transporter le résultat par un DTO spécifique, sans log ni diagnostic contenant la valeur réelle.

View File

@@ -1,15 +1,15 @@
// file: crates/ksp-config-lib/src/lib.rs
// version: 9
// version: 10
#![warn(missing_docs)]
#![deny(unreachable_pub)]
#![forbid(unsafe_code)]
//! KSP-owned application configuration facade.
//!
//! `0.1.3-pre.013` owns bootstrap roots, the logical file registry, generic JSON/JSON Schema loading, standard-document profile resolution, generic composite
//! resolution and KSP/KSPB environment resolution through process + `.env` + fallback precedence. The standard Logging document remains the first registered
//! runtime document. Environment-derived values preserve real/safe representations, sensitivity and provenance; the standard Logging profile can now be
//! mapped explicitly to `ksp_logging_lib::LoggingSettings`. Explicit management now owns typed Logging mutation, safe environment reports, privileged reveal calls and atomic JSON/`.env` persistence.
//! The `0.1.3` surface owns bootstrap roots, the logical file registry, JSON/JSON Schema validation, standard-document profiles, generic composites and
//! KSP/KSPB environment resolution through process + `.env` + fallback precedence. Resolved values preserve real/safe representations, sensitivity and
//! provenance. The standard Logging document maps explicitly to `ksp_logging_lib::LoggingSettings`, while the management surface provides typed Logging
//! mutation, safe environment reports, explicit privileged reveal calls and atomic JSON/`.env` persistence.
mod bootstrap;
mod composite;