Comment les plugins fonctionnent
Cette page explique le système de plugins du point de vue de l’opérateur : comment un plugin est découvert, ce qu’il est autorisé à faire, et comment l’hôte confine un plugin non fiable. Pour le contrat sur disque qu’un auteur de plugin implémente (champs du manifeste, exports du bridge, fonctions hôte), voir Protocole de plugin.
La forme du système
Un plugin est un module WebAssembly sandboxé et un manifeste. L’hôte le charge, lit les capacités et les permissions qu’il déclare, et expose ses outils à l’agent uniquement lorsque l’opérateur a activé le système de plugins. Rien dans un plugin n’est implicite : un plugin ne dispose que des capacités déclarées dans son manifeste et autorisées par la politique de l’opérateur, et rien d’autre. Pour le développer vous-même, commencez par les guides des plugins.
Trois propriétés s’appliquent à chaque couche :
- Désactivé par défaut. Le système de plugins ne charge rien tant que
[plugins] enabled = truen’est pas activé. Une build par défaut sans configuration de plugins n’exécute aucun code de plugin. - Refuser par défaut. Un plugin accède à une capacité hôte (sortie HTTP, configuration, mémoire) uniquement en déclarant la permission correspondante dans son manifeste. Une capacité non déclarée est inaccessible, pas simplement inutilisée.
- Vérifié par la politique. Le fait qu’un plugin non signé ou non fiable soit chargé ou non relève de la décision de l’opérateur, définie une seule fois dans la configuration et appliquée uniformément lors de la découverte.
Cycle de vie du chargement d’un plugin
Lorsque le runtime construit son ensemble d’outils, le chargeur de plugins parcourt ces étapes dans l’ordre. Un plugin qui échoue à une étape antérieure n’atteint jamais une étape ultérieure.
- Gate. Si
`[plugins] enabled`estfalse, le chargeur ne fait rien. Il s’agit de la première vérification, et la plus légère. - Découverte. Le chargeur scanne le répertoire des plugins résolu (
[plugins] plugins_dir, par défaut~/.zeroclaw/plugins/) à la recherche de sous-répertoires contenant unmanifest.toml. - Valider la structure. Chaque manifeste doit déclarer au moins une capacité, et un plugin non-skill doit spécifier un
wasm_pathexistant. Un manifeste malformé est ignoré avec un avertissement et n’est jamais chargé. - Appliquer la politique de signature. Chaque plugin est vérifié par rapport à la
[plugins.security] signature_modeettrusted_publisher_keysconfigurées. Un plugin ne respectant pas la politique est exclu de l’ensemble chargé et n’est pas exposé en tant qu’outil. - Enregistrer les outils. Les plugins d’outils restants sont encapsulés comme outils d’agent et ajoutés après les outils intégrés. Le répartiteur d’outils résout les noms en privilégiant la première correspondance ; ainsi, un plugin d’outil dont le nom entre en conflit avec un nom intégré n’est jamais sélectionné : attribuez aux plugins d’outils des noms uniques. Les plugins d’outils et de compétences sont découverts automatiquement, cette énumération n’a donc lieu que lorsque
[plugins] auto_discover = true(falsepar défaut, avec verrouillage en cas d’échec) : avecenabled = truemaisauto_discover = false, aucun outil ni aucune compétence de plugin ne sont chargés, bien que les canaux que vous déclarez sous[channels.plugin.<alias>]restent activés. Le chargeur de compétences applique le même contrôleauto_discover.
L’étape de signature est celle qui est le plus facilement mal configurée, il vaut donc la peine de la comprendre isolément.
Politique de signature
Chaque manifeste de plugin peut comporter une signature Ed25519 ainsi que la clé publique encodée en hexadécimal de l’éditeur qui l’a signée. L’opérateur décide de la rigueur avec laquelle cette signature est exigée via [plugins.security] signature_mode :
| Mode | Ce qui se charge | Utiliser lorsque |
|---|---|---|
disabled | Tout plugin bien formé, signé ou non | Développement local sur les plugins que vous avez vous-même créés |
permissive | Tous les plugins bien formés, non signés, non fiables et à signatures invalides se chargent avec un avertissement. | Migration vers la signature sans casser les installations existantes |
strict | Seuls les plugins avec une signature valide d’un éditeur de confiance se chargent | Tout hôte partagé ou de production |
En mode strict, le publisher_key du manifeste doit figurer dans [plugins.security] trusted_publisher_keys, et la signature doit être vérifiée à partir des octets canoniques du manifeste. Un plugin non signé, signé par une clé non approuvée, ou dont la signature échoue la vérification est rejeté lors de la découverte et ne devient jamais un outil. La valeur par défaut est disabled afin qu’un nouveau checkout local fonctionne sans gestion des clés, mais un hôte qui charge des plugins depuis n’importe quelle source que vous ne contrôlez pas doit utiliser le mode strict.
Cette politique est appliquée de manière uniforme : le même contrôle que l’hôte applique lorsque vous listez les plugins est le contrôle que le runtime de l’agent applique lorsqu’il construit l’ensemble d’outils, de sorte qu’un plugin que vous ne pouvez pas voir en mode strict est également un plugin que l’agent ne peut pas appeler.
Capacités et permissions
Un manifest déclare deux choses distinctes, et la distinction est importante.
- Les Capacités indiquent le type d’extension du plugin :
tool,channel,memory,observerouskill. Un plugintoolfournit des outils que le LLM peut invoquer. - Autorisations définissent les services de l’hôte auxquels le code du plugin peut accéder à l’exécution : sortie HTTP, configuration, mémoire. Toute autorisation non déclarée dans le manifeste est une fonction de l’hôte que le plugin ne peut pas atteindre.
L’hôte accorde les permissions de manière restrictive : une permission que le manifeste ne déclare pas correspond à une fonction hôte que le plugin ne peut pas atteindre. La configuration est résolue à partir d’une identité d’instance émise par l’hôte ; un plugin ne peut donc pas sélectionner un autre paquet ou une autre liaison et ne lit jamais l’environnement brut du processus. http_client contrôle la surface sortante wasi:http ; la politique d’egress partagée protégée contre les SSRF reste un travail connexe de renforcement des plugins. Cette page couvre la limite de la politique de signature.
Référence de configuration
Tous les paramètres sont définis dans les chemins de configuration plugins.* et sont configurés via n’importe quelle interface de configuration (zerocode, la passerelle ou le CLI) :
# Interrupteur maître. Rien ne se charge tant que cette valeur est false.
zeroclaw config set plugins.enabled true
Charger les plugins d’outils et de compétences découverts automatiquement lors de l’exécution (par défaut : false).
# Without this, `enabled = true` activates only explicitly-declared channels.
zeroclaw config set plugins.auto_discover true
# Emplacement de découverte des plugins (par défaut : ~/.zeroclaw/plugins).
zeroclaw config set plugins.plugins_dir ~/.zeroclaw/plugins
# désactivé | permissif | strict
zeroclaw config set plugins.security.signature_mode strict
# Clés publiques Ed25519 encodées en hexadécimal autorisées à publier des plugins en mode strict.
zeroclaw config set plugins.security.trusted_publisher_keys '["a1b2c3d4e5f6..."]'
Un hôte conçu pour charger des plugins tiers doit définir enabled = true, signature_mode = "strict" et ne répertorier que les clés des éditeurs auxquels vous faites confiance. Pour charger également les plugins d’outils et de compétences détectés automatiquement, définissez aussi auto_discover = true ; cette option est false par défaut, si bien que enabled = true seul n’active que les canaux que vous déclarez sous [channels.plugin.<alias>], sans activer d’outils ni de compétences de plugin. Un hôte qui n’exécute que les plugins que vous développez vous-même peut conserver signature_mode à sa valeur par défaut disabled pendant le développement, puis durcir ce paramètre avant de partager l’hôte.
Ce qu’un plugin ne peut toujours pas faire
Même avec toutes les autorisations accordées, le sandbox limite un plugin :
- Il s’exécute en tant que module WebAssembly sans accès implicite au processus hôte ou au système de fichiers en dehors de son espace de travail raciné. La sortie réseau est contrôlée par l’autorisation HTTP ; la limite de sortie protégée contre les attaques SSRF est elle-même assurée par les travaux de durcissement du plugin compagnon.
- Un outil de confiance ou un plugin de canal peut lire le texte en clair d’un secret désigné par le schéma via son import
secrets.getlimité à sa portée lors d’un appel de service autorisé. Les outils y accèdent pendantexecute. Les canaux reçoiventconfig.getetsecrets.getpendantconfigureet lors des appels opérationnels ; les lectures effectuées au cours d’un même appel utilisent une révision canonique unique, de sorte qu’une rotation public/secret associée à la même liaison est disponible lors de l’opération suivante. L’instanciation et la découverte des métadonnées statiques ne peuvent utiliser aucun de ces imports. L’hôte empêche l’injection de configuration publique et la sélection inter-instance, mais un import renvoyant du texte en clair ne peut pas empêcher un invité malveillant de conserver ce qu’il lit. Les plugins de canal conformes doivent résoudre la configuration et les identifiants à chaque point d’utilisation. - Il ne peut pas remplacer un outil intégré : les outils intégrés s’enregistrent en premier et le dispatch des outils privilégie le premier match, donc un outil plugin en conflit n’est tout simplement jamais sélectionné.
Les limites du bac à sable et de l’espace de noms s’appliquent quels que soient les efforts du code du plugin. La règle de non-conservation fait en revanche partie du contrat de confiance entre le canal et le plugin, raison pour laquelle l’examen par l’éditeur et la politique de signature restent importants.