Pigeon

title
Pigeon
type
toolbox
summary
Python library for signed, attenuating capability passes an agent hands a sub-agent instead of its own API key, checked at the tool call
tags
ai-agents, security, python, authorization, watchlist
language
Python
license
MIT
created
2026-09-13
updated
2026-09-13

Pigeon addresses one specific mistake: an agent spawns a sub-agent and gives it the same API key, so the sub-agent can do everything the parent can. Instead the parent hands over a Pigeon Pass, a signed credential listing what the child may do, and the tool runner checks the Pass before performing a side effect. It is a library, not a service: there is no Pigeon server to connect to.

The model

grant creates a Pass for a subject with capabilities (actions like deploy or open_pr), resources (environment:staging, repo:acme/api) and constraints such as a rate bound. delegate derives a child Pass from a parent. verify checks a requested action against a Pass and never returns a bare boolean: a denial carries a reason code (RESOURCE_NOT_ALLOWED, CAPABILITY_NOT_GRANTED), a message, and the requested-versus-allowed comparison that failed.

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},
)

assert verify(worker, action="open_pr", resource="repo:acme/api").allowed

try:
    delegate(worker, "agent:rogue", ["open_pr", "deploy"], ["repo:acme/api"])
except DelegationError as exc:
    assert exc.reason_code == "PRIVILEGE_ESCALATION"

Delegation only narrows. A child cannot add capabilities, widen resources, raise a bound or drop a parent's constraint, and if Pigeon cannot prove the child is narrower it rejects the delegation. This is the attenuation idea behind capability tokens such as macaroons, applied to agent trees.

There are two places to change in an existing agent. Where you would copy a key into a sub-agent, call delegate. Where the side effect happens (a deploy, a query, an MCP tool), call verify and refuse on denial. The real secret stays on the runner. An MCP integration mints a narrower Pass per tool call on the client and verifies it on the server before the tool runs (pass_for_tool, execute_tool). A CLI has pigeon keygen and pigeon inspect pass.json.

git clone https://github.com/pigeonlabsHQ/pigeon.git
cd pigeon
pip install .

Limits, mostly stated by the README

The README is unusually direct about scope. Pigeon is not a platform, policy engine, identity provider or key custodian, and it does not stop prompt injection. It bounds blast radius along the dimensions written on the Pass and nothing else. And, in its own words, if the runner never calls verify, the Pass is decoration: enforcement is exactly as good as the coverage of verify calls at every side-effecting tool. A sub-agent that has any other route to the resource, through a network path or a credential already in its environment, is untouched.

The README examples do not show key handling. grant is called without a key, so where signing keys live and how a verifier learns which ones to trust has to come from the referenced SPEC.md and SECURITY.md, not read for this page. It installs from a git clone; no PyPI package is mentioned. Python 3.12 or newer.

Where it sits

onecli hides credentials from agents but leaves each agent the full authority of every key mapped to it; Pigeon is the complement, narrowing authority per delegation without concealing anything. agent-identity-attribution names per-agent scoped permissions as the strongest practical argument for giving agents identities, and a Pass is that scoping without the identity infrastructure. credential-compartmentalization bounds a breach by splitting credentials across stores; attenuation bounds it by splitting authority across the agent tree. Passes carry a rate constraint but no expiry in the README examples, so the ephemeral-credentials property is not something they visibly provide. The runner-side check is the policy layer sandboxing-ai-agents describes, moved from the network to the tool call.

On watchlist: one week old, all commits in a single day, 41★, and a security primitive with no review or adoption yet.

MIT, 41★, 2 forks, created 2026-09-06, last push 2026-09-06. Repo: https://github.com/pigeonlabsHQ/pigeon