Setting up the GitHub App¶
Factoryal reaches GitHub through a GitHub App. The App's private key stays in the control plane (the API). Coding agents get 1-hour tokens that can push to one repository, and pull requests are opened by the platform with a separate token, so they show up as authored by the App's bot.
1. Register the App¶
GitHub → Settings (your org's settings, for org repos) → Developer settings → GitHub Apps → New GitHub App.
| Field | Value |
|---|---|
| GitHub App name | e.g. acme-factoryal (the bot shows as acme-factoryal[bot]) |
| Homepage URL | anything, e.g. your Factoryal URL |
| Webhook → Active | ✅ (untick it if you don't have the URL yet, e.g. before the first AWS deploy) |
| Webhook URL | https://<your-factoryal-api>/v1/integrations/github/webhook |
| Webhook secret | a long random string you choose (openssl rand -hex 32); you'll set it as FACTORYAL_GITHUB_WEBHOOK_SECRET |
Deploying to AWS for the first time? The webhook URL is a stack output, so it doesn't exist yet. Create the App with the webhook inactive, deploy, then set the URL and secret and tick Active (see deploy-aws.md, step 4).
Repository permissions (nothing else):
| Permission | Access | Why |
|---|---|---|
| Contents | Read and write | Agents push branches; webhooks read repo files to refresh standards facts; the dispatcher reads factory repos |
| Pull requests | Read and write | The platform opens PRs, reads reviews, and comments on its PRs and on factory repo PRs (the plan) |
| Commit statuses | Read and write | factoryal/apply and factoryal/plan statuses on factory repo commits |
| Checks | Read-only | Receive CI results (check_suite events) |
| Metadata | Read-only | Required by GitHub |
Subscribe to events: Push, Pull request, Check suite, Pull request review,
Pull request review comment, Pull request review thread. A push to a factory repo's branch
applies it, and a pull request against it gets a plan (see factory.md). The review
event lets a review that asks for changes on a Factoryal PR start a revision (see
agents.md); the comment one lets the reviewer answer people who reply to
its inline comments (below); the thread one tells findings when
a comment's thread is resolved.
Already created the App? Add Commit statuses: Read and write on its page (Permissions & events). GitHub then asks each installation to accept the new permission.
Where can this GitHub App be installed? "Only on this account".
Click Create GitHub App. Note the App ID.
2. Create a private key¶
On the App's page → Private keys → Generate a private key. A .pem file
downloads. Store it as a secret: locally in ./secrets/github-app.pem (git-ignored),
on AWS in the factoryal/github secret, readable only by the API (see
deploy-aws.md, step 2).
3. Install it¶
App page → Install App → choose your org → Only select repositories → pick the repos Factoryal may work on. Repos you don't select stay out of reach even if an agent's scopes name them: the token mint fails with "the GitHub App is not installed on …".
4. Configure Factoryal (API and dispatcher)¶
FACTORYAL_GITHUB_APP_ID=123456
FACTORYAL_GITHUB_PRIVATE_KEY_FILE=/secrets/github-app.pem # or FACTORYAL_GITHUB_PRIVATE_KEY=<pem contents>
FACTORYAL_GITHUB_WEBHOOK_SECRET=<the webhook secret>
Workers need only ANTHROPIC_API_KEY (with Managed Agents access) and the Factoryal
API URL and token.
5. Put the repos in the catalog¶
A repo is reachable by its url property:
kind: Entity
blueprint: repo
identifier: billing-api
team: payments
properties:
url: https://github.com/acme/billing-api
default_branch: main
Give a coding agent repo:write:billing-api (one repo) or repo:write (any repo
in the catalog), then use it in a code step. See
examples/demo/projects/delivery/workflows/linear-agent-fix.yaml.
6. Check it¶
- On the App page → Advanced → Recent deliveries, the initial
pingshould show a202response from Factoryal. - Push to the default branch of an installed repo: its
has_readme,has_ci, ... properties update.
7. Review pull requests¶
examples/demo/projects/code-review/workflows/pr-review.yaml reviews PRs the
way CodeRabbit does. On every push to an open, non-draft PR, it posts one review with a
summary, a walkthrough table of the changed files, and inline comments. Comments can carry
a suggestion block that GitHub can apply in one click.
It needs:
- The
pull_requestblueprint fromexamples/demo/catalog/catalog.yaml. With it,pull_requestwebhooks keep a PR entity per PR (number,state,draft,head_sha, ...). Only the properties the blueprint defines are written. - The repo in the catalog (step 5). PRs on repos that aren't in the catalog are ignored.
A push changes the entity's head_sha, and the workflow's entity_event trigger starts a
review on that change. Edits to the title or description don't start one. The workflow reads
the diff with a github_pull step, which puts the new file's line number in front of each
diff line so the model can point comments at them, and leaves out the generated files its
exclude patterns match (listed as skipped). Its text output is the PR ready for the
prompt: title, description, the App's earlier comments and the numbered diff. Then it posts
with a notify step with target: github_review and pull: pr. Change the system prompt
to set what the reviewer looks for.
Re-reviews don't repeat themselves. since: last_review reads only what changed since the
commit of the App's latest review on the PR, as GitHub records it, so a failed review, which
posts nothing, has its changes come back in the next one. The whole diff is read on a first
review, or when that commit is gone (force-push). text lists the App's earlier inline
comments as [kind] text with their fix as Proposed: ..., without the headers and
suggestion blocks the review added, and the prompt tells the model not to repeat them.
The file also has pr-review-reply, which answers when someone replies to the reviewer's
inline comments. It needs the review_reply blueprint from
examples/demo/catalog/catalog.yaml: with it, pull_request_review_comment
webhooks record replies to the App's own comments as entities (replies to anyone else, or
by the App itself, are ignored). There is one entity per GitHub review, so replies submitted
together ("Start a review", or several threads at once) start a single run; the other
webhooks of that review find it recorded and start nothing. The run reads the PR with
github_pull and review: <id>, whose threads output holds the App's threads that review
replied in and that the App hasn't answered since. The model answers each one from
threads_text, or leaves a reply empty when it needs no answer (an acknowledgment like "done"
or "thanks"), and one notify step with target: github_reply, pull: pr and replies
posts the rest, each in its own thread.
To review a PR by hand: Workflows → pr-review → repo: billing-api, number: 12.
To answer one thread by hand: Workflows → pr-review-reply → repo: billing-api,
number: 12, and review: <review id> (or thread: <comment id> for one thread).