# Connecting the GitHub App

Source: https://codeherder.com/docs/github-app/

Install the CodeHerder Cloud GitHub App on your organization, or create your own App when you self-host.

The hosted App is named **CodeHerder Cloud** (slug `codeherder-cloud`). The name “CodeHerder” is taken on GitHub. A self-hosted App can have any free name that you choose. Use its slug in URLs.

The **GitHub App** links a GitHub organization or account to a workspace. GitHub then sends pull request, review, comment, check and push events to CodeHerder. CodeHerder also mints short-lived installation tokens on the server. This is separate from the [token-based GitHub integration](https://codeherder.com/docs/integrations/).

## Connect GitHub on CodeHerder

1. Open **Admin → Integrations** and find the **GitHub App** panel.
2. Select **Install**. CodeHerder sends you to GitHub.
3. Pick the organization or account. Choose all repositories or only some.
4. Approve the install. GitHub asks you to authorize the App too. Approve that as well.
5. GitHub returns you to CodeHerder. CodeHerder finishes the link and lists the repositories.

Only a workspace **admin** or **owner** can start the install. That person must also be an owner (admin) of the GitHub organization, or the owner of the personal account, that holds the installation. Being able to see the installation, or to read one of its repositories, is not enough. A repository admin who is not an organization owner is refused. CodeHerder checks this with your GitHub authorization. A refused user sees `not_account_admin`. Pasting an installation id does not work. Another tenant’s installation is refused.

Connecting with **Install** or **Connect** also marks your GitHub login as verified for you in this workspace. CodeHerder uses it to attribute comments and events to you. It never grants access. If another member already holds that login, the connect still works and the other link stays.

One installation belongs to one workspace. A second claim of the same installation fails with `already_exists`. A group’s installation is usable in every workspace beneath it. The panel marks it **inherited**.

**Release** removes the link in CodeHerder. It does not uninstall the App on GitHub.

### Installed from GitHub first

Start from CodeHerder when you can. You can also install the App from GitHub: from the App’s public page, from the GitHub Marketplace, or from a link an organization owner shared. GitHub then returns you to CodeHerder with no workspace attached. CodeHerder checks your GitHub authorization, as it does for a normal install. It then shows a page that asks you to choose a workspace.

1. Sign in to CodeHerder if you are signed out. You return to the same page.
2. Pick a workspace. The list holds only the workspaces where you are an **admin** or **owner**.
3. CodeHerder connects the installation to that workspace and lists the repositories.

This path does not verify your GitHub login. The page link proves only that someone installed the App. It does not prove that you are that person. To verify your login, use **Connect** or **Install** later.

The page link works once and expires after 15 minutes. If it fails, install again, or use **Connect** as described below.

If a workspace already holds the installation, the page says so and links to that workspace. CodeHerder does not move the installation. An admin of that workspace must **Release** it first.

If you see no workspace to pick, you are not an admin of any workspace. Ask a workspace admin to connect it, or to make you an admin.

### Connect an installation that no workspace holds

GitHub does not send the authorization step again when you only change the App’s settings. In that case you return to **Integrations** with nothing connected. The **GitHub App** panel then lists each installation that no workspace holds and that your GitHub login owns or administers. Select **Connect** next to it. CodeHerder sends you to GitHub to authorize, then connects the installation to this workspace.

The list needs a GitHub login that GitHub proved to CodeHerder. Connecting once with **Install** or **Connect** proves it. A login that a workspace admin verified by hand does not count. An install that started on GitHub does not. For an organization, the installation also needs the **Members: Read** permission. Without it, the organization is not listed. Use **Install** instead.

### Keeping the repository list current

Add or remove a repository on the installation in GitHub. CodeHerder updates its list within seconds. The panel marks each repository as matched or unmatched. A matched repository is one that you also registered in CodeHerder. Registering a repository stays a separate step: see [Connecting repositories](https://codeherder.com/docs/repositories/).

## Self-host: create your own App

A self-hosted server uses your App, not the hosted one. You create the App once on GitHub. Then you set six server knobs. Replace `<base>` with your server’s public HTTPS URL.

### Create the App

Open **Settings → Developer settings → GitHub Apps → New GitHub App**. You can instead register it from this manifest. Choose a free App name. “CodeHerder” is taken on GitHub.

```
{
  "name": "<your App name>",
  "url": "<base>",
  "hook_attributes": { "url": "<base>/v1/githost-deliveries/github", "active": true },
  "redirect_url": "<base>/v1/github-app/callback",
  "callback_urls": ["<base>/v1/github-app/callback"],
  "setup_url": "<base>/v1/github-app/callback",
  "request_oauth_on_install": true,
  "public": false,
  "default_permissions": {
    "metadata": "read",
    "members": "read",
    "contents": "write",
    "pull_requests": "write",
    "issues": "write",
    "checks": "write",
    "actions": "read",
    "statuses": "write"
  },
  "default_events": [
    "pull_request", "pull_request_review", "pull_request_review_comment",
    "issue_comment", "check_suite", "check_run", "workflow_run",
    "push", "create", "delete"
  ]
}
```

### Register the manifest and convert the code

GitHub reads a manifest from a form post. Build an HTML page with a form that posts the manifest to `https://github.com/settings/apps/new`, or to `https://github.com/organizations/<org>/settings/apps/new` for an organization. Put the manifest JSON in a field named `manifest`. Open the page, then select **Create GitHub App** on GitHub.

GitHub then redirects your browser to `<base>/v1/github-app/callback?code=<code>`. CodeHerder shows a page with the next command. GitHub has not created the App yet. The code works once and expires after 1 hour. Convert it:

```
curl -X POST -H "Accept: application/vnd.github+json" https://api.github.com/app-manifests/<code>/conversions
```

The reply holds the App secrets. Keep it private. It maps to the knobs in [Set the knobs](https://codeherder.com/docs/github-app/#set-the-knobs):

| Reply field | Knob |
| --- | --- |
| `id` | `CH_SERVER_GITHUB_APP_ID` |
| `pem` | Save to a file. Set `CH_SERVER_GITHUB_APP_PRIVATE_KEY_FILE` to its path. |
| `slug` | `CH_GITHUB_APP_SLUG` |
| `webhook_secret` | `CH_GITHUB_APP_WEBHOOK_SECRET` |
| `client_id` | `CH_GITHUB_APP_CLIENT_ID` |
| `client_secret` | `CH_GITHUB_APP_CLIENT_SECRET` |

If the code expires, register the manifest again.

Check these settings on the App page:

- **Webhook URL:** `<base>/v1/githost-deliveries/github`
- **Callback URL:** `<base>/v1/github-app/callback`
- **Request user authorization (OAuth) during installation:** on. Without it, CodeHerder cannot prove who installed the App.
- **Webhook secret:** a long random string. Keep a copy.
- **Repository permissions:** **Pull requests: Read and write** and **Issues: Read and write**. Grant **Checks** and **Commit statuses** as Read and write, and **Actions** as Read-only. CodeHerder’s server uses Checks write (or Commit statuses write) to post the `CodeHerder` status check on a task’s pull request (see [Task status on pull requests](https://codeherder.com/docs/pr-status/)). Stages and devices only read all three, so a stage can read CI results. CodeHerder needs Pull requests and Issues write to post a task comment to a pull request (see [Posting a task comment to the pull or merge request](https://codeherder.com/docs/integrations/)). The manifest sets them. An App made by hand needs them too, and an owner of each organization must accept the new permissions on each installation.
- **Optional: Workflows.** Grant **Workflows: Read and write** only if a repo needs to let agents change files under `.github/workflows/`. The manifest leaves it out. A repo opts in with its **Workflow edits** setting (see [Letting agents change CI workflow files](https://codeherder.com/docs/repositories/#letting-agents-change-ci-workflow-files)).

GitHub sends installation events without a subscription. Generate a private key and a client secret on the App page.

### Set the knobs

| Knob | Value |
| --- | --- |
| `CH_SERVER_GITHUB_APP_ID` | The numeric App id. |
| `CH_SERVER_GITHUB_APP_PRIVATE_KEY_FILE` | Path to the PEM private key. CodeHerder reads it at each mint. |
| `CH_GITHUB_APP_SLUG` | The App’s URL name, from `github.com/apps/<slug>`. |
| `CH_GITHUB_APP_WEBHOOK_SECRET` | The webhook secret. |
| `CH_GITHUB_APP_CLIENT_ID` | The App’s OAuth client id. |
| `CH_GITHUB_APP_CLIENT_SECRET` | The App’s OAuth client secret. |

Set all six, then restart the server. If you set only some, CodeHerder logs a warning at boot and treats the App as not configured.

Devices need no App key. The server mints the token that a stage uses, for the workspace the stage runs in. See [Server-minted tokens](https://codeherder.com/docs/github-app/#server-minted-tokens). The device knobs `CH_GITHUB_APP_ID` and `CH_GITHUB_APP_PRIVATE_KEY_FILE` are optional. They apply to a self-hosted device that has no server App.

## What the App reads

With the App installed on a repo, CodeHerder reads that repo with a short-lived installation token. This covers merge state, mergeability and repo health. The token covers one repo and has read access only. CodeHerder revokes it after the read. A task can enter a waiting-for-merge stage on such a repo with no GitHub token.

You need a GitHub personal access token only for repos the App does not cover, and for GitHub Enterprise Server. If an installation is suspended or removed, CodeHerder uses the token for that repo. If there is no token, the repo’s health shows `not_connected`. The repo’s integration health names the credential in its **Read by** row.

## Server-minted tokens

A stage gets its GitHub token from the server. The device asks the server for a token for one sandbox, one repo and one access class. The server finds the installation that the sandbox’s workspace holds for the repo. It looks in the workspace first, then in the nearest group above it. A suspended installation does not count. The server mints a short-lived installation token with its own App key and returns it. The device needs no App ID and no private key. The App key stays on the server. The request names a sandbox, not a workspace. The server picks the installation from that sandbox’s own workspace. So a device that serves several workspaces gets each sandbox’s token from that sandbox’s workspace. If that workspace has no installation for the repo, the server mints nothing for it.

A device picks the source of a git credential in this order:

1. **The server.** The workspace has an installation that covers the repo.
2. **The device’s own key.** The device has `CH_GITHUB_APP_ID` and `CH_GITHUB_APP_PRIVATE_KEY_FILE`. This is a second choice, for self-hosted devices.
3. **The git-token pool.** Neither of the above covers the repo. The pool can hold a person’s token, so the pull request then shows that person.

If the server could mint a token and failed, the device serves no credential. It does not use the pool in that case, because the pool could show a person where the workspace’s App belongs. The device asks again on the next git call.

- **What the server mints.** A writable stage on a `read_write` repo gets Contents, Pull requests and Issues, all read and write. It also gets Checks, Commit statuses and Actions, all read, so the stage can read CI results. A read-only stage gets Contents read, Pull requests write and Issues write, plus the three CI reads, so it can post comments and reviews (see [Read-only stage posts](https://codeherder.com/docs/read-only-stage-posts/)). A fetch gets Contents read, Pull requests read and the three CI reads. Metadata read is always implied. A writable stage on a repo with **Workflow edits** allowed also gets Workflows write. See [Letting agents change CI workflow files](https://codeherder.com/docs/repositories/#letting-agents-change-ci-workflow-files).
- **Scope.** The token covers one repo and lasts about 1 hour. The device caches it until shortly before it expires. Sandbox teardown revokes it. The token never enters the sandbox. The relay adds it to each call.
- **Refusals.** The server mints only for the device that hosts the sandbox, for a sandbox that has not ended, and for a repo attached to that sandbox. A write token needs the repo at `read_write` for the workspace. A refusal says nothing about the installation or the key, and no log line holds a token.
- **Fallback.** Your installation may not grant every requested permission. GitHub then refuses the request with a 422. The server drops only the missing permissions and mints the rest. It logs one warning for each installation each hour. For example, an installation without Checks still gets Contents, Pull requests and Issues write, but the stage cannot read CI results. A read-only stage that posts comments is refused when Pull requests or Issues write is missing, and the error names it. Accept the permission update on GitHub. The server asks for the full set again within one hour. No restart is needed. A repo that allows workflow edits works the same way. If the installation lacks Workflows, the server drops only that permission, and GitHub then refuses a push that changes a workflow file. A 422 for a repo the installation cannot access does not cause the fallback.
- **What shows as the App.** Commits keep the agent’s git author. The push, the pull requests, the pull request comments and the issue comments show as `<slug>[bot]`.
- **Which source served.** The device page and `ch device show` name the source for each host: “GitHub mint: server”, “device key” or the pool. When the server did not cover a repo, the check says why, for example `no_installation`. The pool entry then shows as the fallback only.

## Troubleshooting

- **The panel has no Install button.** The server is not configured. Check that all six knobs are set and the server restarted.
- **GitHub says the callback URL is wrong.** The URL must match `<base>/v1/github-app/callback` exactly.
- **A repo reads Gap or Failing.** Open the repo page and read **Integration health**, or run `ch repo health <repo>`. A gap means GitHub shows pull request activity that no delivery announced. In GitHub, open the App’s **Advanced** tab, find the missed delivery under **Recent deliveries**, and redeliver it. While the status is not healthy, CodeHerder reads the repo from GitHub on a short timer, so task state stays correct.
- **No events arrive.** Check the webhook URL and the webhook secret. GitHub’s **Advanced** tab shows each delivery and its answer. A `401` means the secret differs.
- **The installation shows as suspended.** Someone suspended the App on GitHub. Unsuspend it there. CodeHerder clears the mark when GitHub says so.
- **No `CodeHerder` check appears on pull requests.** See [Task status on pull requests](https://codeherder.com/docs/pr-status/). The installation lacks Checks or Commit statuses write. The server log shows one WARN a day, `cannot post the CodeHerder status check`. Add both permissions to the App, then have an owner of the organization accept the update on the installation. CodeHerder tries again within an hour. A repository with no App installation uses the workspace GitHub token instead. That token needs **Commit statuses: Read and write**. If it lacks that permission, the server log shows a WARN that names it.
- **A comment fails with “the host refused the credential”.** The App lacks Pull requests or Issues write permission on that installation. Accept the permission update on GitHub, then retry the comment.
- **Pull request creation fails with 403 on a minted token.** The installation lacks Pull requests or Issues write. The server log shows the warning “lacks requested permissions”. Accept the permission update on GitHub.
- **`gh pr checks` fails with 403 on a minted token.** The installation lacks Checks, Commit statuses or Actions read. The server log shows the warning “lacks requested permissions” with the missing names. Grant the three read permissions on the App, then accept the update on the installation.
- **A push that changes a file under `.github/workflows/` is refused.** Either the repo’s **Workflow edits** setting is off, or the installation lacks **Workflows: Read and write**. Fix both: allow workflow edits on the repo, and grant the permission on the App, then accept the update on the installation. The change applies from the next stage start. See [Letting agents change CI workflow files](https://codeherder.com/docs/repositories/#letting-agents-change-ci-workflow-files).
- **Pull requests still show a person, not the App.** Open the device page and read the git mint check. A reason of `no_installation` means no installation of the workspace covers that repo. Add the repo to the installation, or connect the installation to the workspace. A reason of `app_not_configured` means the server has no App. A device that predates the server mint (an older `ch`) serves its pool until it updates.
- **A repository is missing.** The installation may not include it. Add it in GitHub under the App’s **Configure** page.
- **The claim fails after you return.** The link is valid for a few minutes and works once. Start the install again from the same signed-in account.
- **You installed from GitHub and the workspace still shows no installation.** Finish on the page CodeHerder showed after the install. If you closed it, select **Connect** in the **GitHub App** panel, or install again from CodeHerder.

## Related guides

- [Integrations](https://codeherder.com/docs/integrations/) — connect a token for GitHub or GitLab.
- [Connecting repositories](https://codeherder.com/docs/repositories/) — register the repositories agents work in.
