Documentation
Use Cases

Delegating credentials to sub-agents

When your agent spawns a worker — a VM, a sub-process, a CI runner — it can share a subset of its permissions without exposing its own identity.

When to delegate

Multi-agent workflows
Parent agent orchestrates, sub-agents execute specific API calls.
Ephemeral VMs
Spin up a worker VM that needs GitHub access for 2 hours, then auto-expires.
CI / CD pipelines
Give your pipeline scoped API access without storing long-lived secrets.
Sandboxed execution
Run untrusted code with minimal permissions — it can't escalate beyond its delegate scope.

Create a scoped delegate

A scoped delegate gets a fixed subset of the parent's approved scopes. No dynamic expansion — what you give is what it gets.

terminal
$ keychains delegates add pr_a1b2c3 --scopes "github::repo" --ttl 3600
> Delegate created: del_x7y8z9
> Mode: scoped
> Scopes: github::repo
> Expires in: 1 hour
> Ship KEYCHAINS_DELEGATE_ID and KEYCHAINS_DELEGATE_KEY to the remote system

Create a wildcard delegate

A wildcard delegate starts with zero scopes and accumulates them as the user approves. The parent's scopes act as a hard ceiling.

terminal
$ keychains delegates add pr_a1b2c3 --wildcard --ttl 7200
> Delegate created: del_w1c2a3
> Mode: wildcard
> Scopes: (none — approved on demand)
> Parent ceiling: github::repo, google::gmail.readonly
Note: The wildcard delegate can only request scopes the parent has already approved. Requesting a scope outside the parent's set returns a hard scope_refused error — no approval URL.

The two-tier scope check

When a wildcard delegate requests a scope, two checks happen:

Tier 1 — Parent Ceiling
Is the scope in the parent's approvedScopes? If no: hard refuse. No approval URL.
Tier 2 — Delegate Approval
Scope is in parent but not the delegate? Ask user — returns 403 with approval URL.

Using delegates from code

remote-worker.ts
import { mintDelegateToken, proxyFetch } from '@keychains.dev/machine-sdk';
// On the remote system — env vars shipped from parent
const { token } = await mintDelegateToken(
process.env.KEYCHAINS_DELEGATE_ID,
process.env.KEYCHAINS_DELEGATE_KEY
);
const res = await proxyFetch('https://api.github.com/user/repos', { token });
Warning: Delegate private keys are secrets. Always use short TTLs and revoke delegates as soon as the task completes.

Revoking a delegate

Revoke from the CLI, SDK, or dashboard. Takes effect immediately.

terminal
$ keychains delegates rm del_x7y8z9
> Delegate del_x7y8z9 revoked
Tip: Revoking the parent permission automatically revokes all its delegates via cascade revocation.

Delegation mode matrix

ParentDelegateBehavior
scopedscopedFixed subset. No expansion.
scopedwildcardAccumulates up to parent's fixed set.
wildcardscopedFixed subset of parent's dynamic pool.
wildcardwildcardDynamic accumulation. Parent is hard ceiling.