GitHub Enterprise (ghe.com) — Setup Guide
Audience: anyone connecting a GitHub Enterprise Cloud data-residency tenant
(*.ghe.com) to VibeFlow. This is the hands-on, step-by-step walkthrough,
including the gotchas that actually bite. For the architecture and ops runbook,
see the companion github-enterprise.md.
What you're setting up
GitHub Enterprise Cloud with data residency puts a customer on an isolated
tenant with its own subdomain — e.g. https://axiomstudio-vibeflow.ghe.com, API
at https://api.axiomstudio-vibeflow.ghe.com. A github.com GitHub App cannot
serve that tenant (different, isolated host), so the customer registers their
own GitHub App on their tenant and pastes its credentials into VibeFlow. One
configuration per VibeFlow organization; all projects in that org share it.
Login stays SAML — there is no GitHub OAuth anywhere in this flow.
Once connected, the App powers: PR creation, PR comments, QA/Security check runs, issue sync, commit links, and cloud-runner git access — all against the tenant.
Prerequisites
| # | Requirement |
|---|---|
| 1 | A GitHub Enterprise Cloud data-residency tenant (e.g. axiomstudio-vibeflow.ghe.com) and enterprise-owner access to it. |
| 2 | An organization inside that enterprise that you own, with a test repo. Apps install on organizations — you cannot install on an EMU user account. |
| 3 | A VibeFlow deployment reachable from the tenant over HTTPS (e.g. https://cloud-uat.axiomstudio.ai). The tenant must be able to POST webhooks to it. |
| 4 | Access to the VibeFlow Integrations tab as a workspace-org admin (see Access & the gate). |
| 5 | For cloud-runner clone/push: the cortex/nimbus backend that VibeFlow talks to must include the host-aware git-credentials fix (cortex develop ≥ commit a0dcd516). Control-plane features — PRs, checks, Test Connection — do not need this. |
The flow at a glance
1. Create the GitHub App on the tenant ──► get App ID + private key (.pem)
2. Save the VibeFlow card (host, App ID, .pem, webhook secret)
──► card shows the per-org Webhook URL
3. Put that Webhook URL + secret back into the GitHub App
4. Test Connection in the card ──► green + Install link appears
5. Install the App on an org + repos ──► webhook fires → "Installation detected"
6. Link a repo to a project → create a runner → clone/push
There is a deliberate two-pass on the webhook (steps 1→3): you need the App ID to save the card, and you need the card's saved config to learn the per-org Webhook URL. GitHub Apps are editable, so you finish the webhook fields in step 3.
Part 1 — Register the GitHub App on your tenant
On https://<sub>.ghe.com → Settings → Developer settings → GitHub Apps → New GitHub App.
Basic information
| Field | Value |
|---|---|
| GitHub App name | anything, e.g. axiomstudio-enterprise-app |
| Homepage URL | your VibeFlow base URL, e.g. https://cloud-uat.axiomstudio.ai |
Identifying and authorizing users — turn these OFF
Login is SAML, so the App never does GitHub OAuth:
- Callback URL — leave empty (if the field insists on a value, it's harmless since the next box is off).
- Request user authorization (OAuth) during installation — unchecked.
- Enable Device Flow — unchecked.
- Expire user authorization tokens — irrelevant (leave default).
Post installation
- Setup URL — leave empty. Install detection is webhook-driven; pointing it at a VibeFlow endpoint just lands the installer on a JSON page.
- Redirect on update — off.
Webhook
- Active — checked.
- Webhook URL — for now put a placeholder (you'll replace it in Part 3 with the per-org URL). ⚠️ This is the field people get wrong — see The #1 gotcha.
- Secret — you'll set this in Part 3 to match the card's secret.
- SSL verification — Enabled.
Permissions (Repository permissions)
Set exactly these; nothing else:
| Permission | Access |
|---|---|
| Contents | Read and write |
| Pull requests | Read and write |
| Checks | Read and write |
| Issues | Read and write |
| Metadata | Read-only (auto-selected) |
No Organization or Account permissions are required.
Subscribe to events
Check: Pull request, Issues, Push.
(installation, installation_repositories, and meta are delivered
automatically — you don't tick them.)
Where can this GitHub App be installed?
- This enterprise — recommended, so you can install it on organizations in the enterprise.
- Only on this account restricts installation to the owning account only.
Create the app. Then:
- Note the App ID — the numeric id on the app's General/About page (e.g.
17921). ⚠️ Use the App ID, not the Client ID. - Generate a private key → a
.pemdownloads (GitHub shows it once — keep it safe; it's the app's signing key).
Part 2 — Configure the VibeFlow card
VibeFlow → project → Integrations tab → GitHub Enterprise (a separate card, below the regular GitHub one).
| Field | Value |
|---|---|
| Enterprise host | the bare subdomain, e.g. axiomstudio-vibeflow (it derives https://api.axiomstudio-vibeflow.ghe.com; a full URL works too) |
| App ID | the numeric App ID from Part 1 |
| Display name | free text, e.g. Axiom Studio Enterprise |
| Private key | upload the .pem (or paste its contents) |
| Webhook secret | click Generate, then Copy it now — it's write-only and shows •••• configured after saving |
| Enabled | check it |
Click Save configuration.
After saving, the "APP REGISTRATION ON YOUR TENANT" section appears with the per-org Webhook URL (and, after Test Connection, the Install link). Copy that Webhook URL.
Part 3 — Finish the webhook on the GitHub App
Back in the GitHub App settings:
- Webhook URL → paste the exact per-org URL from the card. It looks like:
wherehttps://cloud-uat.axiomstudio.ai/rest/v1/vibeflow/github/webhook/<ORG_ID><ORG_ID>is your VibeFlow organization's id. - Secret → paste the same secret you generated in the card.
- Save.
GitHub sends a fresh ping on every webhook change — see verification.
Part 4 — Test Connection
In the card, click Test Connection. It builds an App JWT from your stored
credentials and calls GET https://api.<sub>.ghe.com/app:
- Green → "Connection OK — app "
<name>" with N installations". This proves your App ID + private key + host are all correct, and stores the app slug. The Install link now appears. - Errors are classified:
bad_credentials(App ID / key wrong),app_not_found/unreachable_host(host wrong).
0 installations at this point is expected — you haven't installed it yet.
Part 5 — Install the App & verify it end to end
- Click the Install link in the card
(
https://<sub>.ghe.com/apps/<app-slug>/installations/new), or install from the org → Settings → GitHub Apps side. - Choose an organization in the enterprise and select the repos VibeFlow may touch. (If the account list is empty, see troubleshooting.)
- Verify in GitHub App → Advanced → Recent Deliveries:
- the
installationevent returns 202 → - the card flips from
0 installationsto "Installation detected".
- the
- In VibeFlow, link that repo to a project (project → Settings).
- Create a cloud runner on the project and confirm it clones + pushes.
(Runner clone/push needs cortex ≥
a0dcd516.)
The #1 gotcha: the Webhook URL needs your org id
The regular github.com webhook route is …/rest/v1/vibeflow/github/webhook.
The enterprise route is per-organization:
…/rest/v1/vibeflow/github/webhook/<ORG_ID>.
If you leave off the /<ORG_ID>, the delivery hits VibeFlow's github.com handler,
which verifies against the deployment's github.com secret — not your enterprise
secret — and fails. The response code tells you exactly where it landed:
| Response | Meaning | Fix |
|---|---|---|
400 {"error":"Invalid signature"} | Hit the github.com route — URL is missing /<ORG_ID> | Set the Webhook URL to the per-org URL from the card |
401 {"error":"Invalid signature"} | Reached the enterprise route, but the secret doesn't match | Make the GitHub App's webhook secret identical to the card's |
| 404 | Unknown/disabled org id in the URL | Use the exact URL the card generated; ensure the config is Enabled |
202 {"status":"ok"} | ✅ Verified and processed | — |
The card only reveals the per-org URL after you save, which is why the setup
is a two-pass on the webhook. When you change the webhook, GitHub re-sends a
ping; look at the newest delivery (or hit Redeliver) — a stale earlier
delivery keeps its old snapshot in the payload body.
Troubleshooting
Empty "Install" list
On a data-residency (EMU) enterprise, apps install on organizations, and the install page lists only orgs where you are an owner.
- Set the App's "Where can this GitHub App be installed?" to This enterprise.
- Make sure you own an organization in the enterprise (create one under Enterprise → Organizations → New organization, with a test repo).
- Reload the install page, or install from org → Settings → GitHub Apps.
Test Connection fails
bad_credentials→ the App ID and/or private key don't match. Re-check the numeric App ID and re-upload the.pem.app_not_found/unreachable_host→ the host is wrong. Use the bare subdomain; the API base must resolve toapi.<sub>.ghe.com.
Card doesn't appear at all (Integrations tab)
The card is gated to workspace-org admins. See Access & the gate.
Runner clones an empty workspace / push fails
- Empty clone → the cortex/nimbus build is older than
a0dcd516(host-aware credentials). Deploy the cortex fix. - Push fails after ~1 hour → the token TTL (see known limitations).
Access & the gate
The GitHub Enterprise card and its /rest/v1/admin/github-enterprise endpoints
are normally gated to organization admins of a workspace org (an org whose
name is a bare domain, i.e. contains no @). This mirrors the SAML SSO gate.
- If the card isn't visible, check
…/rest/v1/user/is-adminand…/rest/v1/user/is-workspace-org— both must betrue. - In some test environments this gate is temporarily relaxed to any authenticated user; that relaxation is tracked and must be restored before production.
Note: even under the relaxed gate, org_id always comes from the session —
tenant isolation is never relaxed.
Known limitations
- Runner push window = 1 hour. Cloud-runner git credentials are GitHub App installation tokens (GitHub-capped at 1 hour), injected once at runner start. Clone at start and control-plane PR/checks are unaffected, but a push more than ~1 hour into a session fails until the runner restarts. A call-home token-refresh is the planned follow-up.
- User PATs and MCP PAT tooling stay github.com-only.
- Data residency is a data-flow question: the VibeFlow control plane pulls PR diffs and repo content out of region for PR creation, checks, and diff display. Surface this to the customer's security review before go-live — it needs an owner.
Quick reference — what goes where
| Value | GitHub App page | VibeFlow card |
|---|---|---|
| Tenant host | (the tenant you're on) | Enterprise host = bare subdomain |
| App ID (numeric) | General/About page | App ID |
Private key (.pem) | Generate → downloads | Private key (upload) |
| Webhook secret | Webhook → Secret | Webhook secret (Generate → copy) — must match |
| Webhook URL | Webhook → URL (…/webhook/<ORG_ID>) | shown after Save, in "App registration" |
| App slug | (auto, from name) | filled by Test Connection |
| Permissions | Contents/PRs/Checks/Issues RW, Metadata R | — |
| Events | Pull request, Issues, Push | — |
Companion docs: github-enterprise.md (architecture & ops runbook) and the Somecompany onboarding checklist.