When an AI spawns a sub agent (a helper AI), a copied API key hands the child every power the parent has.
When an AI coding agent spawns a helper to push a hotfix, the helper usually inherits the parent's API key. That single key now lets the helper deploy to production, read the payments database, and merge to main. Those are powers the parent never meant to hand over. A small open-source Python library called Pigeon is one of the first runnable exhibits of the discipline that has to replace this: hand the child a signed, narrowed credential instead of a copy of your identity.
The library comes from pigeonlabsHQ and targets Python 3.12+. At v0.1 it ships its own credential format; the project is explicit that it is not JWT, not Biscuit, not UCAN, and signs every Pass with Ed25519. A Pass carries three bounded lists: capabilities (which actions the child may perform), resources (databases, repos, deploy endpoints, or other targets those actions may touch), and constraints (rate limits, counts, time bounds). The format and API are still in 0.x, so integrations are early-stage by the project's own admission.
The architecture is what is not there: no Pigeon server. The library is serverless by design. The parent's secret stays on the runner; the child carries only the Pass. A leaked child is then scoped to whatever the Pass actually granted, not to the parent's full credential set.
A child cannot add a capability the parent did not grant, widen a resource the parent did not already include, raise a numeric bound, or drop a parent constraint. Any of those attempts fails closed with PRIVILEGE_ESCALATION. Constraints propagate down the chain: if a parent's Pass caps deployments at three per hour, the child's Pass inherits that cap, and the child cannot raise it. The README walks through the verify() surface. It never returns a bare boolean. Denials come back as a reason code, a human message, and the specific comparison that failed (requested resource vs. allowed resource, requested capability vs. granted capability). The success path returns .allowed. That is the difference between a credential check and a debuggable one.
The Hacker News launch thread for the project restates the thesis in two lines: identity says who the agent is; authority says what it may do. Today's default, copying the API key, collapses those two. Pigeon separates them by construction.
Then the honest limit, because the project's security policy is explicit about it. Pigeon is an enforcement point, not a magic shield. The runner, which is the parent agent or its host process, must call verify() at the side-effect boundary before any privileged action: a deploy, a database query, an MCP tool call. If the runner skips that call, the Pass is decoration. A signed, narrowed credential no one checks at the moment of action is the same as no credential at all. That is a discipline problem, not a library problem, and the project says so.
The limit is the part of the story a reader who actually plans to use Pigeon needs to hear. A pattern becomes teachable only when its failure mode is named, and the failure mode here is concrete: a team ships a Pass, forgets to wire verify() at the action boundary, the helper deploys anyway, and the same blast radius the parent tried to avoid is back. Pigeon gives the mechanism. The human still has to wire the check.
Two near-term signals will tell whether this is settling into a default or staying a single-project exhibit. The first is whether the v0.x format stabilizes into a version the rest of the agent stack can target. The second is whether the MCP helper layer the project ships starts bundling verify() calls at the tool boundary by default, so a runner that forgets the discipline still gets it for free.