
Applica la delega con privilegi minimi per gli agenti AI tramite credenziali firmate e con ambito limitato. Concedi ai sotto-agenti capacità e risorse ristrette, verifica le azioni prima dell'esecuzione e previeni l'escalation dei privilegi.
Il tuo agente ha generato un sotto-agente e gli ha consegnato la stessa chiave API. Quel sotto-agente ora può fare deploy in produzione, leggere il database dei pagamenti e fare merge su main.
Pigeon blocca tutto questo. Dai al figlio un Pigeon Pass: una credenziale ristretta e firmata per ciò che può fare, non una copia di tutto ciò che puoi fare tu.
Python 3.12 o successivo.
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 non restituisce mai un semplice booleano. Una negazione include un codice motivo, un messaggio e il confronto che è fallito (requested vs allowed).
Provalo senza scriverlo tu:
python examples/01_infrastructure.py
python demo/agent.py
Non esiste un server Pigeon a cui connettersi. Modifichi due punti che già possiedi:
delegate(...) e consegna un Pass al figlio.verify(...) e non eseguire il tool se viene negato.Tieni il segreto reale sul runner. Il figlio porta il 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}, # cannot drop a parent constraint
)
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"
Un figlio non può aggiungere capacità, ampliare le risorse, alzare un limite o eliminare un vincolo del genitore. Se Pigeon non può dimostrare che il figlio è più ristretto, lo rifiuta.
Se il runner non chiama mai verify, il Pass è solo decorazione.
Questo è un punto di enforcement, non parte del protocollo. Il client genera un Pass più ristretto per ogni chiamata al tool. Il server lo verifica prima che il tool venga eseguito.
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"
L'identità ti dice chi è l'agente. L'autorità ti dice cosa può fare.
pigeon keygen
pigeon inspect pass.json
Pigeon è un piccolo primitivo. Non è una piattaforma, un motore di policy, un provider di identità o un custode di chiavi. Non blocca la prompt injection. Limita il raggio d'esplosione lungo le dimensioni che inserisci nel Pass, e solo quelle.
SPEC.mdSECURITY.mdexamples/ (infrastruttura, dati, codice, poi pagamenti)