> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ewake.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# GitHub App (self-hosted)

> Create a GitHub App in your own organisation and let a self-hosted Ewake deployment act as it. This is how GitHub is connected on a self-hosted deployment.

<Info>
  **Who this is for.** Operators running Ewake in their own AWS account (BYOC). If you're using Ewake's hosted service, connect the shared App from the [main GitHub integration page](/integrations/code/github) instead.
</Info>

A self-hosted deployment reads GitHub as a GitHub App you own. You create the App,
you hold its private key, and the install callback completes on your own deployment —
nothing routes through Ewake.

This is the only way to connect GitHub here. There is no token form in the dashboard
and no token field in the integration: reads are made with installation tokens, minted
on demand and never stored. A deployment that does not read GitHub can skip this page
entirely.

<Note>
  The App path installs from `github.com` and reads `api.github.com`, which covers
  GitHub Enterprise Cloud as well. A self-hosted GitHub Enterprise **Server** is a
  different host and this flow does not reach it — talk to us before you start.
</Note>

***

## Why an App

An App is installed on the organisation by an owner, is granted a fixed read set, and
keeps working when that person leaves. Its tokens are minted per installation, live
about an hour, and are never written down — so there is nothing to rotate, nothing to
leak from the database, and no one person's rate limit shared across every read.

That is the reason there is no token option to fall back to.

***

## 1. Create the App

Go to `https://github.com/organizations/<your-org>/settings/apps/new` (replace
`<your-org>` with your organisation's login — the one that appears in your repository
URLs). Fill in these fields — leave everything else at its default:

| Field                                   | Value                                                    |
| --------------------------------------- | -------------------------------------------------------- |
| GitHub App name                         | `Ewake (<your-org>)` — ≤ 34 characters, unique on GitHub |
| Homepage URL                            | `https://ewake.ai`                                       |
| Callback URL                            | *leave blank*                                            |
| **Setup URL**                           | `<your-dashboard-url>/api/v1/auth/github/callback`       |
| **Redirect on update**                  | **checked**                                              |
| Webhook → Active                        | **unchecked**                                            |
| Where can this GitHub App be installed? | Only on this account                                     |

<Warning>
  GitHub labels **Setup URL** as optional on the form; that is optional to *GitHub* — Ewake requires it. It is the URL GitHub redirects to once someone installs the App, so a blank Setup URL leaves the customer on the App's own settings page on GitHub, and the install completes with no callback ever reaching Ewake.
</Warning>

<Note>
  **Redirect on update** is the checkbox directly under Setup URL. Tick it. Without it,
  someone who already has the App installed and clicks **Install** in the Ewake dashboard
  again — to add a repository, say — saves the change on GitHub and is then left sitting
  there, while the dashboard waits on a callback that never arrives.
</Note>

Then grant the read permissions Ewake calls:

* **Repository permissions** (all Read): Contents, Issues, Pull requests, Actions,
  Deployments. Metadata is set to Read automatically.
* **Organization permissions** (Read): Members.

Click **Create GitHub App**.

<Note>
  A few things worth knowing before you do:

  * The organisation must be an organisation, not a personal account. An App on a
    personal account installs only on that person, and Ewake's install callback
    refuses that.
  * The dashboard URL under **Setup URL** must be `https://`. GitHub will not call
    back over plain http, and the path `/api/v1/auth/github/callback` must match
    exactly.
  * App names are unique across GitHub. `Ewake (<your-org>)` avoids collisions with
    other customers. The slug follows the name — GitHub lowercases it and replaces
    spaces with hyphens — so `Ewake (acme)` becomes `ewake-acme`. Note the slug: you
    need it in step 3.
</Note>

***

## 2. Collect three values

Creating the App lands you on its settings page. From here:

| Value             | Where                                                                               |
| ----------------- | ----------------------------------------------------------------------------------- |
| `APP_SLUG`        | the last path segment of the App's URL, `github.com/settings/apps/<slug>`           |
| `CLIENT_ID`       | "Client ID" on the settings page                                                    |
| `APP_PRIVATE_KEY` | "Generate a private key" — downloads a `.pem` once, and GitHub never shows it again |

<Note>
  The App also shows a `CLIENT_SECRET`. Ewake does not use it — the OAuth user-to-server
  flow it exists for is not part of this integration. Leave it out.
