> For the complete documentation index, see [llms.txt](https://docs-lunar.earthly.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs-lunar.earthly.dev/install/git-platforms/github.md).

# GitHub

The Hub authenticates with a **GitHub App** installed on your organization. The App vends short-lived installation tokens, which Lunar uses to read repositories and post results, so no long-lived credential is stored anywhere.

You create the App; Lunar never creates one on your behalf. Either flow below produces the same App.

This page covers the GitHub side only. The install steps that consume it are in [Prerequisites → Step 5](/install/lunar-hub/self-hosted/prerequisites.md#step-5-connect-your-git-platform) for self-hosted, or the [Dedicated](/install/lunar-hub/dedicated/setup.md) setup steps.

## Permissions

The App ends up with these permissions however you create it: the setup tool sets them for you, and the [manual flow](#manual-setup-alternative) has you set them by hand. This is the list to hand a security reviewer.

| Permission           | Access | Why                                           |
| -------------------- | ------ | --------------------------------------------- |
| `actions`            | read   | Read workflow runs for CI data collection     |
| `checks`             | write  | Post policy results as PR checks              |
| `contents`           | read   | Fetch config and source for policy evaluation |
| `metadata`           | read   | Required by GitHub on every App               |
| `pull_requests`      | write  | Post PR comments and statuses                 |
| `repository_hooks`   | write  | Auto-register per-repo webhooks               |
| `organization_hooks` | write  | Auto-register organization-level webhooks     |

**Events:** `push`, `pull_request`, `workflow_run`.

## App creation

### Setup tool (recommended)

1. Visit [**earthly.dev/lunar/github-app-setup**](https://earthly.dev/lunar/github-app-setup/).
2. Follow the prompts. The tool uses GitHub's [manifest flow](https://docs.github.com/en/apps/sharing-github-apps/registering-a-github-app-from-a-manifest) to register the App with the permissions and events above.
3. **Download the PEM private key when prompted.** GitHub shows it exactly once — if you click past this page, you'll have to generate a new key from the App settings later.
4. Click *Install App on GitHub* and select the org. Choose **All repositories** unless you have a specific reason not to — Lunar's actual monitoring scope is configured in `lunar-config.yml`, so a narrower scope here just means coming back to **Org Settings → GitHub Apps → Lunar → Repository access** every time you add a new repo to Lunar.

The hosted tool proxies the manifest exchange to GitHub and returns the credentials to your browser; we never persist them.

### Manual setup (alternative)

{% hint style="info" %}
**Create the App by hand** when the hosted tool can't reach your environment — either of these:

* **GitHub Enterprise Server.** The App has to be created on your own GHES instance.
* **Air-gapped**, or `earthly.dev` is otherwise unreachable from the browser session you would run the tool in.
  {% endhint %}

Create the App at GitHub's [App creation page](https://github.com/settings/apps/new) (or for an org: **Org Settings → Developer settings → GitHub Apps → New GitHub App**). For GHES, create it on your GHES instance instead. GitHub's [registering-a-github-app docs](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app) walk through every form field if you want a reference.

Set the [permissions and events above](#permissions), then fill in the rest:

* **Homepage URL** — any URL works (e.g. your internal Lunar URL, or `https://earthly.dev/lunar`).
* **Webhook** — uncheck "Active." The Hub registers its own per-repo webhooks at runtime; the App-level webhook is unused. URL can be a placeholder (e.g. `https://example.com/placeholder`) — GitHub requires a value but nothing will ever hit it.
* **Webhook secret** — leave blank. Since you unchecked "Active" above, GitHub won't deliver App-level events. The Hub's per-repo webhook signing secret is a separate thing — see [prereqs Step 6](/install/lunar-hub/self-hosted/prerequisites.md#step-6-plan-your-kubernetes-secrets).
* **Where can this app be installed?** — "Only on this account."

Then, once the App exists:

1. **Generate a private key** (App settings → "Private keys" → "Generate a private key"). Save the `.pem` file — GitHub does not show it again.
2. **Install the App** on your organization (App settings → "Install App"). Choose **All repositories** unless you have a specific reason not to, for the same reason as above.

Whichever flow you used, you now hold four things: the owner, the App ID, the installation ID, and the PEM private key. [Prereqs Step 5](/install/lunar-hub/self-hosted/prerequisites.md#step-5-connect-your-git-platform) lists where each one comes from. Confirm you have all four, and that the PEM is saved, before moving on.

## Hub configuration (Dedicated)

Nothing to configure: Earthly runs the Hub and configures it for you. Continue at [Dedicated → Deposit your credentials](/install/lunar-hub/dedicated/setup.md#step-4-deposit-your-secrets), where the PEM goes to your install rather than to Earthly. The self-hosted section below does not apply to you.

## Hub configuration (self-hosted)

Those four values are what the Hub reads. [Install Step 4](/install/lunar-hub/self-hosted/install-walkthrough.md#git-platform-credentials) has the values block to paste; this is the reference behind it.

| Setting         | Chart value                     | Environment variable         | Meaning                                                                                                                                      |
| --------------- | ------------------------------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| Owner           | `hub.github.app.owner`          | `HUB_GITHUB_APP_OWNER`       | The org or user the App is installed on. Matching is case-insensitive and trimmed.                                                           |
| App ID          | `hub.github.app.id`             | `HUB_GITHUB_APP_ID`          | Numeric. Quote it in YAML, or it renders as scientific notation.                                                                             |
| Installation ID | `hub.github.app.installId`      | `HUB_GITHUB_APP_INSTALL_ID`  | Numeric, from the install URL.                                                                                                               |
| Private key     | mounted from `lunar-github-app` | `HUB_GITHUB_APP_PRIVATE_KEY` | The base64-encoded PEM.                                                                                                                      |
| Base URL        | `hub.github.baseUrl`            | `HUB_GITHUB_BASE_URL`        | GHES only. Your instance's API endpoint.                                                                                                     |
| Host            | (none)                          | `HUB_GITHUB_HOST`            | Defaults to `github.com`. On GHES set it to your instance hostname, so component names (`<host>/org/repo`) match what your manifests author. |

All four App fields are required together. A partial configuration is rejected at startup.

### Multiple organizations

When one Hub fronts several orgs that each install their own Lunar App, register one entry per owner in `HUB_GITHUB_APPS` instead of the single-App variables:

```bash
export HUB_GITHUB_APPS=$(cat <<'EOF'
[
  {"owner": "earthly", "app_id": 123, "private_key_path": "/secrets/github-apps/earthly.pem", "install_id": 100},
  {"owner": "acme",    "app_id": 456, "private_key_path": "/secrets/github-apps/acme.pem",    "install_id": 200}
]
EOF
)
```

Each entry pins its own App credentials and installation; the Hub mints tokens scoped to the right org per request. Owner matching is case-insensitive and trimmed.

For the PEM files, the recommended pattern is one Kubernetes Secret with multiple keys, mounted as a single volume:

```yaml
# Secret
data:
  earthly.pem: <base64-encoded PEM>
  acme.pem:    <base64-encoded PEM>

# Pod
volumes:
- name: github-apps
  secret: { secretName: github-apps }
volumeMounts:
- { name: github-apps, mountPath: /secrets/github-apps, readOnly: true }
```

`HUB_GITHUB_APPS` is mutually exclusive with the single-App env vars (`HUB_GITHUB_APP_OWNER` / `HUB_GITHUB_APP_ID` / `HUB_GITHUB_APP_PRIVATE_KEY` / `HUB_GITHUB_APP_INSTALL_ID`). Use one mode or the other.

## Next steps

Self-hosted installs carry these four values into Kubernetes secrets at [prereqs Step 6](/install/lunar-hub/self-hosted/prerequisites.md#step-6-plan-your-kubernetes-secrets), and into your chart values at [install Step 4](/install/lunar-hub/self-hosted/install-walkthrough.md#git-platform-credentials). On Dedicated you [deposit the PEM](/install/lunar-hub/dedicated/setup.md#step-4-deposit-your-secrets) into your install's secret drop instead.

For how GitHub and GitLab differ once Lunar is running, see [Git Platforms](/install/git-platforms.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs-lunar.earthly.dev/install/git-platforms/github.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
