Cycle de vie de la configuration
La configuration est à la fois une interface opérateur et un contrat d’exécution. Traitez-la comme un état ayant un propriétaire clair, et non comme des paramètres dispersés copiés dans le sous-système qui en a besoin.
La source canonique est zeroclaw_config::schema::Config, chargée depuis config.toml. Les surfaces de configuration destinées aux utilisateurs, la référence de configuration générée, l’éditeur de configuration de la gateway, les surcharges par variables d’environnement, zeroclaw config set, zeroclaw config patch, Quickstart et les méthodes de configuration RPC passent tous par ce même schéma typé.
Pour l’ordre de construction, les règles de sortie suivies et les vérifications de dérive qui transforment le schéma typé en référence de configuration, consultez Pipeline de documentation générée.
Qui possède quoi
| Surface | Propriétaire | Limite de persistance | Limite d’application à l’exécution |
|---|---|---|---|
| Schéma de configuration | crates/zeroclaw-config/src/schema.rs plus les derives Configurable | Code, pas de documentation générée | Nouveau build binaire |
| Référence générée | cargo mdbook refs / markdown-schema | docs/book/src/reference/config.md au moment de la compilation | Documentation uniquement |
| Emplacement de Bootstrap | ZEROCLAW_CONFIG_DIR, ZEROCLAW_DATA_DIR, déprécié ZEROCLAW_WORKSPACE | Environnement uniquement | Avant l’existence de Config |
| Surcharges de miroir de schéma | ZEROCLAW_<lowercase_path> avec __ pour les points | Uniquement en mémoire | Chaque Config::load_or_init() |
| écritures de configuration CLI | zeroclaw config set, config patch, alias, helpers de modèle | save_dirty() dans config.toml | Prochain chargement/rechargement sauf si la commande actuelle utilise la nouvelle valeur en mémoire |
| Écritures de configuration RPC et TUI | config/* méthodes RPC utilisées par zerocode | Commit de configuration admis : save_dirty() puis publication | Publication de la paire mise à jour immédiatement ; les sous-systèmes gérés par le démon doivent être rechargés |
| Démarrage rapide appliquer | Chemin d’application partagé pour le web, la CLI et zerocode | Application par étapes terminée sous forme de commit de configuration (surfaces supervisées) | Web et RPC peuvent signaler le rechargement du démon ; le CLI autonome s’applique au prochain chargement/rechargement |
| Écritures de configuration de la passerelle | Gestionnaires de l’API de configuration via persist_and_swap() | Commit de configuration admis : save_dirty() puis publication | Les mises à jour de paire publiées sont appliquées immédiatement ; les sous-systèmes du démon les appliquent après rechargement |
| Rechargement du daemon | /admin/reload, RPC config/reload, ou le canal de rechargement en processus | Relit config.toml | Recrée les sous-systèmes du démon dans le même PID |
Ne modifiez pas manuellement la référence de configuration générée. Si un champ, une enum, une section des alias, un marqueur de secret ou une description est incorrect, corrigez le schéma ou le générateur et régénérez la référence.
Ordre de chargement
Le chargement de la configuration se déroule en plusieurs phases distinctes :
- Résoudre la racine d’installation à partir des variables d’environnement de bootstrap. Cela se produit avant qu’aucune
Confign’existe, donc les noms bootstrap conservent leur forme majuscule et n’utilisent pas la grammaire schema-mirror. - Lire
config.toml, exécuter la migration de schéma en mémoire, déchiffrer les secrets configurés et enregistrer toute section critique pour la sécurité malformée comme sécurité dégradée. - Appliquez les overrides du schéma miroir à la configuration en mémoire. Dans les variables d’environnement,
__est mappé sur., doncZEROCLAW_providers__models__openai__api_keycibleproviders.models.openai.api_key. - Valider et avertir sans verrouiller l’opérateur hors de l’éditeur de passerelle.
Lors d’une installation fraîche, les valeurs par défaut sont enregistrées avant l’application des surcharges d’env. Cela maintient les secrets injectés par l’env et les valeurs CI locales hors du nouveau fichier.
Les surcharges d’environnement ne sont pas sauvegardées
Les variables d’environnement Schema-mirror sont des injections à l’exécution. Elles aboutissent sur le Config en mémoire au chargement et sont suivies dans env_overridden_paths afin que le CLI, le tableau de bord et le quickstart puissent afficher le marqueur de surcharge.
La sauvegarde doit masquer ces chemins pour les ramener à leur valeur disque pré-override ou par défaut avant le chiffrement. Cela importe surtout pour les secrets : si un opérateur a une clé API chiffrée sur le disque et démarre temporairement avec un override d’env pour le même chemin, une sauvegarde de configuration non liée ne doit pas remplacer le véritable identifiant par la valeur d’env ou par une chaîne d’affichage masquée.
Examinez les modifications de configuration en gardant cet invariant à l’esprit :
- Les valeurs de schéma miroir
ZEROCLAW_*affectent le processus en cours d’exécution après le chargement. - Ils ne deviennent pas une config durable.
- Les chemins de sauvegarde doivent préserver les secrets chiffrés et les références de secrets externes sauf si le même chemin a été modifié intentionnellement.
Les entrées d’identifiants restent typées
Les valeurs d’exécution de type identifiant restent des valeurs de configuration. Les clés API, les jetons OAuth, les URL de point de terminaison et autres identifiants de fournisseur/canal doivent transiter par le schéma de configuration typé, la gestion des secrets de configuration ou les surcharges ZEROCLAW_* en miroir du schéma avant qu’un constructeur d’exécution ne les voie.
N’ajoutez pas de lectures ad hoc de std::env::var("PROVIDER_API_KEY") dans les constructeurs de fournisseurs, de canaux, d’outils, de transcription, de TTS, de mémoire ou de passerelle. Cela crée une seconde source d’identifiants en dehors de Config, contourne la prise en compte des remplacements par les variables d’environnement et peut rendre incohérents les comportements de la CLI, de la passerelle, de RPC/TUI, du démarrage rapide et du rechargement.
Si ZeroClaw prend en charge intentionnellement un pont d’environnement natif pour une famille d’intégrations, documentez ce pont à la frontière de l’intégration et mappez-le dans la même valeur de configuration typée avant la construction. Sinon, les noms de shell par défaut de l’écosystème tels que ANTHROPIC_API_KEY, OPENROUTER_API_KEY ou QDRANT_URL doivent être pontés par les opérateurs vers la variable de miroir de schéma ZEROCLAW_* correspondante ; voir Variables d’environnement.
Chemins modifiés et écritures incrémentales
La plupart des surfaces d’édition utilisent Config::mark_dirty() et save_dirty(), et non une réécriture complète. save_dirty() n’écrit que les chemins en notation pointée modifiés, préserve les entrées non modifiées et les commentaires dans la mesure du possible, marque la schema_version actuelle, et effectue l’écriture via un remplacement atomique de fichier temporaire.
Ce chemin est également responsable des sections de clés de map. La création d’un alias tel qu’un fournisseur de modèle, un serveur MCP, un bundle de compétences ou un bundle de connaissances doit salir la bonne section afin que l’alias survive à une sauvegarde et un rechargement. Une modification de configuration qui ne met à jour que l’état du tableau de bord en mémoire n’est pas complète.
Lors de la revue d’une écriture de configuration, vérifiez que :
- le chemin édité est marqué dirty avant la persistance ;
- les créations, renommages et suppressions de map-key salissent la section parente ou la clé naturelle ;
- les chemins secrets et surchargés par l’environnement conservent leur comportement de masquage à la sauvegarde ;
schema_versionreste à jour après des écritures incrémentielles ;- la valeur modifiée survit à
save_dirty()suivi d’un rechargement.
Les sauvegardes complètes nécessitent la provenance du chargement
Config::save() refuse d’écraser un config.toml existant, sauf si la valeur en mémoire a été chargée depuis ce fichier exact (loaded_from, défini par load_or_init dans ses branches « fichier existant » et « initialisation à neuf », ainsi que par les loaders qui relisent un fichier de configuration avant de le modifier). Un Config construit par défaut, construit programmatiquement ou redirigé vers un autre chemin ne peut donc pas remplacer la configuration peuplée d’un opérateur par un instantané presque vide.
La création d’un fichier manquant (première exécution) réussit toujours : une valeur qui n’a jamais lu de fichier n’est créée qu’une seule fois et doit ensuite être rechargée ou forcée, et force_save() est le chemin explicite pour un écrasement intentionnel et vérifié. Lors de la revue d’un chemin de code qui construit un nouveau Config et prévoit de l’écrire par-dessus un fichier existant, chargez d’abord le fichier ou justifiez l’appel à force_save().
force_save() ignore uniquement la vérification de provenance du chargement. Les vérifications du chemin de destination restent appliquées. Pour remplacer un fichier existant, définissez config_path sur le chemin visé, avec un répertoire parent non vide. Un nom de fichier seul, tel que config.toml, est refusé si sa destination résolue à l’exécution existe déjà, ce qui préserve la protection existante contre l’écrasement d’un fichier situé à une destination déduite.
Enregistré vs appliqué
Un enregistrement réussi signifie que le fichier a été modifié. Cela ne signifie pas toujours que chaque composant d’exécution a pris en compte la modification.
Le démon possède le graphe de sous-systèmes de longue durée : passerelle, écouteurs de canaux, planificateur, écouteur MQTT, câblage de session, backend mémoire, fabriques de fournisseurs et câblage des coûts. POST /admin/reload signale la boucle du démon, qui relit config.toml et réinstancie ces sous-systèmes dans le même processus. Le PID reste le même, mais les écouteurs se rattachent brièvement.
Les écritures de configuration de la passerelle appellent persist_and_swap() : enregistrement sur disque à l’intérieur d’une validation de configuration admise et sérialisée, puis publication de la configuration enregistrée comme nouvelle paire publiée et positionnement de pending_reload. Cela permet à l’éditeur de configuration de refléter immédiatement l’écriture, tandis que la bannière de rechargement indique à l’opérateur que les canaux, les fournisseurs, le planificateur ou d’autres composants gérés par le démon peuvent encore fonctionner avec l’instance de sous-système précédente.
Le mode autonome zeroclaw gateway start n’a pas de superviseur de démon. Son endpoint de rechargement renvoie une réponse indiquant qu’un redémarrage est requis car il n’y a pas de boucle de démon externe à signaler.
Paire publiée et sérialisation de l’écrivain
Le processus supervisé conserve une paire canonique publiée : le Config accompagné de sa révision, d’une époque d’autorité opaque et d’une séquence vérifiée. Les lecteurs reçoivent un handle live en lecture seule (zeroclaw_config::live::LiveConfigHandle) et observent la configuration et sa révision comme une seule unité ; ce handle n’expose ni mutation ni ne maintient de seconde copie de la configuration.
Chaque écrivain participant (HTTP, RPC, TUI, Quickstart, appairage et identité de canal) passe par LiveConfigAuthority (begin_config_commit) de la génération du démon. La validation acquiert le verrou mutex global d’écriture de configuration une seule fois, puis conserve un bail d’écriture de configuration de génération jusqu’à la publication. Attendre le mutex ne détient pas de bail d’écriture de configuration. Une réservation d’alias d’agent distincte, comme la réservation de RPC Quickstart avant l’admission, peut toujours compter dans le drainage de la génération. La phase irréversible (persistance, puis publication sous une révision allouée avant toute E/S disque) s’exécute conservée, de sorte qu’une requête annulée ne peut pas abandonner une validation déjà lancée entre le remplacement atomique du fichier et sa publication. Les validations échouent en mode fermé dès que la génération est en cours de fermeture ; un rechargement complet démarre une nouvelle autorité avec une nouvelle époque (les numéros de séquence ne se comparent qu’au sein d’une même époque).
La politique d’autorisation acceptée dérivée possède son propre compteur de publication et de révision, distinct de la paire de configuration. Les écritures de configuration HTTP publient cette politique avant la paire de configuration ; les écritures de configuration RPC la publient après. Ces ordres existants ne fournissent pas une frontière de visibilité atomique unique pour les deux publications, et le compteur de la politique n’est pas une séquence ConfigRevision.
Autres limites dignes d’être nommées :
- Une publication validée n’est pas annulée lorsqu’un effet de bord ultérieur échoue. Quickstart publie la configuration validée même si l’installation des fichiers de personnalité échoue ensuite. La CLI signale l’alias enregistré et indique de récupérer les fichiers de l’espace de travail au lieu de relancer Quickstart. Web et RPC renvoient toujours le même résultat d’erreur que celui utilisé pour un refus, sans résultat distinct de validation, alors qu’ils signalent un rechargement après ce succès partiel.
- Le endpoint config migrate prépare un candidat entièrement hydraté (secrets déchiffrés, surcharges d’environnement appliquées, validation stricte) avant de remplacer le fichier, de sorte qu’un candidat qui ne peut pas être hydraté ou validé est refusé et le fichier d’origine reste intact.
- Les outils
model_routing_configetproxy_configenregistrent encore des instantanés de disque chargés indépendamment, sans admission de commit de configuration ni publication.model_routing_config upsert_agentutilise une persistance par chemin modifié (dirty-path) afin que les valeurs et commentaires non concernés soient préservés, mais ses autres actions etproxy_configécrivent toujours des instantanés typés complets. Ces writers non participants peuvent perdre des modifications concurrentes sur le même chemin ou laisser les lecteurs publiés obsolètes. Leur migration vers le contrat des writers participants reste un travail distinct. - La publication n’applique pas en soi la configuration aux sous-systèmes en cours d’exécution. Le registre applicatif enregistre les accusés de réception des actualisations RPC existantes des fournisseurs de modèles ; les autres composants gérés par le démon nécessitent toujours
/admin/reload. Les nouvelles superpositions de sécurité et le comportement de transfert de canal restent soumis à ADR-012.
État de l’application
GET /api/config/reload-status conserve pending_reload et ajoute une vue application issue de l’autorité de configuration active. La RPC config/status conserve la disponibilité de l’intégration et ajoute la même vue. La révision publiée contient une époque et une séquence opaques. Chaque enregistrement identifie la révision effectivement traitée, un chemin sémantique sous forme de composants champ/clé, et l’instance cible. Une clé de map contenant un point reste un seul composant.
La cible daemon signale queued_for_reload avec daemon_reload_required. Seul un rafraîchissement RPC existant et sélectionné du fournisseur de modèles peut signaler applied_live pour son incarnation de session capturée. Son enregistrement reste pending jusqu’à ce que le consommateur propriétaire accuse réception, et une mise à jour d’instance refusée est rejected. Les métadonnées d’application du schéma identifient une capacité de rafraîchissement existante potentielle, et non la preuve qu’une instance en cours d’exécution a adopté une valeur. Les chemins inconnus nécessitent un rechargement. Si l’extraction des chemins modifiés est indisponible, un chemin vide désigne une exigence de rechargement de toute la configuration avec change_scope_unavailable.
La vue HTTP ne contient que des enregistrements du démon. Les enregistrements de session RPC exigent en outre des autorisations de lecture de session en cours de validité, le sélecteur d’agent, l’appartenance de la session au principal authentifié, l’appartenance à la connexion et l’incarnation active actuelle. Les administrateurs et l’opérateur partagé conservent le contournement existant de la vérification d’appartenance au principal. Aucune valeur de configuration, aucun identifiant, aucune transcription ni aucun texte d’erreur du fournisseur n’entre dans ces enregistrements. La suppression ou le remplacement d’une session retire son enregistrement à la frontière de lecture de statut protégée ; un ancien rappel ne peut ni recréer cet enregistrement ni acquitter son successeur.
Le registre conserve uniquement la dernière modification ou tentative observée pour chaque chemin et chaque cible, et non un historique d’événements durable ni un inventaire de chaque sous-système. La rétention est limitée par record_limit ; truncated signale l’éviction. Un enregistrement absent n’est pas une preuve d’application. Une modification sans rapport ne réétiquette pas les accusés de réception antérieurs avec la dernière révision. Une nouvelle autorité après un rechargement complet démarre une nouvelle époque, sans accusé de réception de modification à chaud enregistré. Effacer pending_reload ou renvoyer une réponse saine ne peut pas modifier les résultats d’application. Ce lot n’ajoute aucune affirmation sur la livraison ni sur l’état de santé du watcher.
Recharger l’accès
Le rechargement local est autorisé depuis loopback. Le rechargement distant nécessite les deux :
gateway.allow_remote_admin = true- appairage activé et un jeton Bearer appairé valide
L’activation de l’administration à distance lorsque l’appairage est désactivé est rejetée plutôt que traitée comme un accès anonyme de rechargement à distance.
Les sections de configuration malformées critiques pour la sécurité ne sont autorisées à se dégrader que lorsque l’opérateur opte explicitement pour un service dégradé. Sinon, le processus refuse de servir car la posture de sécurité de réinitialisation aux valeurs par défaut peut être plus faible que celle prévue par le fichier.
Restauration et réparation
Les écritures de configuration utilisent un remplacement atomique via un fichier temporaire et des permissions réservées au propriétaire. Lors du remplacement d’un fichier existant, le writer crée un config.toml.bak dans le même répertoire pendant le remplacement, puis le supprime après une écriture réussie. Les écritures de la passerelle prennent aussi un instantané du fichier avant écriture et tentent, au mieux, de le restaurer si la persistance échoue avant la publication de la nouvelle paire.
Il n’existe pas de rollback transactionnel général pour une modification de configuration valide mais indésirable après qu’elle a été enregistrée et appliquée. Restaurez le précédent config.toml à partir de la sauvegarde, rétablissez le champ via la CLI ou le tableau de bord, puis rechargez ou redémarrez selon la frontière de runtime ci-dessus.
Config-visible n’est pas toujours pris en charge à l’exécution
Un champ peut être visible dans le schéma avant que chaque chemin d’exécution ne le consomme. Cela n’est acceptable que si la documentation et les notes de revue l’indiquent clairement.
Par exemple, knowledge_bundles est visible dans le schéma et apparaît dans les sections de configuration des API. Une PR qui ajoute ou modifie une telle surface doit préciser si elle stocke uniquement la configuration, branche le comportement d’exécution ou couvre les deux.
Lors de la revue d’une PR qui touche un champ visible dans le schéma mais pas encore consommé au runtime, exiger que la description de la PR indique si le câblage runtime est différé, hors périmètre ou réalisé par le même changement.
Liste de contrôle du relecteur
Pour les modifications de config-schema, env-var, default ou reload, demandez :
- Quelle est la source de vérité pour la nouvelle valeur ?
- Cela crée-t-il un état dupliqué, ou résout-il directement depuis
Configà l’utilisation ? - La référence générée provient-elle du code plutôt que d’un texte rédigé manuellement ?
- Les remplacements de variables d’environnement sont-ils uniquement appliqués lors du chargement et masqués lors des sauvegardes ?
- Les interfaces CLI, gateway, RPC/TUI et quickstart utilisent-elles le même chemin ponctué ?
- Les identifiants sont-ils résolus via une configuration typée ou des passerelles documentées vers un miroir de schéma, plutôt que par des lectures ad hoc des variables d’environnement natives du fournisseur ?
- Une sauvegarde persiste-t-elle après un rechargement du processus, et pas seulement au rendu immédiat en mémoire ?
- Le PR indique-t-il si les utilisateurs doivent recharger, redémarrer, effectuer une migration ou un retour arrière manuel ?
- Si le champ est uniquement visible en configuration, la PR évite-t-elle de prétendre à une prise en charge à l’exécution ?
Pointeurs source
- Schéma et persistance de la configuration :
crates/zeroclaw-config/src/schema.rs - Stockage de paires publiées et handle en lecture seule :
crates/zeroclaw-config/src/live.rs - Autorité de configuration en direct, validations et cycle de vie :
crates/zeroclaw-runtime/src/live_config_authority.rs - Syntaxe de remplacement d’environnement :
crates/zeroclaw-config/src/env_overrides.rs - Commandes CLI de configuration :
src/main.rs - Méthodes de configuration RPC et TUI :
crates/zeroclaw-runtime/src/rpc/dispatch.rs - Chemin d’application Quickstart partagé :
crates/zeroclaw-runtime/src/quickstart/mod.rs - Signalisation de rechargement Web Quickstart :
crates/zeroclaw-gateway/src/api_quickstart.rs - API de configuration de la passerelle et bandeau de rechargement :
crates/zeroclaw-gateway/src/api_config.rs - Endpoint de rechargement et porte d’accès :
crates/zeroclaw-gateway/src/lib.rs - Utilitaire d’authentification Bearer Gateway : `crates/zeroclaw-gateway/src/api.rs`
- Pipeline de référence généré :
xtask/src/cmd/mdbook/refs.rs