Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

Migration vers une configuration typée des plugins

La configuration d’instance typée constitue un changement rétro-incompatible pour tout plugin pré-1.0 qui lit la configuration de l’opérateur. Cette page est le document de migration destiné aux auteurs de plugins et aux opérateurs : ce qui cesse de fonctionner, pourquoi, et les étapes exactes pour corriger un paquet.

Le comportement décrit ici est vérifié par rapport à crates/zeroclaw-plugins/src/config.rs, crates/zeroclaw-plugins/src/instance.rs et au chemin d’admission dans crates/zeroclaw-plugins/src/host.rs.

Décision de publication

L’application de cette règle est fournie avec la fonctionnalité. Il n’y a aucun mécanisme de compatibilité, aucune période de grâce ni aucun indicateur de désactivation. Les plugins constituent une surface expérimentale pré-1.0 ; le projet accepte donc la rupture plutôt que de conserver définitivement un chemin de configuration plus faible : un repli non typé devrait fournir à un invité des valeurs que l’hôte ne peut ni typer, ni nommer, ni borner, ce qui constitue précisément la faille que cette fonctionnalité comble.

Les paquets qui ne sont pas migrés cessent d’être détectés. Rien n’est rétrogradé silencieusement et aucune configuration partielle n’atteint le code invité.

Ce qui casse

Trois choses, indépendamment :

  1. Un manifeste qui demande config_read sans config_schema n’est plus détecté ni installé. Les deux sont liés par une condition nécessaire et suffisante : un schéma sans l’autorisation est tout aussi invalide.
  2. Les entrées de configuration indexées par le nom du package ou de la liaison ne sont plus consultées. Les valeurs des opérateurs résident désormais sous une clé d’instance complète dérivée du package, de la capacité et de la liaison.
  3. Les invités reçoivent du JSON typé, et non une map de chaînes. Un invité qui analysait lui-même les chaînes reçoit désormais de vrais booléens, nombres, tableaux et objets.

Pourquoi l’hôte a besoin d’un schéma

Les valeurs de l’opérateur sont stockées sous forme d’une map de chaînes marquées comme secrètes et chiffrées au repos, tandis que le composant invité est du code tiers non fiable. Sans contrat déclaré, l’hôte ne peut pas répondre aux deux questions auxquelles il doit répondre avant le démarrage du composant invité : quelles clés ce package est-il autorisé à recevoir, et de quel type est chaque valeur. Les mondes WIT sont fixes et partagés par tous les plugins, de sorte que les types de configuration propres à chaque package ne peuvent pas résider dans l’ABI. Le manifeste est le seul endroit où le contrat peut être déclaré, et l’association de additionalProperties = false avec une map properties explicite est ce qui donne à l’autorisation config_read une portée énumérable.

Étapes pour les auteurs

1. Déclarer le schéma

Ajoutez un objet Draft 2020-12 fermé couvrant exactement les clés lues par votre plugin. Chaque propriété de premier niveau doit se résoudre en un seul type explicite : string, boolean, integer, number, array ou object.

name = "my-plugin"
version = "0.2.0"
wasm_path = "my_plugin.wasm"
capabilities = ["channel"]
permissions = ["config_read"]

[config_schema]
"$schema" = "https://json-schema.org/draft/2020-12/schema"
type = "object"
required = ["bot_token"]
additionalProperties = false

[config_schema.properties.bot_token]
type = "string"
minLength = 1

[config_schema.properties.poll_interval_secs]
type = "integer"
minimum = 1

[config_schema.properties.allowed_chats]
type = "array"

L’hôte impose ces limites au schéma lui-même : 64 KiB sérialisés, au plus 32 niveaux d’imbrication, aucun $id, et les cibles de $ref doivent être des pointeurs JSON locaux. Les références distantes sont rejetées, si bien qu’un schéma ne provoque jamais de récupération réseau.

Les mots-clés à clés dynamiques ne font pas partie de ce dialecte : patternProperties, propertyNames et unevaluatedProperties sont rejetés à la racine, car l’hôte ne matérialise une valeur que pour une clé nommée dans la map properties de la racine ; les clés admises par un motif n’atteindraient donc jamais votre plugin. Les schémas de propriétés imbriqués ne sont pas concernés.

pattern utilise le dialecte d’expressions régulières en temps linéaire

L’hôte résout la configuration en recompilant et en revalidant votre schéma à chaque appel, et ce travail s’exécute sur l’hôte, et non dans le budget de fuel de votre composant. Ainsi, pattern est limité aux expressions régulières dont l’hôte peut prédire le coût :

  • Les références arrière et les assertions de contexte sont refusées. (\w+)\s\1, (?=...), (?<=...) et autres nécessitent un moteur avec retour arrière. Les motifs sont plutôt compilés dans le dialecte regex à temps linéaire, qui effectue la correspondance en un temps proportionnel à la longueur de la valeur, quelle que soit la manière dont le motif est écrit.
  • Un seul motif ne peut pas être compilé en un programme de plus de 256 Kio. Cela pose problème avec des nombres élevés de répétitions : ^[\s\S]{0,200}$ convient, ^[\s\S]{0,1000}$ ne convient pas. Utilisez maxLength pour les limites de longueur ; sa vérification est gratuite et exprime clairement votre intention.

Les deux refus ont lieu au moment de l’installation avec une erreur InvalidManifest indiquant le schéma, de sorte qu’un plugin dont l’hôte ne peut pas borner le pattern ne s’exécute jamais. Les patterns structurels se comportent comme prévu : les slugs, les UUID, les adresses e-mail, les URL et les textes libres courts et bornés se compilent tous.

