---
id: w2/m8/t001
title: ADR docs/ADR026-github-integration.md — GitHub App model, manifest flow, env vars, trust chain
worker: worker2
status: done
estimate: 40m
depends_on: []
---

## Objective

Write the ADR that fixes the design before code: bex integrates GitHub via a **GitHub App** (not an OAuth app, not a pasted PAT), self-hosters mint their own app through GitHub's app-manifest flow, configuration is env-var-based with 503-when-unset, and connections live in the control-plane Postgres.

## Context

- Render's model (the parity target): install the Render GitHub App with all-repos or selected-repos grant → repo list in the create flow → private clones → signed push events (`render.com/docs/github`).
- Existing prior art to cite: [docs/ADR017-deploy-from-chat.md](../../../docs/ADR017-deploy-from-chat.md) (HMAC webhook, `autoDeploy`), [docs/ADR003-control-plane.md](../../../docs/ADR003-control-plane.md) (opt-in Postgres store), [docs/ADR012-auth.md](../../../docs/ADR012-auth.md) (secrets out-of-band).
- `w4/003` (GitHub social login via Kratos OIDC) is a *different* feature but shares the "who owns the GitHub app for self-hosters" question — the manifest-flow answer here unblocks it; cross-link.
- Known accepted limitation to record: installation tokens live 1h; an operator-side build retry >1h after its trigger finds an expired clone token and fails until the next deploy.

## Steps

1. Write `docs/ADR026-github-integration.md` as an ADR: context, decision (GitHub App + manifest flow + env config + CP-store `git_connections`), alternatives considered (OAuth app, PAT — rejected with reasons), consequences, verification.
2. Specify the env contract: `BEX_GITHUB_APP_ID`, `BEX_GITHUB_APP_PRIVATE_KEY` (out-of-band secret), `BEX_GITHUB_APP_SLUG` (install URL `github.com/apps/<slug>/installations/new`); note `BEX_GITHUB_WEBHOOK_SECRET` is introduced by w2/m9.
3. Specify the surfaces (REST/GraphQL/MCP/UI) and mark REST `GET /v1/repos` + MCP repo tools as bex supersets (Render has no public equivalent).
4. Add the doc to CLAUDE.md's docs index; add the new env vars to CLAUDE.md's env table and to `.env.example`/`.env.template` (names + comments, never values).

## Files

- `docs/ADR026-github-integration.md` (new)
- `CLAUDE.md` (docs index + env table)
- `.env.example`, `.env.template`

## Acceptance criteria

- [ ] ADR states the GitHub App decision with PAT/OAuth-app alternatives explicitly rejected
- [ ] Self-hoster path (manifest flow) documented end to end
- [ ] Env contract + 503-when-unset semantics specified; `.env.example`/`.env.template`/CLAUDE.md in sync
- [ ] 1h-token build-retry limitation recorded

## Out of scope

- GitLab/Bitbucket providers; PR previews; webhook secret plumbing (m9)
