
AIエージェントに対して、署名付き・スコープ限定の認証情報を用いて最小権限の委任を強制します。サブエージェントには狭い範囲の機能とリソースのみを付与し、実行前にアクションを検証して、権限昇格を防止します。
あなたのエージェントがサブエージェントを生成し、同じAPIキーを渡しました。そのサブエージェントは、本番環境へのデプロイ、支払いデータベースの読み取り、mainへのマージができるようになりました。
Pigeonはそれを阻止します。子エージェントにはPigeon Passを渡します。これは、あなたができることのコピーではなく、実行が許可された内容に絞り込まれた署名付き認証情報です。
Python 3.12以降。
git clone https://github.com/pigeonlabsHQ/pigeon.git
cd pigeon
pip install .
from pigeon import grant, verify
authority = grant(
subject="agent:deployer",
capabilities=["deploy"],
resources=["environment:staging"],
)
allowed = verify(authority, action="deploy", resource="environment:staging")
assert allowed.allowed
denied = verify(authority, action="deploy", resource="environment:production")
assert not denied.allowed
assert denied.reason_code == "RESOURCE_NOT_ALLOWED"
print(denied.reason_code, denied.message, denied.details)
verifyは単純なブール値を返しません。拒否には理由コード、メッセージ、および失敗した比較(requested vs allowed)が含まれます。
自分で書かずに試すには:
python examples/01_infrastructure.py
python demo/agent.py
接続するPigeonサーバーはありません。既存の2つの場所を変更するだけです。
delegate(...)を呼び出して子エージェントにPassを渡します。verify(...)を呼び出し、拒否された場合はツールを実行しないでください。実際の秘密情報はランナー上に保持します。子エージェントはPassを保持します。
from pigeon import delegate, grant, verify, DelegationError
parent = grant(
subject="agent:orchestrator",
capabilities=["deploy", "open_pr"],
resources=["environment:staging", "repo:acme/api"],
constraints={"max_deploys_per_hour": 3},
)
worker = delegate(
parent,
subject="agent:pr-bot",
capabilities=["open_pr"],
resources=["repo:acme/api"],
constraints={"max_deploys_per_hour": 3}, # 親の制約を削除することはできない
)
result = verify(worker, action="open_pr", resource="repo:acme/api")
assert result.allowed
denied = verify(worker, action="deploy", resource="environment:staging")
assert denied.reason_code == "CAPABILITY_NOT_GRANTED"
try:
delegate(worker, "agent:rogue", ["open_pr", "deploy"], ["repo:acme/api"])
except DelegationError as exc:
assert exc.reason_code == "PRIVILEGE_ESCALATION"
子エージェントは、機能を追加したり、リソースを拡大したり、制限を引き上げたり、親の制約を削除したりすることはできません。Pigeonが子エージェントがより狭い範囲であることを証明できない場合、拒否します。
ランナーがverifyを呼び出さなければ、Passは飾りにすぎません。
これはプロトコルの一部ではなく、強制ポイントです。クライアントはツール呼び出しごとに狭いPassを発行します。サーバーはツールの実行前にそれを検証します。
from pigeon import grant
from pigeon.integrations.mcp import execute_tool, pass_for_tool
parent = grant(
subject="agent:github",
capabilities=["create_issue", "merge_pr"],
resources=["mcp:github"],
)
tool_pass = pass_for_tool(parent, "create_issue", "mcp:github")
def create_issue(*, title, body):
return {"created": True, "title": title}
ok = execute_tool(tool_pass, "create_issue", "mcp:github",
{"title": "bump deps", "body": "automated"}, create_issue)
assert ok["allowed"]
no = execute_tool(tool_pass, "merge_pr", "mcp:github",
{"title": "nope", "body": "nope"}, create_issue)
assert no["reason_code"] == "CAPABILITY_NOT_GRANTED"
アイデンティティはエージェントが誰であるかを示します。権限はエージェントが何を実行できるかを示します。
pigeon keygen
pigeon inspect pass.json
Pigeonは小さなプリミティブです。プラットフォーム、ポリシーエンジン、アイデンティティプロバイダー、またはキー管理システムではありません。プロンプトインジェクションを阻止しません。Passに設定した次元に沿って爆発半径を制限するだけであり、それ以外は制限しません。
SPEC.mdSECURITY.mdexamples/(インフラストラクチャ、データ、コード、そして支払い)