GitHub App Marketplace + Frictionless Install¶
Status: Planning
Scope: dev-health-ops (backend) + dev-health-web (frontend)
Linear: CHAOS (parent + per-phase sub-issues)
Goal¶
Let users connect GitHub by installing the Dev Health GitHub App in one click
(and, eventually, installing it from the GitHub Marketplace) instead of manually
wiring GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY_PATH, and
GITHUB_APP_INSTALLATION_ID (see GitHub App auth).
Two distinct outcomes:
- Frictionless install — the actual "easier setup": user clicks Install, picks repos on GitHub, lands back connected. No env vars, no PEM juggling.
- Marketplace listing — makes the App discoverable and installable from the GitHub Marketplace, with plan/entitlement webhooks. Depends on (1).
Current state (verified by code audit)¶
The hard capability already exists. The missing pieces are glue + one gap fix.
| Capability | Status | Location |
|---|---|---|
| Mint installation tokens (RS256 JWT -> install token) | Exists | connectors/utils/github_app.py (create_github_app_jwt, GitHubAppTokenProvider) |
GitHubCredentials supports App auth (app_id/private_key/installation_id) |
Exists | credentials/types.py (is_app_auth) |
| Encrypted per-org credential storage | Exists | integration_credentials table, models/settings.py (IntegrationCredential) |
| Credential resolution (CLI > env > DB) | Exists | processors/sync.py (_resolve_github_sync_credentials), credentials/resolver.py |
Inbound webhook router w/ X-Hub-Signature-256 verify |
Exists | api/webhooks/router.py, api/webhooks/auth.py |
| Stripe webhook (entitlement pattern to mirror) | Exists | api/billing/router.py |
| Admin REST: credentials CRUD + test | Exists | api/admin/routers/credentials.py |
| Admin REST: sync-configs CRUD + trigger/backfill | Exists | api/admin/routers/sync.py |
| Sync config model (parent/child, targets, options) | Exists | models/settings.py (SyncConfiguration) |
| Tenancy: Organization / User / Membership | Exists | models/users.py |
| Web integrations UI (PAT form today) | Exists | web/src/app/(app)/admin/integrations/[provider]/ |
Web -> backend proxy (injects Authorization + X-Org-Id) |
Exists | web/src/proxy.ts |
| Web GitHub social login (OAuth, NextAuth v5) | Exists (different concern) | web/src/lib/auth.ts |
Gaps¶
- Background sync ignores App auth (bug, prerequisite).
workers/sync_runtime.py(~L508) anddiscovery/repos.pyextract only the"token"key and raiseValueError("Missing GitHub token ...")when absent. App-auth sync configs therefore fail in the Celery worker today. - No installation persistence. No
installation_id -> orgmapping table and no install callback. App auth today requires an admin to manually pasteapp_id+ PEM +installation_idinto anIntegrationCredential. - No install / Marketplace webhook events. The webhook router handles
push-style provider events, not
installationormarketplace_purchase. - Web has no "Connect GitHub App" path — only the PAT form and OAuth social
login (note:
web/docs/github-app-auth.mddocuments social login, which is a different mechanism from the ops installation-token sync auth).
Plan¶
Phase 0 — Fix worker App-auth gap (prerequisite, ops only)¶
Without this, none of the later phases produce working syncs.
- Make
workers/sync_runtime.pyanddiscovery/repos.pyresolve a fullGitHubCredentials(PAT or App) instead of grabbing the"token"key. ReuseGitHubCredentials.is_app_authand the existing resolver. - Add a worker test that runs a sync with an App-auth sync config.
Touches: src/dev_health_ops/workers/sync_runtime.py,
src/dev_health_ops/discovery/repos.py, tests/.
Phase 1 — Frictionless install capture (ops + web)¶
The actual "easier setup". Depends on Phase 0.
ops
- New table
github_app_installations:installation_id,account_login,account_type,org_id(FK),suspended_at, timestamps. New Alembic migration undersrc/dev_health_ops/alembic/versions/. - Extend
api/webhooks/router.pyto handle theinstallationevent (created/deleted/suspend/unsuspend), verified with the existingX-Hub-Signature-256dependency. - New endpoint
POST /api/v1/admin/integrations/github/install-callbackthat mapsinstallation_id-> org (via signedstate) and writes anIntegrationCredentialin App mode. The app-level private key (PEM) comes from a single server secret/env, not from the end user.
web
- "Connect GitHub" button ->
https://github.com/apps/<app-slug>/installations/new?state=<signed-org-token>. - New route handler
web/src/app/(app)/.../github-app/callback/route.tsthat receivesinstallation_id+setup_actionand forwards to the backend through the existingsrc/proxy.tspassthrough. - Surface App-install as an option in
admin/integrations/[provider]alongside the existing PAT form.
Result: user clicks Install -> selects repos on GitHub -> returns connected.
Phase 2 — Marketplace listing (ops + GitHub.com config) — VERIFY FIRST¶
The reference pass on current Marketplace-listing prerequisites was incomplete. Run a focused GitHub-docs pass and update this section before committing work.
- ops: extend the webhook router for
marketplace_purchase(purchased/changed/cancelled/pending_change) and map plan -> org entitlement, reusing the existing Stripe/billing entitlement patterns. - GitHub.com (non-code): make the App public, complete publisher verification, write listing copy, define pricing plans, add screenshots.
Open items to verify: free vs paid plan implications, publisher verification
requirements, whether the listing requires the App to be public, and the exact
current marketplace_purchase payload fields.
Phase 3 (optional) — Self-hosted App Manifest helper¶
For OSS / self-hosted operators: use the GitHub App Manifest flow
(POST /app-manifests/{code}/conversions, which returns id, pem,
webhook_secret, client_id/client_secret) so they one-click create their
own App rather than following the manual setup doc. Independent of Phases 1-2.
Dependencies¶
Phase 0 (worker fix) --+--> Phase 1 (install capture) --> Phase 2 (marketplace)
|
+--> (also unblocks any App-auth sync)
Phase 3 (manifest) -- independent
References¶
- Manual setup (today):
user-guide/github-app-auth.md - Token minting:
src/dev_health_ops/connectors/utils/github_app.py - Credential model:
src/dev_health_ops/credentials/types.py - Webhook router:
src/dev_health_ops/api/webhooks/router.py - Stripe entitlement pattern:
src/dev_health_ops/api/billing/router.py