Credential boundaries for coding agents

unYOLO keeps provider credentials in separate broker processes. Your agent can use the operations allowed by policy without receiving the token or privilege behind them.

agent-a@workstation
$ git push origin agent-a/parser-fix
   4d1e07c..9f2c1ab  agent-a/parser-fix -> (new branch)

$ gh-broker operation submit pull_request.create \
    --target-json '{"kind":"repo","owner":"acme","name":"api"}' \
    --arguments-json '{"head":"agent-a/parser-fix"}'
op_01JQ8W3M  succeeded  pull request #482 opened

# The same agent cannot rewrite main.
$ git push --force origin main
 ! [remote rejected] main -> main
   (gh-broker: no rule allows git.push.force)

# And it never held a GitHub token.
$ git config --get credential.helper
gh-broker git credential

Credential boundary

Agent tools often receive the same account-wide token a person would use. A mistaken command can then reach every repository and operation covered by that token.

A broker keeps the provider token in another process. The agent receives a client credential whose authority comes from policy, so an unauthorized force-push fails before GitHub sees it.

Without a broker

You hand the token to the agent. Now the agent reaches everything the token reaches.

You give your GitHub token to the agent, which can then reach your branch, the default branch, the repository, and a private repository. token YOU AGENT agent-a/parser-fix branch main default branch acme/api repository acme/secrets private repo
With unYOLO

The broker holds the token. The agent can only ask, and only the wire your policy allows carries anything.

You give your GitHub token to the broker. The agent sends requests to the broker, which allows the branch push and cuts the other three paths. token request YOU AGENT BROKER agent-a/parser-fix branch main default branch acme/api repository acme/secrets private repo

Request path

Every broker uses the same request path. Only classification and execution depend on the provider.

  1. 1 Client authentication

    The caller presents a named broker-client secret before the broker accepts a request.

  2. 2 Request classification

    The provider adapter identifies the client and operation together with its target attrs.

  3. 3 Policy evaluation

    The shared engine matches that tuple against the rules file.

  4. 4 Active grants

    An approved grant acts as an allow rule with an expiry and a use budget.

  5. 5 Approval request

    A requestable operation waits in the operator inbox and can also appear in Telegram.

  6. 6 Provider execution

    The broker performs the operation with the provider credential and returns only the result.

  7. 7 Audit entry

    The broker records the decision and matching rule IDs without including secrets.

Decision order, fixed regardless of rule order in the file

deny active grant allow request no_match

Deny wins over everything, including an approved grant, and a request that matches no rule at all is refused.

Read the policy engine documentation

Policy file

A broker loads one JSON rules file at startup as its authorization source. You can read it directly and review changes in a pull request. unYOLO does not infer permissions from traffic.

Attrs provide the useful narrowing. The rule beside this text allows pushes to refs/heads/agent-a/**. It leaves refs/heads/main uncovered, so a push to the default branch is denied. Unknown fields, duplicate rule IDs, unsupported operations, and invalid globs prevent the service from starting.

Full policy schema
{
  "rules": [
    {
      "id": "agent-a-read-and-branch",
      "effect": "allow",
      "clients": ["agent-a"],
      "operations": [
        "contents.read",
        "git.fetch",
        "git.push.fast_forward"
      ],
      "targets": [
        { "kind": "repo", "owner": "acme", "name": "api" }
      ],
      "attrs": {
        "refs": ["refs/heads/agent-a/**"]
      }
    }
  ]
}

Operator approval

Marking an operation request creates a durable approval record and keeps the original call open. Once approved, a git push resumes as the same push. A denial, expiry, or change in upstream state returns an ordinary Git failure and forwards nothing.

Operators decide through a protected inbox on a separate listener with its own credential. Telegram can display the same approval record. Either interface closes the request exactly once, and approval may only narrow its duration or use count.

How approvals work
operator@host
$ curl -sS --unix-socket "$OPERATOR_SOCK" \
    -H "Authorization: Bearer $OPERATOR_TOKEN" \
    localhost/api/operator/v1/requests?status=pending

{
  "items": [{
    "id": "g_01JQ8W3M",
    "revision": 3,
    "requester": "agent-a",
    "operation": "git.push.force",
    "presentation": {
      "title": "Rewrite history on acme/api",
      "risk": "high",
      "warnings": ["Removes 2 commits from main"]
    }
  }]
}

# Approve, narrowing the window to two minutes.
$ curl -sS -X POST .../g_01JQ8W3M/approve -d '{
    "expected_revision": 3,
    "constraints": {"duration_seconds": 120}
  }'
200  state=active  uses_remaining=1

Custom brokers

The included brokers use the same framework available to custom providers. To protect an internal API key or cloud role, implement the provider-specific classifier and executor. Shared packages supply the policy and approval machinery. The architecture check rejects shared code that imports a provider.

You write

  • A classifier that identifies the client and operation with its target attrs
  • A registry declaring operations and their target kinds with accepted attrs
  • An executor that holds the credential and performs the action
  • Bounded approval wording with a title, risk facts, and warnings

You inherit

  • Client and operator authentication on separate credentials
  • The policy engine and its fixed decision order
  • Grant lifecycle with use budgets, reservations, and idempotent retries
  • The operator inbox, SSE cursors, and the Telegram channel
  • Agent Operations V1, its MCP bridge, and restart recovery
  • Secret-safe audit, installers, service rendering, and doctor checks

Read the framework overview

GitHub quickstart

Run gh-broker against one repository, push an allowed branch, and verify that a force-push to main is refused.

Esc

Type to search across every documentation page.