Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

ピアグループ

ピアグループは、エージェントがチャネル上でどこからの受信メッセージを受け入れるか、またそこでどの他のエージェントとメッセージを交換できるかを宣言します。これはチャットチャネルの受信ゲートであり、エージェント間ディスパッチのためのルーティングプリミティブです。設定では [peer_groups.<name>] に配置されます。ピアグループがエージェントの構成にどのように組み込まれるかについては、Agents を参照してください。

受信送信者は、チャンネルインスタンスに対して解決されたピアセットに基づいて制限されます。このピアセットは、channel がチャンネルタイプまたはそのドット区切りのエイリアスと一致するすべての [peer_groups.<name>] ブロックから取得されます。照合では先頭の @ を取り除き、チャンネルのネイティブな送信者識別子に対して大文字小文字を区別せずに比較します。空のセットはすべての送信者を拒否します。"*" を含むセットは誰でも受け入れます。それ以外の場合は、リストされた external_peers(およびピアエージェント)のみを受け入れます。これは、ゲートウェイのペアリング([gateway] require_pairing)とは別のものです。ゲートウェイのペアリングは HTTP/WebSocket クライアントを認証するものであり、チャットチャンネルの送信者を認証するものではありません。

フィールド

[peer_groups.<name>] ブロックには以下が含まれます:

フィールド意味
channelチャネルタイプ("telegram"、そのタイプのすべてのエイリアスに適用されます)またはドット付きエイリアス("telegram.work"、その特定のインスタンスにのみ適用されます)。
agentsエイリアスによるメンバーエージェント。2つのエージェントは、両方が同じグループに属している場合にのみピアとなります。メンバーシップは相互的です。
external_peersチャンネルのネイティブなユーザー名/IDによる非エージェントメンバー。["*"] は誰でも受け入れます。空の場合は誰も受け入れません。
ignoreグループごとのブロックリスト。解決されたピアセットとともに引き継がれ、ワイルドカードを含むあらゆる権限付与より優先される。チャネルのアイデンティティルールに基づき、そのチャネルによって適用される。
output_modalityグループの優先返信モダリティ: mirror(入力に応じる、デフォルト)、voice(音声対応チャンネルでは常にTTSノートとして返信し、プロアクティブなメッセージを配信する)、または text(常にテキスト)。
admin_for_agent_scopetrue の場合、グループの external_peers はバインドされたエージェントに対して /model --agent <model> を発行する権限を持ちます。デフォルトは false(デフォルトで拒否)です。管理者エージェントスコープ承認を参照してください。

解決

外部送信者の認可はチャネル単位で範囲が定められます。ランタイムは、チャネル種別またはドット区切りのエイリアスに一致するすべてのグループから external_peers を和集合として取り込み、一致するすべてのグループの ignore リストも予約済みの !name エントリとして併せて渡します。両方ともチャネルに渡され、チャネルは許可より先に拒否を適用します。

拒否は、リゾルバーではなくチャネルによって適用されます。 2つのエントリが同じアカウントを指しているかどうかを判断できるのは、チャネルだけです。Reddit は u/alice と alice を同一ユーザーとして扱い、WhatsApp は +1555… と 1555… を同一の番号として扱い、Nostr は npub を hex に書き換えます。リゾルバーが無視対象のエントリを自分で取り除く場合、その判断を推測しなければならず、推測が外れると、チャネルが拒否を適用する前に拒否が落とされ、オペレーターが記載した送信者が通過してしまいます。したがって ignore は、アイデンティティを理解しているチャネルの中以外では、どこでも差し引かれることはありません。

ignore は、ワイルドカードを含め、許可(grant)よりも優先されます。一方のマッチするグループに external_peers = ["*"] があり、別のグループに ignore = ["alice"] がある場合、alice を除くすべてのユーザーが認可されます。ignore = ["*"] はその逆で、すべての送信者を拒否します。拒否は、同じ文字列がそのチャネルで許可するものと一致し、さらに大文字小文字と先頭の @ を無視します。ブロックリストは拒否する方向に倒すべきだからです。1 つのアカウントに対して複数の識別子を受け付けるチャネルでは(Bluesky はハンドルまたは DID を受け付けます)、そのいずれか 1 つを無視すると、そのアカウントは拒否されます。

ワイルドカードは前後の空白を取り除いた状態で認識されるため、[" * "] と ["*"] はルールの両方の側で同じ意味を持ちます。

! は解決済みリストにおいて拒否マーカーを開始します。RFC 5322 では電子メールのローカルパートに ! を含めることが認められているため、ID が ! で始まることも正当です。したがって決定を担うのは単なる有無ではなく、先頭マーカーの 個数 です。先頭に k 個のマーカーを持つ ID は、許可としては 2k、拒否としては 2k + 1 として出力されます。つまり偶数個の連続は許可、奇数個の連続は拒否を意味し、連続数を半分にすると ID が復元されます。

identityexternal_peersとしてignore
alicealice!alice
!user@example.com!!user@example.com!!!user@example.com

! で始まらない識別子は従来とまったく同じようにエンコードされるため、既存の設定が異なる意味に読まれることはありません。運用者が記述できる内容は、どちらの方向においても黙って反転されることはありません。アドレスはそのまま記述します。二重化は解決済みリストの内部でのみ行われます。

