# Pigeon

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.

```python
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`.

```bash
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 [[toolbox/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>
