unYOLO is a framework for building credential brokers and proxies for services like GitHub, Hugging Face, or Google Workspace. Your agent talks to the broker and never holds the real credential.
unYOLO lets you keep fine-grained policies in a local file. No permission screens to click through and no separate account to create for the agent. When the agent needs more permissions, you can give it timed grants that expire on their own.
$ gh-broker operation submit pull_request.createop_9c2f succeeded pull request #482 opened$ gh-broker operation submit pull_request.mergeop_1d55 failed required review not satisfied$ gh-broker operation submit pull_request.merge_adminop_4a71 pending approval requiredop_4a71 approved by operator, single useop_4a71 succeeded pull request #482 merged unYOLO
Approval required
agent-a wants to merge #482 in acme/api.
Bypasses the required review on main.
09:41Agent 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.
You hand the token to the agent. It does the work you wanted, and the same token reaches everything else in your account.
The broker holds the token. The agent asks for the same work, and the calls you never authorized are refused.
Every broker uses the same request path. Only classification and execution depend on the provider.
The caller presents a named broker-client secret before the broker accepts a request.
The provider adapter identifies the client and operation together with its target attrs.
The shared engine matches that tuple against the rules file.
An approved grant acts as an allow rule with an expiry and a use budget.
A requestable operation waits in the operator inbox and can also appear in Telegram.
The broker performs the operation with the provider credential and returns only the result.
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.
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.
{
"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/**"]
}
}
]
} {
"rules": [
{
"id": "request-force-push-to-main",
"effect": "request",
"clients": ["agent-a"],
"operations": ["git.push.force"],
"targets": [
{ "kind": "repo", "owner": "acme", "name": "api" }
],
"attrs": {
"refs": ["refs/heads/main"]
},
"grant_policy": {
"mode": "window",
"default_minutes": 5,
"max_minutes": 10,
"default_max_uses": 1,
"max_uses": 1
}
}
]
} {
"rules": [
{
"id": "never-delete-refs",
"effect": "deny",
"clients": ["*"],
"operations": ["git.ref.delete"],
"targets": [{ "kind": "repo" }],
"description": "Deletion is never delegated, even under an approved grant."
}
]
}
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$ 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
The repository includes GitHub and Hugging Face brokers. sudo-broker handles
approved Unix commands. Each runs as a separate process and cannot reach another
provider's credentials.
Holds GitHub App credentials. For a repo-scoped request it mints a short-lived installation token narrowed to that one repository and the minimum permissions the operation needs.
Holds a Hugging Face token for Hub repositories and Router inference. It parses Git and LFS pushes before forwarding them, so history rewrites stop at the broker.
Runs one exact command from a root-owned catalog as another Unix user. It rejects shell strings and arbitrary executables along with TTYs or caller-supplied environments.
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.
Run gh-broker against one repository, push an allowed branch, and verify that
a force-push to main is refused.