channel で複数の書き方をされたチャネル(WeCom WebSocket は wecom-ws と wecom_ws の両方に応答する)は、1つのセットとして解決される。各表記を個別に解決して結果を結合すると、一方の表記にワイルドカード、もう一方の表記に ignore が設定された場合に、互いの存在を認識しないままになってしまう。起動経路はすべて同じ解決処理を使わなければならない。そのため、デーモンの通常のチャネル構築と単一チャネルのワンショット実行の両方が、まさにこの理由から、名前付きの一つのヘルパーを経由する。

Deny が解決済みリストの中に含まれているため、チャネルは allowlist::is_user_allowed、is_user_allowed_by、あるいは送信者が複数の識別子を持つ場合は is_identity_allowed を通じて送信者を認可します。リストに対して "*" を直接テストしたり、識別子を1つずつ確認したりすると、オペレーターが除外した送信者を通してしまいます。

解決済みリストは認可の入力であり、アドレスの一覧ではありません。ワイルドカードと拒否マーカーを含みます。メッセージの送信先が必要なコードは Config::channel_addressable_peers に問い合わせます。このメソッドは、到達可能な単一のアカウントを指定するエントリのみを返します。

同じ理由から、「リストが空かどうか」は「誰かが認可されているかどうか」とは異なります。ignore はあるものの external_peers がないグループは、誰にも許可を与えない空でないリストに解決されます。そのため、まだペアリングされていないかを判定するチャネルは、リストが空かどうかを調べるのではなく allowlist::grants_anyone を問い合わせます。また、自身の ignore によって取り消された許可も数えません。external_peers = ["alice"] と ignore = ["alice"]、または ["*"] と ignore = ["*"] の組み合わせでは、誰も認可されないため、この結果に基づいてペアリングを制御するチャネルは、引き続きバインドコードを提示し続けます。

ペアリングは続いて external_peers に書き込むため、同じ ignore がペアリングで保存されたばかりの内容を遮蔽しうる状態になります。動作しないバインドを報告するのではなく、書き込みは拒否され、オペレーターは先に ignore エントリを削除するよう指示されます。明示的なブロックリストのエントリは、ペアリングの試行よりも優先されます。これはすべての書き込み元に当てはまり、チャネル内の /bind のやり取り、zeroclaw channel bind-telegram、POST /api/channels/bind のいずれも拒否します。この拒否は、すでに grant として登録されている ID にも及びます。ignore によって遮蔽された grant は、有効なバインディングでもないためです。

クロスエージェントのルーティングはエージェント単位で行われます。特定のエージェントについて、ランタイムはそのエージェントが所属するすべてのグループを走査し、グループのチャネル上にある他のメンバーのエイリアスを和集合として取り、そこから ignore を差し引きます。自己ループを避けるため、エージェント自身のエイリアスも防御的に除外されます。どのピアグループにも属さないエージェントは単独で実行され、クロスエージェントのディスパッチは行われません。

各チャネルが照合する送信者識別子は、プラットフォームによって異なります(Telegram のユーザー ID、Matrix の @user:server、E.164 形式の番号、UUID など)。各チャネルのページに、想定される識別子の形式が記載されています。

Example

my_discord_group のような名前の discord ピアグループでは、channel = "discord" を設定し、external_peers で 111111111111111111 を許可し、ピアエージェントに researcher、summariser という名前を付け、ignore を介して 222222222222222222 をブロックします。これは gateway ダッシュボード、zerocode、または zeroclaw config set を使って設定します。

各チャネルページには、そのチャネルの送信者識別子の形式を持つディレクティブ形式が表示されます。

管理者エージェントスコープ認可

admin_for_agent_scope = true はグループの権限境界を拡張します。ルーティング可能なピアであることに加え、各 external_peers メンバーはバインドされたエージェントに対して /model --agent <model> を発行できるようになります。すなわち、エージェントの バインディング をデフォルト以外のモデルに切り替えられます。このフラグはデフォルトで拒否されます。明示的に true に設定されていないすべてのグループはこの機能を拒否し、これには空でない external_peers を持つグループも含まれます。/model --user <model>(セッション限定のオーバーライドで、エージェントの再バインディングは行われません)はこのフラグによって制御されず、受け入れられたすべてのピアが利用できます。

オーケストレーターは、認可された管理者セットを Config::channel_agent_scope_admins からライブで解決しますが、ディスパッチゲートはランタイムの起動時に使用された設定スナップショットを通じて読み取ります。したがって、admin_for_agent_scope(またはグループの external_peers / channel / agents)へのオペレーターによる編集は、実行中のプロセスではなく、次回のデーモン再起動時に反映されます。稼働中のデーモンでフラグを切り替えても、それ自体では現在のセッション内で新しい送信者が認可されることはありません。

これは意図的な動作です。認可は起動時に不変となる設定に対して計算されるため、実行中に管理グループへ_追加された_ピアは、現在のプロセスの存続期間中に権限を昇格させることはできません。この集合が再解決されるのは完全な設定リロード時のみであり、これ自体が再起動のパスとなります。スコープ内にあると想定していた送信者に対して /model --agent の呼び出しが「not authorized」と報告する場合は、admin_for_agent_scope を編集した後にデーモンを再起動し、さらなる結論を導く前に新しいクライアントセッションからコマンドを再発行してください。