GitHub Enterprise (ghe.com Data Residency)
VibeFlow supports GitHub Enterprise Cloud tenants with data residency (https://<subdomain>.ghe.com, API at https://api.<subdomain>.ghe.com) alongside github.com, per organization. The deployment-level github.com App keeps serving every organization that has no enterprise configuration; an organization with an enabled GitHub Enterprise configuration routes its entire GitHub surface — App API calls, webhooks, pull requests, check runs, issue sync, commit links, and cloud-runner git credentials — to its own tenant.
Key platform constraint (why bring-your-own-app): a github.com GitHub App cannot be installed on or authenticate against a .ghe.com tenant. Data-residency enterprises can only install apps owned by the enterprise or its own organizations, so the customer registers their own GitHub App on their tenant and pastes its credentials into VibeFlow. Login stays SAML — there is no per-tenant GitHub OAuth anywhere in this integration.
Setup guide (organization admin)
The GitHub Enterprise card lives in Integrations and is visible only to workspace-organization admins (matching the /rest/v1/admin/github-enterprise endpoint gate).
1. Register the GitHub App on your tenant
On https://<subdomain>.ghe.com, as an organization owner:
- Org Settings → Developer settings → GitHub Apps → New GitHub App (org-owned; "Where can this app be installed: this account only" is fine).
- Name it (e.g.
VibeFlow (Acme)); homepagehttps://cloud.axiomstudio.ai. - Webhook URL + secret: copy both from the VibeFlow card (see step 2 — save the card first to get your per-org webhook URL, and use its Generate button for the secret).
- Permissions: Contents RW · Pull requests RW · Checks RW · Issues RW · Metadata R.
- Events:
installation,installation_repositories,pull_request,issues,push. - Setup URL and OAuth callback: leave EMPTY. Install detection is webhook-driven and login is SAML — the app needs no OAuth configuration at all.
- Generate a private key (a
.pemfile downloads) and note the App ID from the app's About page.
2. Configure the card in VibeFlow
- Integrations → GitHub Enterprise.
- Enter the host (bare subdomain like
acme, or the fullhttps://acme.ghe.com). The card shows the derived API base (https://api.acme.ghe.com) read-only — both URLs are stored explicitly. - Enter the App ID, upload the .pem private key (or paste it), and Generate (or paste) the webhook secret. Secrets are write-only: they are AES-256-GCM-encrypted at rest, never echoed by any API, and render as
•••• configuredafter saving. Leaving a secret field empty on a later save keeps the stored value. - Copy the Webhook URL (
/rest/v1/vibeflow/github/webhook/{org_id}) into the app registration. - Enable the toggle and Save.
- Click Test Connection — it builds an app JWT, calls
GET {api_base}/app(validating key, host, and App ID in one shot), stores the app slug, and lists installations. Errors are classified:bad_credentials(JWT rejected — check App ID/private key),app_not_found/unreachable_host(check the host). - Use the Install link (appears once Test Connection has discovered the slug) to install the app on your tenant org and select the repositories VibeFlow may touch. The installation webhook binds the installation to your VibeFlow organization automatically.
- Link repositories to projects in each project's Settings tab exactly as with github.com.
Architecture notes (operators / reviewers)
- The enabled config row IS the feature flag.
resolveGitHubForOrg(orgID): enabledgithub_enterprise_configsrow → per-request tenant client from decrypted credentials; otherwise the deployment github.com singleton. No separate flag system. - Host-scoped identities. Fresh ghe.com tenants issue installation IDs from ~1 and owner/repo names repeat across hosts, so
(github_host, installation_id)and(github_host, repo_owner, repo_name)are the unique keys on all GitHub tables. The installation-token cache is keyed by host too. - Webhooks: secret before verify. The per-org route picks the org from the path (ghe.com deliveries carry no reliable host header;
X-GitHub-Enterprise-Hostis GHES-only), verifies the HMAC over the raw body with that org's secret, and only then parses. The org id in the URL is routing, not authentication — the HMAC is the authentication. Every installation/repo/PR-mapping a delivery references must belong to the path org (two orgs may share one tenant host). - Runner tokens. For enterprise-linked projects, runner creation mints a 1-hour installation token downscoped to the job repo with
contents:writeonly (bothrepositoriesandpermissionsare always passed at mint — omittingpermissionswould grant the app's full grant). The token travels through the existing cortex git-credentials channel as a hiddenmanagedprovider row with usernamex-access-token; the PEM never leaves the control plane. The token is re-minted on every runner start and best-effort revoked when the user's last runner is deleted.
Ops runbook
| Situation | What to do |
|---|---|
| Private key rotation | GitHub Apps support multiple private keys. On the tenant: generate a new key on the app. In VibeFlow: upload the new .pem in the card and save (write-only replace), run Test Connection, then delete the old key on GitHub. No schema or downtime involved. |
| Webhook secret rotation | Generate a new secret in the card, save, then paste the same secret into the app registration on the tenant. Deliveries signed with the old secret 401 in the window between the two updates — GitHub retries. |
| Runner pushes fail with auth errors after ~1 hour | Known v1 ceiling: the minted token's TTL is 1 hour and it is only re-minted on runner start. Restart the runner to re-inject a fresh token. The follow-up (call-home refresh over the pod's vault api_key channel, Buildkite-style ~50 min cadence) is tracked as future work. |
| Webhook deliveries failing | Check the tenant app's Advanced → Recent Deliveries. 404 = org id wrong in the webhook URL or config disabled; 401 = secret mismatch (rotate per above); 202 with {"status":"error"} = signature verified but processing failed — check github_webhook_events (host-scoped) and server logs. |
| "Installation suspended" on the card | The app was suspended on the tenant; unsuspend it there — the webhook updates the row. |
| Cross-org 4xx on webhook processing | A delivery referenced an installation/repo owned by a different VibeFlow org on the same tenant host. This is the tenant-isolation guard working; check which VibeFlow org owns the repo link. |
| Cortex provider accumulation | Each enterprise runner start mirrors a fresh cortex git provider carrying the current token (cortex has no reliable provider update path). Stale mirrors are inert once their 1-hour token expires; they are not user-visible. |
| IP allowlists | If the customer enables GitHub's "IP allow list configuration for installed GitHub Apps", our egress IPs must be admitted. Theirs: gh api /meta --hostname <sub>.ghe.com. |
Known limits (v1, by design)
- Runner push window = token TTL (1 hour); re-mint happens on runner start.
- User PATs and the MCP PAT tooling stay github.com-only.
- Bitbucket Data Center / GitLab: out of scope.
- Manifest-based app auto-creation deferred (manual credential paste is the documented flow).
- Data residency is a data-flow question: the VibeFlow control plane pulls PR diffs and repo content out of region. This must be surfaced in the customer's security review / DPA before go-live — it is a conversation, not an engineering item.
Manual E2E checklist (test tenant)
Run on the test tenant (https://axiomstudio-vibeflow.ghe.com, API https://api.axiomstudio-vibeflow.ghe.com) against a deployed environment whose webhook URL the tenant can reach:
- Register a test app per the setup guide above (org-owned, permissions/events per step 1, Setup URL + OAuth callback empty).
- Save credentials in the card; Test Connection green (JWT accepted at
api.<sub>.ghe.com/app; slug persisted; install link appears). - Install the app on a tenant org/repo → webhook hits
/webhook/{org_id}, HMAC verifies, installation row created org-bound with the tenantgithub_host. - Link the repo to a project; the picker lists tenant repos.
- The one unverified platform fact (highest priority):
git push https://x-access-token:<minted-token>@<sub>.ghe.com/owner/repo.git— the mechanism is documented for enterprise cloud but no official page prints this literal host form. Prove it before anything downstream. - Runner E2E: create a runner → managed provider injected → clone, commit, push from the pod. Also record what git
user.name/user.emailcortex sets (bot attribution would be<app-slug>[bot]+<BOT_USER_ID>+<app-slug>[bot]@users.noreply.<host>). create_prvia MCP → PR opens on the tenant; draft-PR project setting honored.- QA Verification + Security Review check runs appear on the PR and transition on verify/reject.
- Commit links in the UI point at
https://<sub>.ghe.com/...; provider detection classifies the remote as GitHub. - Cross-tenant regression: a github.com org in the same deployment runs the identical flow unchanged; same-named owner/repo linked on both hosts coexist.