プラグインの仕組み
このページでは、プラグインシステムをオペレーターの視点から説明します。プラグインがどのように検出されるか、何を行うことが許可されているか、そしてホストが信頼できないプラグインをどのように封じ込めるかについて解説します。プラグイン作成者が実装するディスク上のコントラクト(マニフェストフィールド、ブリッジのエクスポート、ホスト関数)については、Plugin protocolを参照してください。
システムの形
プラグインは、サンドボックス化された WebAssembly モジュールとマニフェストです。ホストはそれを読み込み、宣言されたケーパビリティとパーミッションを読み取り、オペレーターがプラグインシステムをオンにした場合にのみ、そのツールをエージェントに公開します。プラグインについて暗黙的なものは何もありません。プラグインは、マニフェストが宣言し、オペレーターのポリシーが許可するケーパビリティのみを取得し、それ以外は何も取得しません。自分で構築するには、プラグインガイド から始めてください。
すべてのレイヤーで3つのプロパティが成り立ちます:
- デフォルトでは無効です。 プラグインシステムは
[plugins] enabled = trueでない限り何も読み込みません。プラグイン設定のないデフォルトビルドでは、プラグインコードは実行されません。 - デフォルトで拒否。 プラグインは、マニフェストで対応する権限を宣言することによってのみ、ホストの機能(HTTP egress、config、memory)に到達できます。宣言されていない機能は、単に未使用なのではなく、到達不能です。
- ポリシーにより検証済み。 未署名または信頼されていないプラグインが読み込まれるかどうかはオペレーターの決定であり、設定で一度設定され、検出時に一律に適用されます。
プラグインのロードのライフサイクル
ランタイムがツールセットを構築する際、プラグインローダーはこれらのステージを順番に実行します。前のステージで失敗したプラグインは、後のステージに到達することはありません。
- ゲート。
[plugins] enabledが false の場合、ローダーは何もしません。これは最初で最も安価なチェックです。 - Discover. ローダーは解決されたプラグインディレクトリ(
[plugins] plugins_dir、デフォルトは~/.zeroclaw/plugins/)をスキャンし、manifest.tomlを含むサブディレクトリを探します。ドットで始まるディレクトリはスキップされます。これにはzeroclaw plugin installがパッケージを構築してから配置先にリネームするまでの間に使う隠しステージングディレクトリも含まれるため、書き込みが途中で終わったインストールが読み込まれることはありません。 - 形状を検証する。 各マニフェストは少なくとも1つのcapabilityを宣言しなければならず、スキル以外のプラグインは範囲内に閉じ込められた相対パスの
wasm_pathを指定しなければなりません。トラバーサルパスおよびシンボリックリンクのパスは拒否されます。不正な形式のマニフェストは警告とともにスキップされ、決して読み込まれません。 - 署名ポリシーを強制する。 各プラグインは、設定された
[plugins.security] signature_modeとtrusted_publisher_keysに対してチェックされます。ポリシーに失敗したプラグインは、読み込まれたセットから除外され、ツールとして表示されません。 - 実行可能バイト列の受け入れ。 ホストは閉じ込められたコンポーネントを一度だけ開き、宣言された
wasm_sha256があればそれを検証し、そのバイト列をそのまま保持する。strictモードでは、署名付きマニフェストがこのダイジェストを宣言していなければならない。アダプタはパスを開き直すのではなく、受け入れ済みのバッファをコンパイルする。Unix では、ホストはパッケージディレクトリを一度だけ開き、そのハンドルからコンポーネントまでパス要素を一つずつたどる。その際シンボリックリンクは決して追わない。そのため、受け入れ中にディレクトリを移動または置き換えても、受け入れは失敗するか、読み取りは閉じ込められたパッケージ内にとどまる。コンポーネントは通常ファイルでなければならず、FIFO やデバイスの場合はブロックせず即座に失敗する。パッケージディレクトリとマニフェストは、受け入れ開始時点ではパス名によって探索される。strictモードでは、署名付きのwasm_sha256がマニフェストとバイト列を結び付ける役割を果たす。その他のプラットフォームでは引き続きパス名チェックを用いる。このチェックは実行時点で置き換えが残っていれば拒否するが、探索をアトミックにするものではなく、FIFO による open のブロックを防ぐものでもない。 - ツールを登録する。 生き残ったツールプラグインはエージェントツールとしてラップされ、組み込みツールの後ろに追加されます。パッケージ名またはツール名が既に登録済みのツールと衝突するプラグインは、登録されず警告とともに拒否されます。詳細は Tool name conflicts を参照してください。ツールプラグインとスキルプラグインは_自動検出_されるため、この列挙は
[plugins] auto_discover = true(デフォルトはfalse、フェイルクローズ)の場合にのみ行われます。enabled = trueかつauto_discover = falseの場合、プラグインのツールもスキルも読み込まれませんが、[channels.plugin.<alias>]に手動で、またはzeroclaw plugin bindで宣言したチャネルは引き続き有効になります。スキルローダーも同じauto_discoverのゲートを適用します。
署名ステージは最も設定を誤りやすいステージであるため、単独で理解しておく価値があります。
レガシーインストールの復旧
zeroclaw plugin migrate を実行すると、レガシーのパッケージディレクトリを [plugins] plugins_dir へ移動します。自動リカバリは、設定されたデータルートの plugins/、分割されていない <install>/workspace/plugins、そして data フィールドの値が異なる場合でも、V2 のプロデューサーが実際に書き込む <install>/data/plugins を検索します。新しい V2 ファイルシステム分割では、プラグインがホストのデータ領域へ移動されるため、新規および再開された分割でも到達可能な状態が保たれ、デフォルトエージェントの書き込み可能なライブ workspace をリカバリ元として使用することはありません。ホストのデータ領域は、必ずしも設定された実行時のプラグインディレクトリとは限りません。その後の再配置は plugin migrate が行います。設定された移行先およびその正規の別名は除外され、重複するルートは一度だけ報告されます。
レガシールートのサポートされているエイリアスは追跡されます。正規のターゲットエイリアスは除外され、同じルートのエイリアスは重複排除されます。既知のエージェントワークスペースと重なるルートは、そのワークスペース内にある場合でも、それを包含する場合でも、エイリアスを含めて除外されます。既知のロールは、設定された各エージェントの実際のワークスペースパス、暗黙のデフォルト、および過去のデフォルトワークスペースから取得されます。既存のパスは正規形で比較されます。存在しない宣言済みワークスペースは、利用可能な場合は既存の祖先エイリアスを解決したうえで、正規化された字句パスとして保守的に比較されます。選択処理では、存在しないワークスペースを作成することはなく、宣言されたパスを古い移行の証拠として扱うこともありません。
これはルート選択に関する変更です。既存の移動処理は、対象となるルートを依然として独自のパッケージチェックで再スキャンします。回復ヒントと移動処理の両方が、ランタイムの探索と一致するよう、通常の子パッケージディレクトリを要求します。子ディレクトリのシンボリックリンクは移動元に残されます。通常のパッケージを含むルートであっても、移動も実体化もされません。通常のパッケージディレクトリ内にあるマニフェストのエイリアスは、既存のマニフェスト読み込みおよび受け入れチェックを引き続き使用します。
この変更は、グローバルなno-follow保証、認証済みスナップショット、新しい並行実行時の上書き防止の契約を作るものではありません。既存の同名宛先チェックにより、衝突するパッケージはソース側に残され、オペレーターが解決して再試行できるようになっています。
ディレクトリ移行ヘルパーは、プラグインの設定、付与された権限、発行元の信頼、有効化状態を編集しません。コマンドの前に行われる設定の読み込みでは、通常の初期化および移行処理が引き続き行われます。パッケージを移動すると、通常の検出の対象になります。プラグインが有効で auto_discover = true が設定されている場合、ツールおよびスキルのプラグインは、受け入れ審査を経たうえで利用可能になることがあります。移動そのものでは、ABI互換性の検証も配布元の認証も行われません。
パッケージはすでにエージェント用ワークスペースに分割されています
以前のバージョンでは、パッケージを <install>/agents/default/workspace/plugins に移動できた場合がありました。この過去の場所と、カスタムのライブエージェントワークスペースは、もはや plugin migrate の自動的な移行元ではありません。マニフェスト、一致するバックアップ名、または呼び出し側が書いたマーカーでは、ディレクトリ全体の取り込みは許可されません。取り込みたい各パッケージを確認し、そのうえで既存の install コマンドで個々のディレクトリを選択してください:
zeroclaw --config-dir /operator/install plugin install \
'/operator/install/agents/default/workspace/plugins/package-name'
実際のインストールパスとパッケージパスを使用してください。plugins/ ディレクトリ全体を渡さないでください。バックアップ内のパッケージも同様に、実際のパスを指定して明示的に選択できます。権限は、パスの見かけ上の履歴ではなく、オペレーターのインストール操作によって与えられます。これには、プラグインCLIサポートを備えたビルドが必要です。
これは互換性のあるパッケージの新規インストールです。ホストは現行のマニフェスト、署名、ペイロード、設定スキーマのルールを適用します。CLI は既定で、実行可能パッケージを WIT ABI および制限値に照らして読み込み時に検証します。厳格ポリシーでの未署名・信頼されていないパッケージ、不完全な旧マニフェスト、互換性のないコンポーネント、サポートされていない旧形式の設定エントリは拒否されることがあります。再試行する前に、診断メッセージと型付き設定への移行ガイドに従ってください。旧形式の変換や署名ポリシーのバイパスは提供されていません。同名のインストール済みパッケージは、黙って置き換えられるのではなく拒否されます。
明示的なインストールではソースディレクトリが保持されます。承認済みのマニフェストと実行可能ペイロードを書き込み、宣言されたスキルのコンテンツをコピーします。任意の補助ファイルはディレクトリ全体の復元契約ではなく、スキルのコピーは不変の世代スナップショットではありません。ホスト所有の状態を必要とするツールバインディングについては、インストールによって不足している型付き設定行を作成し、宣言された HTTP egress の宛先を新しい行に設定できます。既存の行と権限付与は自動的には拡張されず、古いパッケージ名の行は黙って変換されません。実行時利用を有効にする前に、これらのインストールによる影響を確認し、必要な値を設定してください。したがって、明示的な採用は、自動移行ヘルパーの設定/権限付与の変更なし動作とは別のものです。
署名ポリシー
すべてのプラグインマニフェストには、Ed25519 署名と、署名した公開者の 16 進数エンコードされた公開鍵を含めることができます。オペレーターは、[plugins.security] signature_mode を通じて、その署名をどの程度厳密に強制するかを決定します:
| モード | 何が読み込まれるか | 使用するタイミング |
|---|---|---|
disabled | 署名の有無にかかわらず、すべての整形式のプラグイン | 自分で構築したプラグインに対するローカル開発 |
permissive | すべての整形式プラグイン。未署名、信頼されていない、無効な署名は警告付きでロードされます | 既存のインストールを壊さずに署名へ移行する |
strict | 信頼できる発行元からの有効な署名を持つプラグインのみが読み込まれます | 任意の共有または本番ホスト |
strict モードでは、マニフェストの publisher_key が [plugins.security] trusted_publisher_keys に含まれている必要があり、署名は正規化されたマニフェストのバイト列に対して検証されなければなりません。実行可能プラグインは、許可されたバイト列と完全に一致する署名付きの wasm_sha256 も宣言する必要があります。いずれかのチェックに失敗したプラグインは検出時に破棄され、ツールとして登録されることはありません。デフォルトは disabled で、鍵管理を行わなくても新規のローカルチェックアウトがそのまま動作しますが、管理外の場所からプラグインを読み込むホストでは strict で実行すべきです。
このポリシーは一律に適用されます。プラグインを一覧表示する際にホストが適用するチェックと同じものが、エージェントランタイムがツールセットを構築する際にも適用されます。そのため、strict モードで表示されないプラグインは、エージェントも呼び出せないプラグインとなります。
ツール名の競合
Tool プラグインはパッケージ名の順に登録され、ホストは各プラグインを2回チェックします。登録時には名前の衝突を拒否し、あるツールが別のツールを上書き(シャドウ)することはありません:
- パッケージ名。 パッケージ名(マニフェストの
name)が、登録済みのツールと一致するプラグインは、そのコンポーネントがインスタンス化される前に拒否されます。ホストは、attributes内にerror_keyplugin_package_name_conflictを持つWARNイベントをログに記録します。 - ツール名。 ゲストは独自のツール名を宣言するため、ホストはコンポーネントをインスタンス化してメタデータを読み取って初めてその名前を知ります。ツール名が既に登録済みのツールと一致するプラグインは登録されません。ホストは
attributes内にerror_keyとしてplugin_tool_name_conflictを含むWARNイベントをログに記録します。__を含むツール名は、まだ誰も保持していない場合でも拒否されます。MCPツールは<server>__<tool>という名前を持ち、プラグインツールの後に登録されるためです。この拒否にはerror_keyとしてplugin_tool_name_reservedが付与されます。
チェックされる名前は、そのレジストリのビルドで既に登録されているツールです。同じパス内で先に受け入れられたプラグインツールも含みます。[pipeline] enabled = true の場合は execute_pipeline も含まれます。これはすべての組み込み名を固定で列挙したリストではありません。設定セクションが無効になっているなど、ビルドで登録されない組み込みは、そのビルドでは予約されていません。MCP名前空間を除き、プラグイン登録の後にレジストリへ追加されるツールはこのチェックの対象外です。プラグインパッケージとツールには、この仕組みに頼るのではなく、最初から一意な名前を付けてください。
ケーパビリティと権限
マニフェストは2つの別々のものを宣言し、その区別は重要です。
- Capabilities は、プラグインがどの種類の拡張であるかを示します:
tool、channel、memory、observer、またはskill。toolプラグインは、LLM が呼び出せるツールを提供します。 - パーミッションは、プラグインのコードが実行時に到達できるホストサービスです: HTTP エグレス、構成、メモリ。マニフェストで宣言されていないパーミッションは、プラグインが到達できないホスト関数です。
ホストは権限を限定的に付与します。マニフェストで宣言されていない権限に対応するホスト関数には、プラグインはアクセスできません。設定はホストが発行したインスタンス ID から解決されるため、プラグインが別のパッケージやバインディングを選択することはできず、生のプロセス環境を読み取ることもありません。http_client は外向きの wasi:http サーフェスへのアクセスを制御します。共有の SSRF 対策付きエグレスポリシーは、引き続き別途行うプラグイン強化作業です。このページでは署名ポリシーの境界を扱います。
設定リファレンス
すべての設定は plugins.* 設定パス配下にあり、任意の設定サーフェス(zerocode、ゲートウェイ、または CLI)経由で設定されます:
# マスタースイッチ。これが false の間は何もロードされません。
zeroclaw config set plugins.enabled true
# 実行時に自動検出されたツールおよびスキルのプラグインを読み込む(デフォルト: false)。
# Without this, `enabled = true` activates only explicitly-declared channels.
zeroclaw config set plugins.auto_discover true
# プラグインが検出される場所(デフォルト: ~/.zeroclaw/plugins)。
zeroclaw config set plugins.plugins_dir ~/.zeroclaw/plugins
# disabled | permissive | strict
zeroclaw config set plugins.security.signature_mode strict
# 厳格モードでプラグインの公開が許可された、16進数エンコードされた Ed25519 公開鍵。
zeroclaw config set plugins.security.trusted_publisher_keys ["a1b2c3d4e5f6..."]
サードパーティ製プラグインを読み込むホストでは、enabled = true、signature_mode = "strict" を設定し、信頼する発行元のキーのみを列挙します。自動検出されたツールおよびスキルプラグインも読み込むには、auto_discover = true も設定します。これはデフォルトでは false であるため、enabled = true だけでは、[channels.plugin.<alias>] の下で宣言したチャネルだけが有効になり、プラグインのツールやスキルは有効になりません。自分で構築したプラグインだけを実行するホストでは、開発中は signature_mode をデフォルトの disabled のままにし、ホストを共有する前に制限を強化できます。
チャネルのバインディングは plugins.* の外側、[channels.plugin.<alias>] の下に置かれます。このテーブルは手動で書くこともできますし、インストール済みのチャネルパッケージに対して zeroclaw plugin bind <package> --channel-alias <alias> を実行して自動生成させることもできます。このコマンドはインスタンスの [[plugins.entries]] 行も作成します。宣言された接続先を持つマニフェストから行を作成する前に、明示的な --egress declared または --egress none フラグを必須とし、その接続先に到達できるトランスポート(http_client、websocket_client、socket_client のいずれか)を保持していることを確認します。また、インスタンスを起動する前に何が不足しているかを報告します。plugins.enabled は設定せず、エージェントの channels も編集しません。詳細は チャネルインスタンスのバインド を参照してください。
リリースバイナリからプラグインを実行する
プラグインシステムは実験的な機能です。wit/v0 の契約は不安定なため、プラグインは、それを実行するリリースに同梱の WIT に対してビルドする必要があります。不一致は、マニフェストではなくインストール時のロードチェックで検出されます。この制約の範囲内で、64ビットのデスクトップおよびサーバー向けターゲットのリリースアーカイブには、プラグインホストと、.wasm コンポーネントを読み込むコンパイラが含まれています。また、すべてのリリースは公開前に、これらのアーカイブそれぞれからプラグインを 1 つ実行します。32ビット ARM のアーカイブ、実験的な Android アーカイブ、および Cargo のデフォルト設定でのソースビルドには、プラグインホストは含まれていません。すべてのアーティファクトの一覧は プロトコルのページ にあります。リリースバイナリでプラグインを実行するには、次の 4 つの運用上の判断が必要です。いずれも既定では無効になっています。
-
パッケージをインストールする。 ローカルパッケージの場合は
zeroclaw plugin install <directory>、プラグインレジストリのパッケージの場合はzeroclaw plugin install <name>を実行します。インストールでは、マニフェストの検証、署名ポリシーの実行、マニフェストが指定している場合のペイロードダイジェストの確認を行い、このホストの WIT に対してコンポーネントをインスタンス化します(--no-verifyを指定するとこのインスタンス化は省略されます)。これらのいずれかに失敗したパッケージはインストールされません。インストール(--no-verifyを指定しない場合)、zeroclaw plugin info <name>、zeroclaw plugin list --verifyは、プラグインシステムが有効かどうかにかかわらず、コンポーネントのコンパイルとインスタンス化を行います。インスタンス化ではコンポーネントのエクスポートは一切呼び出されませんが、コンポーネントのコアモジュールが持つ開始関数(start function)は実行されます。その実行環境はファイルもネットワークもないストアであり、呼び出しごとの燃料(fuel)、メモリ、時間の上限が適用されます。 -
プラグインシステムを有効にする。
zeroclaw config set plugins.enabled trueを実行し、続けてzeroclaw config set plugins.auto_discover trueを実行すると、ツールおよびスキルのプラグインが列挙されます。両方が設定されるまで、インストール済みのツールまたはスキルのパッケージは何も提供しません。両方が設定されると、ツールレジストリのビルドのたびに、許可された各ツールコンポーネントがコンパイルされてインスタンス化され、その名前、説明、パラメータスキーマが問い合わされます。これはパッケージで宣言された権限と送信ポリシーの下で動作するゲストコードであり、承認の前に実行されます。その後、そのツールがモデルに提示されます。チャンネルパッケージには、
plugins.enabled、[channels.plugin.<alias>]の宣言、そしてplugin.<alias>をチャンネルとして含む有効なエージェントが必要です。auto_discoverはこれに関与しません。providesを通じてネイティブチャンネルをミラーするパッケージの場合、[channels.<provides>.<alias>]の下でのアクティベーションは記録されますが、まだ構築されません。同じパッケージを[channels.plugin.<alias>]の下にバインドすると、実行されます。 -
承認を行う: プラグインツールの呼び出しも、事前承認されていない他のツールと同様にオペレーターの承認が必要です。CLI はターミナル上で確認を求め、チャンネルは承認カードを送信します。エージェントにプラグインツールを無人で呼び出させるには、そのエージェントのリスクプロファイルの
auto_approveリストにツールを追加します。例:zeroclaw config set risk_profiles.default.auto_approve '["<tool-name>"]'。config setはリスト全体を置き換えます。組み込みのデフォルトは設定の読み込み時にrisk_profiles.defaultにマージし直されますが、自分で追加したエントリは再度指定する必要があります。別名のプロファイルは、指定したものだけを保持します。level = "full"と*ワイルドカードは、always_askに含まれないすべてのツールを事前承認します。「Always」と回答すると、そのツールは現在の CLI セッションまたはチャンネルのターンの残りの間事前承認されますが、always_askに含まれるツールは毎回確認を求められます。承認者がいない場合、呼び出しは拒否され、実行されません。 -
署名ポリシーを選択する。
signature_modeはデフォルトでdisabledのままです。そのため、自分で作成したプラグインも鍵管理なしでインストールできます。strictの場合、trusted_publisher_keysにある鍵で署名されたパッケージだけが読み込まれます。公式レジストリは現時点で未署名のパッケージを公開しているため、発行者が署名するまでは、どのパッケージもstrictモードではインストールされません。
コンポーネントのコンパイルには、書き込んだ後に実行可能になるメモリが必要です。MemoryDenyWriteExecute=yesを指定したsystemdユニット(Nixモジュールのデフォルト)のように、そのようなメモリを禁止するホストでは、コンパイラがコードを実行可能としてマップできないため、すべてのプラグインの読み込みが拒否されます。デーモンの他の部分はそのようなマッピングを必要としないため、引き続き動作します。
ロールバックするには、zeroclaw plugin remove <name> でパッケージを削除するか、plugins.enabled = false を設定して、アンインストールせずにすべてのプラグインの読み込みを停止します。プラグインホストなしのリリースに戻すには、zeroclaw update --version <previous> --force でバイナリを固定します。--force がない場合、アップデーターは古いバージョンへの移行を拒否します。アップデーターは Linux musl ホストを glibc アーカイブに解決するため、パッケージマネージャーからインストールされた musl 環境などは、以前のアーカイブまたはパッケージを再インストールすることでロールバックします。
プラグインでまだできないこと
すべての権限が付与されていても、サンドボックスはプラグインを制限します:
- WebAssembly モジュールとして実行され、ホストプロセスやルート化されたワークスペース外のファイルシステムへの環境アクセスはありません。ネットワークエグレスは HTTP パーミッションによってゲートされ、SSRF ガードされたエグレス境界自体は、コンパニオンのプラグインハードニング作業によって提供されます。
- 信頼されたツールまたはチャネルプラグインは、認可されたサービス呼び出し中に、スコープ付き
secrets.getインポートを通じて、スキーマで指定されたシークレットの平文を読み取れます。ツールはexecute中にアクセスを受け取ります。チャネルはconfigureおよび運用呼び出し中にconfig.getとsecrets.getへのアクセスを受け取ります。1 回の呼び出し内での読み取りには 1 つの正規リビジョンが使用されるため、同一バインディングの公開設定とシークレットのローテーションは次の操作で利用可能になります。インスタンス化と静的メタデータの検出では、どちらのインポートも使用できません。ホストは公開設定の注入とクロスインスタンス選択を防ぎますが、平文を返すインポートでは、悪意のあるゲストが読み取った内容を保持するのを防げません。準拠するチャネルプラグインは、使用する各時点で設定と認証情報を解決する必要があります。 - It は既に登録されているツールの名前を使用できません。登録時には、競合するプラグインのツールを登録せず、その登録を拒否します。対象となる範囲は ツール名の競合 に記載のとおりです。
サンドボックスと名前空間の制約は、プラグインコードが何を試みるかにかかわらず適用されます。一方、保持禁止ルールは信頼されたチャネルプラグイン契約の一部であり、だからこそ発行元の審査と署名ポリシーが依然として重要です。