</Note>

***

## 3. Hand them to terraform

The three are terraform variables on your deployment — `github_app_client_id`,
`github_app_slug` and `github_app_private_key`. Apply, and terraform creates the
Secrets Manager secret holding them and injects `GITHUB_CLIENT_ID`, `GITHUB_APP_SLUG`
and `GITHUB_APP_PRIVATE_KEY` into the reactive task. Nothing to create by hand.

`github_app_private_key` is declared `sensitive`, so terraform keeps it out of plan
output and the apply log. It still reaches the state file — that is what passing a
key to terraform costs — so the state backend wants encryption and restricted reads,
as it already does for the JWT and admin passwords terraform generates for you.

The self-hosted repository declares all three for you, so put them in your
`terraform.tfvars`:

```hcl theme={null}
github_app_client_id = "Iv23li..."
github_app_slug      = "ewake-acme"

github_app_private_key = <<-EOT
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA...
-----END RSA PRIVATE KEY-----
EOT
```

<Warning>
  A `.tfvars` file takes literal values only. `file("your-app.private-key.pem")`
  looks natural there and is rejected outright:

  ```
  Error: Function calls not allowed
  ```

  Paste the PEM in as the heredoc above, or keep it in its own file and pass it
  through the environment, where `cat` is available:

  ```bash theme={null}
  export TF_VAR_github_app_private_key="$(cat ~/Downloads/ewake-acme.private-key.pem)"
  terraform apply
  ```

  Don't try to answer terraform's interactive prompt with it — that reads one line,
  and a PEM is many.
</Warning>

<Warning>
  **All three or none.** Setting only one or two fails the plan, naming which are
  missing. That case is almost always a half-finished edit.

  Leaving all three unset is a supported deployment, but it means GitHub is not
  connected at all — there is no token form to fall back to. The dashboard says the
  deployment has no App configured, and that is the whole story.
</Warning>

<Note>
  Setting the three on an older application build is harmless — terraform writes the
  secret and injects the values, and a build that does not read them ignores them. The
  install action appears once you are on a build that does. Check the release notes.
</Note>

***

## 4. Install it

From the dashboard, not from GitHub: **Integrations → GitHub → Install the Ewake
GitHub App**.

<Warning>
  Starting from the App's own page on GitHub skips the step that ties the install to
  your browser session, and the callback refuses it.
</Warning>

***

## Troubleshooting

**After clicking Install, GitHub lands you on the App's settings page.** Setup
URL is empty on the App. Open the App on GitHub → **Post installation** → set
**Setup URL** to `<your-dashboard-url>/api/v1/auth/github/callback` → **Save
changes**. Then uninstall the App from your organisation and reinstall from the
Ewake dashboard.

**You changed the installation on GitHub and landed on "GitHub connection failed".**
Expected, and harmless. **Redirect on update** sends you back to Ewake after any
change to the installation, but a change you started on GitHub carries none of the
session state the callback checks, so the callback refuses it. Your change is already
saved on GitHub, and Ewake picks up the new repositories on its next installation
token — within the hour. Start the change from **Integrations → GitHub** in the
dashboard to skip the detour.

**The dashboard reports "GitHub connection failed" after install.** Check
reactive's logs. Two common causes:

* `Rejected a GitHub callback that carried no connect nonce of ours` — the
  install was started from the App's page on GitHub rather than from the Ewake
  dashboard. The dashboard sets a session-scoped nonce before it sends you to
  GitHub, and the callback checks it. Uninstall and start again from
  **Integrations → GitHub** in the dashboard.
* `GitHub App identity: private key parse failed` — the PEM in your deployment's
  `github-app` secret has been collapsed onto one line, or its newlines were not
  escaped as `\n` inside the JSON string. Re-seed the secret with the PEM's
  real newlines encoded as `\n`.

***

## Rotating the key

Generate a new private key on the App's settings page, point
`github_app_private_key` at the new PEM, and apply — terraform writes a new version
of the secret. The ARN does not change, so the task definition does not either and
the running task keeps the old key in memory: force a new deployment of the reactive
service to pick it up. Delete the old key on GitHub once the new one is serving. The
App, its installation and its slug are unchanged, so nothing else moves.