2. Faites correspondre les encodages de valeurs

Le stockage de l’opérateur reste un mappage de chaînes. Le schéma indique à l’hôte comment lire chaque chaîne stockée :

Type déclaréCe que l’opérateur stockeCe que reçoit l’invité
stringsecret-value"secret-value"
booleantruetrue
integer44
number0.50.5
array["a","b"]["a","b"]
object{"k":"v"}{"k":"v"}

Tout ce qui ne peut pas être analysé comme le type déclaré est rejeté avant l’exécution de votre code.

3. Déterminez ce qui est obligatoire ou facultatif pour chaque clé

Les autorisations effectives sont vérifiées séparément des demandes du manifeste. Lorsque config_read est demandé mais n’est pas accordé, l’hôte valide un objet vide par rapport à votre schéma :

  • Un schéma dont tous les champs sont facultatifs reçoit {} : attribuez donc à chaque champ une valeur par défaut côté invité.
  • Un champ required échoue en mode fermé, ce qui est souhaitable pour les identifiants. Un canal qui ne peut pas s’authentifier doit refuser de démarrer plutôt que de fonctionner avec une configuration partielle.

4. Désérialiser du JSON typé dans l’invité

Remplacez l’analyse de chaînes par une seule désérialisation de l’objet injecté. Les plugins d’outils lisent la clé réservée __config, que l’hôte fusionne avec les arguments de l’appel après avoir supprimé toute valeur de ce nom fournie par le modèle.

5. Reconstruire et re-signer

config_schema est couvert par la signature du manifeste ; un paquet signé doit donc être signé à nouveau après son ajout. Consultez Distribution des plugins pour connaître le processus de signature.

Étapes de l’opérateur

Les blocs [[plugins.entries]] existants portant le nom d’un paquet ou d’une liaison ne sont pas lus. Le chemin de migration disponible dépend de la capacité du plugin.

Instances d’outils

Les commandes Install et info peuvent déduire une instance d’outil à partir de la liaison d’outil par défaut du paquet. Pour déplacer les valeurs d’outil vers la nouvelle clé :

  1. Exécutez zeroclaw plugin info <package> pour afficher la clé d’instance complète, qui ressemble à zpi1_....
  2. Renommez le name de l’entrée existante avec cette clé, ou réinstallez le plugin pour initialiser l’entrée, puis définissez les valeurs avec zeroclaw config set plugins.entries.<instance-key>.config.<key>.
  3. Enregistrez la configuration. Les valeurs restent chiffrées au repos.

La clé est un encodage versionné et réversible du paquet, de la capacité et de la liaison, c’est pourquoi deux paquets peuvent tous deux utiliser une liaison nommée main sans partager leurs identifiants. Les nouvelles installations initialisent et affichent automatiquement cette clé d’outil.

Instances de canal

Une clé de canal inclut l’alias de canal configuré. zeroclaw plugin install et zeroclaw plugin info connaissent le package, mais ne contrôlent pas cet alias ; ils ne peuvent donc ni dériver, ni afficher, ni initialiser une clé de canal, et ne doivent pas inventer de substitut au niveau du package. La construction de canaux prenant en charge les alias et la résolution de la configuration à l’exécution ont été intégrées dans zeroclaw#10146 : un daemon construit une instance de canal explicitement déclarée et résout sa configuration typée à partir de zpi1(package, channel, alias), en fonction de l’alias effectivement configuré.

L’affichage automatique de la clé par plugin info et son initialisation lors de l’installation pour les instances de canal restent manuels jusqu’à la cérémonie d’octroi de zeroclaw#9584. Tant que cette cérémonie n’a pas été intégrée, les opérateurs renseignent manuellement la clé du canal avec zeroclaw config set au lieu de laisser l’installation ou info l’afficher et l’initialiser pour eux ; par conséquent, un paquet limité au canal qui s’appuie sur le chemin automatique d’installation et d’affichage de la clé n’est pas encore complet.

Diagnostiquer un rejet

MessageCause
demande config_read, mais ne déclare aucun config_schemaétape 1 non terminée
déclare config_schema sans demander config_readsupprimez le schéma ou ajoutez l’autorisation
config_schema doit définir additionalProperties = falsel’objet racine est ouvert
config_schema ne doit pas déclarer <keyword> à la racinela racine utilise un mot-clé de clé dynamique ; nommez plutôt chaque clé dans properties
la propriété utilise un type non pris en chargeune propriété n’a aucun type explicitement pris en charge, ou un $ref local non résoluble
config contient une propriété absente de config_schemaune clé d’opérateur n’est pas déclarée, souvent à cause d’une faute de frappe
la propriété de configuration doit être un entier JSONla chaîne stockée ne peut pas être analysée comme le type déclaré
config ne respecte pas config_schema à <path>une contrainte telle que minimum ou required a échoué

Packages de première partie

Chaque package publié dans zeroclaw-labs/zeroclaw-plugins demande config_read, et aucun n’avait déclaré config_schema au moment de l’intégration ; ils nécessitent donc tous les étapes 1 et 5. La migration est suivie dans ce dépôt plutôt qu’ici, puisque les packages sont versionnés indépendamment de l’hôte. Les packages d’outils peuvent terminer dès maintenant l’étape liée à la clé d’opérateur. Les packages exclusivement dédiés aux canaux doivent attendre le chemin de clé prenant en charge les alias indiqué ci-dessus avant que le suivi puisse les marquer comme migrés ou les publier comme migrés pour ce contrat.

Plugins de mémoire

Les plugins de mémoire ne disposent pas encore d’export de configuration et ne doivent pas demander config_read tant que cette ABI n’existe pas.