An agent in Ryph works on your repositories the way a developer does. It clones them, commits, opens pull requests, keeps a branch up to date with main, files issues, comments on threads and reviews pull requests, on GitHub, on GitLab and on Radicle. On GitLab, a pull request is a merge request.
It does all of that without a credential for your forge. Every request goes through Ryph’s forge, which holds the keys and checks each one against what the agent was allowed to do.
Read code
git clone, git fetch
A read-only key for this run’s repos
Ryph’s forge
Serves the clone from its own cache. The gateway only serves clones.
Your forge
Fetched with Ryph’s credential
Ship work
submit_patch
Uploads a git bundle of the branch
Ryph’s forge
Checks the bundle carries the run’s own branch on top of the base, then stores it.
Your forge
Pushed and opened as a pull or merge request, after approval if the repo needs it
Issues and reviews
create_issue, add_comment, submit_review…
A token for Ryph’s tools
Ryph’s forge
Checks the agent’s grant, its limits and the repo’s visibility, and signs the write.
Your forge
Written as Ryph’s GitHub App, GitLab bot or Radicle node
The run holds no key
An agent that reads a hostile issue or web page can be talked into anything, so a push token in its environment is a risk. In Ryph, the run only gets these:
- A read-only key for cloning. It works for the repositories this run checks out, and only while the run is going. The git gateway behind it serves clones and nothing else, so there’s no way to push with it.
- A bundle upload for shipping. When the agent ships a branch, the runner packs the new commits into a git bundle and uploads it. Ryph checks that the bundle carries the run’s own branch and applies to the base, and stores it before it answers. From that moment the work is safe, even if the forge is down.
- A token for Ryph’s tools. Issues, comments and reviews go through Ryph’s tool gateway to the forge, which checks each call. On GitHub, it writes with a token made for that one call: one repository, one permission. The GitHub App’s private key, the GitLab bot’s token and the Radicle node’s key never leave the forge. GitLab has no token that can be narrowed per call, so there the checks below are the fence, and the bot’s Developer role keeps it off protected branches.
So an agent can’t push to main, merge a pull request, delete a branch or change a file through the API, however it’s prompted. Pushing to the forge is something only Ryph’s forge does, after the repository’s approval policy says yes.
Git inside the run is locked down as well. The repository’s own git hooks never run, and git commands never see the run’s credentials.
Working with code
Inside its checkout, the agent uses plain git: branches, commits, git log, a rebase. A few tools from Ryph cover what leaves the checkout:
| Tool | What it does |
|---|---|
submit_patch | Ships a branch as a patch, which becomes a pull request. The agent keeps working, and can submit again. |
update_from_base | Merges the latest base branch, usually main, into the branch. Conflicts are left for the agent to resolve before its next submit. |
checkout_change_request | Moves the checkout onto an existing pull request, so the agent’s commits go onto it. |
patches | Lists what the run has shipped and where each patch stands. |
run_checks | Runs the repository’s checks on a commit. |
Patches
A patch is one repository’s share of a run’s work on its way to the forge. It exists the moment Ryph has the bundle, before anyone has approved anything. Then the repository’s submission policy decides what happens. You set it on the repository’s page under Repos, in Submitting work:
- Submit automatically: Ryph opens the pull request straight away.
- Wait for approval: the patch waits under Patches until someone opens it and clicks Approve, or Reject. A patch that failed to submit gets a Retry button there.
An agent only ships patches while Let this agent open patches is on, in the Permissions step of its settings.
If GitHub, GitLab or Radicle can’t take it right then, Ryph retries with backoff, starting at 30 seconds and up to 30 minutes apart, eight times before it asks a person. A run that changed several repositories ships one patch for each, and their pull requests link to each other.
The agent hears what happens to every patch as a message: opened, approved, merged, closed or rejected, with the reason. It can follow up, wait or revise without checking back.
Working on an existing pull request
Asked with /ryph on a pull request, or after checkout_change_request, the agent pushes its commits onto that pull request’s branch. Ryph reads the pull request again right before it pushes: it has to still be open, on the same branch, and that branch can’t be the repository’s default. The push is fast-forward only, from the tip Ryph just read, so commits someone else pushed meanwhile are never overwritten.
Generations
Once a pull request merges, its branch is done. The agent can keep working, and its next submit starts a new generation:
-
The pull request merges
Ryph tells the agent, and moves the repository’s base to where the pull request merged.
-
The agent keeps committing
New commits go on the same local branch, as before.
-
The next submit moves them onto the new base
Ryph rebases the new commits onto the merged base and ships them on a fresh branch, such as
…/work-2. A conflict comes back with the files to resolve, and the agent resolves them and submits again. -
A fresh pull request opens
It holds only the new work, and reviewers never see the merged commits again.
Within a generation, every submit adds to the same pull request. If a merge left out some of the agent’s commits, the agent is told, and they ship with its next submit.
Lines
An agent that fixes two unrelated things in one repository can ship them as two pull requests. Each independent stream of work is a line: its own branch, its own generations and its own pull request.
The agent starts a line with plain git, from the base:
git switch -c fix-login origin/main and ships it with submit_patch, naming the branch. Every branch of a run lives under the run’s own namespace, such as ryph/ada/r-7f3a/fix-login, and the default line is work. A run that never starts another line ships exactly as before.
Every branch of the run sits under ryph/ada/r-7f3a/
work The default line
- …/work PR #212 merged
- next
- …/work-2 PR #219 open
fix-login git switch -c fix-login origin/main
- …/fix-login PR #213 open
work moved the new commits onto the merged base
and opened PR #219 with only the new work.- Up to 8 lines per repository in one run.
- Each line starts from the base. A branch started from another submitted line is refused, so two pull requests never carry the same commits.
- When a line’s pull request merges, only that line moves on to its next generation. The others carry on untouched.
- When the run ends, Ryph ships the default line and every line the agent submitted. If the agent left a local branch it never submitted,
finishnames it, so the agent can still ship it. - In a task working on an existing pull request, the default line goes onto that pull request, and every other line opens its own against the same base.
Working with issues and pull requests
With forge tools on, an agent reads and writes threads on your forge:
| Tool | What it does |
|---|---|
get_issue | Reads an issue with all its comments. |
list_issues | Lists open or closed issues, filtered by label. |
get_change_request | Reads a pull request with its comments, changed files, the lines a review can comment on, and its CI status. |
compare_change_request | Compares two revisions of a pull request for a second review. Radicle only for now. |
list_change_requests | Lists open or closed pull requests. |
create_issue | Opens an issue, with labels from the agent’s allowed list. |
add_comment | Comments on an issue or a pull request, or replies to one review comment and can mark its thread resolved. |
add_labels | Adds allowed labels to a thread. |
submit_review | Posts one review of a pull request: a summary and up to 30 comments on specific lines. |
close_issue | Closes an issue as completed or not planned, with an optional note. |
Agents can’t merge, approve or close pull requests, edit or delete anything, assign people or create labels. Reviews only comment.
Text the read tools return was written by anyone with a forge account, so the agent gets it quoted and marked as material to read.
What an agent may do
An admin sets this per agent. Go to Agents, open the agent, and go to the Permissions step. The settings are under Issues and change requests:
| Setting | What it controls |
|---|---|
| Repositories | Checked out repos, the ones the run works on, or Chosen repositories, connected ones the run may not check out. |
| Read issues and change requests | Whether the agent gets the five read tools. |
| Open issues | Whether it may open issues, and how many per run. |
| Comment | Where it may comment, The run’s own thread and issues it opened, or Any thread of the repositories above. And how many. |
| Review change requests | Where it may review, by the same rule, and how many reviews per run. |
| Close issues | Where it may close issues, by the same rule, and how many. |
| Labels it may apply | The labels it may use. They have to exist on the repository already. |
Click Save changes when you’re done.
Every limit is a number, up to 50 per run. The form suggests 5 issues, 20 comments and 5 reviews. A run keeps the policy it started with, so editing an agent never changes a run that’s already going.
What Ryph checks on every call
-
The run is still going
A finished run’s grant is gone, and the tool says so.
-
The request is in shape
Titles, bodies and arguments are checked against their limits.
-
The grant allows it
The repository is in scope, the capability is on, the thread fits the rule and the labels are on the list.
-
Nothing private goes public
A run that holds a private repository can’t write to a public one, except on the thread it was asked from.
-
The run is under its limit
Ryph reserves the write against the run’s limit before anything is sent.
-
The write goes out, signed
Ryph writes it as its own GitHub App, with a token for that one repository and permission, or as its GitLab bot or Radicle node, and records it on the run’s page with a link.
When a check fails, the agent reads why, so it can correct the call.
Every write is signed
Ryph signs everything an agent writes, review comments included, with a line at the top: the agent’s name, a link to the run and who asked for it. The agent’s text follows as written, and nothing in it can hide the signature. On GitLab, nothing an agent writes runs a quick action.
If the forge accepts a write but the answer gets lost on the way back, Ryph looks for its own signature before it reports a failure, so a retry doesn’t post twice. Comments written by bots, Ryph’s own included, never start a run, so an agent can’t summon another one with a /ryph in its comment.
Within its grant, an agent that was talked into it can post what the grant allows. That’s why every write capability has a limit, every write carries the signature and lands on the run’s trail, and only admins can grant forge tools. @mentions in an agent’s comment are posted as written.
Turn on forge tools
Forge tools act as Ryph’s GitHub App, its GitLab account or its Radicle node, so they come with one:
- The
ryphbinary and Docker Compose turn them on whenever GitHub, GitLab or a Radicle node is connected. - The Kubernetes Helm chart does the same with
forgeTools: auto, the default. - Networked Compose turns them on once
RYPH_FORGE_GATEWAY_TOKENis set in.env.
If they’re not available, the agent form says which settings are missing. On Radicle, Ryph can only add labels and close issues in repositories where its node is a delegate. It can always close issues it opened itself. On GitLab, confidential issues and internal notes are kept from agents.
Follow the implementation
See the git gateway, the patch lifecycle, the runner’s git flow and the forge’s architecture.