GitHub App permissions and webhook subscriptions
This document is the authoritative least-privilege profile for the shipped BoardReadyOps GitHub App control plane. It replaces the broader exploratory permission set described in early architecture notes.
Shipped control-plane behavior
The current service:
- verifies signed GitHub App webhooks;
- records installation and repository lifecycle changes;
- reacts to pull request
opened,reopened,synchronize, andready_for_reviewactions; - creates and updates a native GitHub Check Run;
- optionally dispatches a GitHub Actions workflow;
- accepts the runner result and completes the Check Run; and
- attempts to upsert one top-level pull request summary comment as a non-blocking enhancement.
The control plane does not read repository contents through the GitHub REST API, create commit statuses, administer repositories, manage secrets or variables, access organization membership, or use account-level APIs.
Repository setup follows the same boundary. The App reads workflow metadata with Actions read access and does not call the Administration-only repository Actions settings endpoint, then dispatches a repository-owned setup probe. The workflow uses its own short-lived GITHUB_TOKEN to check out the default branch and GitHub Actions OIDC to return a bounded readiness result. The production App does not write boardreadyops.yml or workflow files. Any future assisted installation must be a separately reviewed, explicit opt-in flow because it would require Contents write.
Minimum repository permissions
Target-repository GitHub Actions profile
Use this profile for the default hosted deployment. The dispatch workflow lives in the same repository that produced the pull request webhook, so the App never uses an installation token across repository or installation boundaries.
| Permission | Level | Required for |
|---|---|---|
| Metadata | Read | GitHub-required repository metadata and installation context |
| Pull requests | Read | Receiving the pull_request webhook payload |
| Checks | Read and write | Creating, starting, and completing the BoardReadyOps Check Run |
| Actions | Read and write | Calling the workflow dispatch endpoint in github-actions runner mode |
Do not grant any organization or account permissions for this profile.
Customer self-hosted execution profile
When a customer runner uses customer-controlled checkout credentials or a local
repository mirror, omit both Actions and Contents access from the App:
| Permission | Level |
|---|---|
| Metadata | Read |
| Pull requests | Read |
| Checks | Read and write |
Planned managed Marketplace execution profile
ADR-0009 selects a managed, lease-based worker plane and a source broker for the future public Marketplace App. After that source broker is implemented and validated, the managed profile is:
| Permission | Level | Required for |
|---|---|---|
| Metadata | Read | Installation and repository context |
| Pull requests | Read | Supported pull request webhook events |
| Checks | Read and write | Check Run lifecycle |
| Contents | Read | Source broker access to the exact repository and commit |
The managed profile omits Actions. The source broker must mint an installation
token restricted to the exact repository and contents: read, fetch the exact
commit archive, and keep the GitHub token out of worker processes. Do not grant
Contents access to the production/public App before the ADR-0009 source broker
and its two-installation end-to-end tests are complete.
Optional pull request summary comments
BoardReadyOps uses the issue-comments REST endpoints because top-level pull request comments are issue comments in GitHub's data model. GitHub accepts either of these write permission sets for those endpoints:
- Pull requests: read and write; or
- Issues: read and write.
Grant only one of them. Prefer Pull requests: read and write because the feature writes only to pull request conversations and the App already needs Pull requests read access for its webhook.
Comment publication is not a release gate. A missing permission or a GitHub comment API error is retained in the run publication audit state, while a successfully published Check Run remains authoritative and the runner callback returns success.
For the public least-privilege profile, leave comment write access disabled unless the product explicitly ships and supports pull request comments.
Webhook subscriptions
Subscribe only to:
pull_request
Handle only these actions:
openedreopenedsynchronizeready_for_review
GitHub Apps receive installation and installation_repositories events by
default; they are not manually selected subscriptions. ping is accepted for
webhook verification.
Do not subscribe to check_suite, check_run, status, push,
issue_comment, security-alert, deployment, organization, or account events
until corresponding shipped behavior is implemented and tested.
Permissions that must remain disabled
Unless a future feature has a reviewed permission rationale, keep these set to No access:
- Administration
- Contents
- Commit statuses
- Deployments
- Environments
- Issues, when optional PR comments are disabled
- Members
- Pages
- Packages
- Repository hooks
- Repository secrets and variables
- Security events and alert APIs
- Workflows; the repository
Actionspermission is the only workflow-dispatch permission required - all organization permissions
- all account permissions
The GitHub Actions runner workflow has its own job-scoped contents: read
permission. That workflow token is separate from the GitHub App installation
token and does not justify granting repository Contents access to the App in the
current compatibility mode.
Execution-plane boundary
The github-actions mode obtains a token for the installation that received the
pull request webhook and dispatches .github/workflows/readiness-runner.yml in
that exact repository. The workflow uses its job-scoped GITHUB_TOKEN for an
exact-SHA checkout and GitHub OIDC for the result callback. Source, workflow
logs, and Actions artifacts remain in the target repository.
ADR-0010 makes this target-repository workflow the hosted default. ADR-0009's lease protocol remains available for explicitly selected customer self-hosted execution. Do not configure a central public runner repository, broaden the App to Contents write, or use a shared VPS worker to bypass the repository boundary.
Production change procedure
- Keep a separate development/test App when broad exploratory permissions are still required.
- Configure the public/production App with the profile for the execution mode that is actually deployed.
- Select only the
pull_requestevent. - Install the App on selected repositories first, not all repositories.
- Rotate the webhook secret and private key if development credentials were exposed to a broader environment.
- Re-authorize installations after changing requested permissions.
- Execute the validation matrix below.
- Record the App settings review date and reviewer in issue #88.
Required validation matrix
The permission reduction is complete only after all of these pass against the reduced production/public App:
- webhook
pingsignature verification; - installation create and delete lifecycle persistence;
- repository add and remove lifecycle persistence;
- pull request
opened,reopened,synchronize, andready_for_review; - Check Run creation, transition to in-progress, and completion;
- exact runner-result replay behavior;
- target-repository GitHub Actions dispatch only when that mode is enabled;
- exact-SHA checkout and run/attempt-bound OIDC callback from the target workflow;
- self-hosted claim isolation only when customer runners are enabled;
- pull request comment creation and update only when comment write permission is intentionally enabled; and
- confirmation that unsupported or unsubscribed events are not delivered.
Issue #88 must remain open until the external GitHub App settings are changed and the applicable matrix is re-run end to end.