Git & credentials

How a run gets a branch, how it pushes, and where pull requests come from.


Peractor treats git as the system of record. Each run works on its own branch, and the branch history is the run history — which is what makes runs reviewable, resumable, and reproducible.

The work branch

A run's branch is named from its task: peractor/web-14-add-login when the task has a key, or from its title otherwise. A start-over appends -r2, -r3.

The branch is the only carrier of state between phases. Each phase starts from what the branch holds, and the branch only advances when a phase completes cleanly — a crashed or aborted attempt leaves it untouched. Workspaces and agent sessions are disposable, so approving a gate a week later picks up exactly where the branch is.

Git steps

Workflows drive your git host with git steps — open-pr (optionally as a draft), ready-pr, merge, and tag.

They run at their declared position, not batched at the end: the machine commits and pushes the work so far, Peractor performs the action with its own credentials, and the result lands back in the transcript. So "open a draft pull request, then keep working" behaves exactly as written. In a run that leaves restricted files out, Peractor checks and pushes that work itself before the step acts (below).

A merge step goes through the normal APIs, so branch protection still applies — Peractor's gate controls the workflow, your repository rules still control the merge.

These steps need the repository connected here. Pushing a branch and opening a pull request are different permissions: the push can come from a token, a key, or a machine of your own, but the pull request is opened by Peractor through the provider's API. If nothing is connected, the workflow editor says so on the step itself, and a run that reaches it fails with the same message rather than reporting a pull request that was never opened.

Who a commit is by

Every commit Peractor makes names two people, as git allows. The author is the person behind it: whoever saved the page, file or asset, or started the run, under the name Peractor shows them by and their email. The committer is your organization, under the name and email in Settings → Git access → Committer — or Peractor <peractor@local> when you set none. A run nobody started, from a trigger or an API key, is authored by the organization too. Each commit message ends with Co-authored-by: Peractor <peractor@local>; a commit an agent makes itself mid-step has the right author and committer, but git can't add that line for it.

An email that belongs to a GitHub or GitLab account shows that account's picture on the commits it is on.

How runs get push access

A one-shot container has no ambient git identity. When a run needs to push, Peractor resolves a credential in this order:

  1. Settings → Git access — a per-organization token or SSH key, with an optional host filter. Works with GitHub, GitLab, Bitbucket, and self-hosted remotes. This always wins when configured.
  2. The Peractor GitHub App — install it once and every run gets a short-lived token scoped to exactly the repository it works on. The app also opens and merges the pull requests. Recommended for GitHub.

With neither, a run that must push fails with a clear message rather than degrading silently. Changing anything in Settings → Git access takes the Manage git access permission.

In a run that clones, the resolved token is also available as GITHUB_TOKEN and GH_TOKEN, so command steps can call gh or install private packages. A run that leaves restricted files out has neither — not even one the workflow's secrets name.

Runs that leave files out

A phase clones the repository as above only when it leaves nothing out: whoever the run acts for — the person who started it, an API key or a trigger — can open every restricted folder and file, and the run includes them all. Otherwise the phase works on a copy Peractor hands it:

  • only the files the run may have, as one commit with no history, so git log and git blame see nothing earlier;
  • no git credential at all;
  • no warm workspace.

The agent commits and pushes as usual, to that copy. When the phase ends, and before a git step acts, Peractor checks every file each new commit changed, makes the same commits on the real branch — author, date and message kept — and pushes them with your organization's credential. A change to a file whoever the run acts for can't edit stops it — those commits aren't pushed, and the phase fails: "This run changed assets/plan.md, which you can't edit." So does writing a file the copy left out, or merging in changes to one. A file the run's starter can't even open is never named: "This run changed files in a folder you can't open."

A run on a file in Assets can't work on such a copy yet. In a project with restricted folders it needs every one of them included, so it runs only for someone who can open them all.

A machine of your own can answer for itself

If a phase runs on a machine you paired, that machine can have its own token or key — or say it uses its own git, in which case Peractor sends it no credential at all and the clone and push are signed by what the machine already has. A machine on its own git never takes a run that leaves files out: when it is the only one a phase's labels match, the phase fails, naming it.

That is the setup for a team that wants the scheduling, the review, and the history without giving the control plane access to the code. Pull request steps still run on Peractor, so those still need the app or an organization token.

Setting up? See Getting started step 3, or Self-hosting for the GitHub App environment variables.