Skip to content

Focus: GitHub App Integration#13

Draft
pavish wants to merge 26 commits intomainfrom
focus/github-app-integration
Draft

Focus: GitHub App Integration#13
pavish wants to merge 26 commits intomainfrom
focus/github-app-integration

Conversation

@pavish
Copy link
Copy Markdown
Owner

@pavish pavish commented Feb 21, 2026

Focus: GitHub App Integration

Created: 2026-02-21 15:00
Status: active
Branch: focus/github-app-integration
PR: #13

What We're Building

The GitHub integration layer for Barae. After the previous focus (GitHub App OAuth login), users can authenticate via GitHub. Now we're building the foundation that lets Barae actually work with GitHub repos: installing the GitHub App on user accounts and organizations, managing which repos Barae can access, creating new repos, and performing git operations (clone, fetch, push).

This is the bridge between "user has a Barae account with GitHub login" and "user manages their sites through Barae." Everything in future milestones (sites, templates, content management) depends on this integration layer.

Key data model: One Barae user maps 1-1 to one GitHub account. One GitHub account can access multiple app installations (personal + orgs they belong to). Barae must surface ALL installations the user's GitHub account has access to — not just ones they installed themselves.

User Flows

Flow 1: Install the Barae GitHub App (user has linked GitHub account)

  1. Authenticated user with a linked GitHub account opens the GitHub integration page
  2. They see a prompt to install the Barae GitHub App (or a list of existing installations)
  3. User clicks "Install on GitHub" — redirected to GitHub's installation page
  4. User selects an account (personal or organization) and chooses repos (all or specific)
  5. GitHub redirects back to Barae with the installation ID (and OAuth code, which is redundant since account is already linked)
  6. Barae verifies the installation using the user's existing GitHub token, stores it, and syncs the repo list
  7. User sees their installation with accessible repos listed

Flow 1b: Install the Barae GitHub App (email-only user, no GitHub linked)

  1. Authenticated user who signed up via email/password (no linked GitHub account) opens the GitHub integration page
  2. They see a prompt to install the Barae GitHub App
  3. User clicks "Install on GitHub" — redirected to GitHub's installation page
  4. GitHub shows both: app installation permissions AND OAuth authorization (enabled via "Request user authorization during installation" in GitHub App settings)
  5. User installs the app and authorizes OAuth in one step
  6. GitHub redirects back to Barae's installation callback with both installation_id and OAuth code
  7. Barae exchanges the OAuth code for a user access token, linking the GitHub account to their existing Barae account
  8. Barae then verifies and stores the installation, syncs the repo list
  9. User now has both: a linked GitHub account (can log in via GitHub) AND the app installed

Flow 2: Discover Existing Installations

  1. User who already installed the GitHub App outside Barae opens the integration page
  2. Barae fetches the user's installations from GitHub API
  3. Any installations not yet tracked in Barae are discovered and stored
  4. User sees all their installations and repos

Flow 3: Sync Repo Changes

  1. User modifies repo selection on GitHub (adds/removes repos from the installation)
  2. Back in Barae, they see possibly stale repo list
  3. User clicks "Refresh" or the page auto-syncs on visit
  4. Barae fetches the latest repo list from GitHub and updates the database
  5. New repos appear, removed repos are marked accordingly

Flow 4: Create a New Repository

  1. User wants to create a new site — clicks "Create Repository"
  2. Selects the target account (personal or an org where the app is installed)
  3. Enters repo name and settings
  4. Barae creates the repo via GitHub API
  5. The new repo appears in the installation's repo list

Flow 5: Git Operations (Foundation)

  1. Barae needs to read from or write to a connected repo (for future content management)
  2. The system generates an installation access token on-demand
  3. Clones/fetches the repo, performs operations, commits and pushes
  4. Tokens are short-lived (1 hour) and never stored persistently

Flow 6: Handle Errors and Edge Cases

  1. Installation is suspended or removed on GitHub → clear error message, option to re-install
  2. GitHub API rate limit hit → user-friendly message with retry timing
  3. Token expired → auto-refresh (handled by libraries)
  4. Network failure → retry guidance
  5. Spoofed installation callback → verify via user's GitHub API before trusting

Success Criteria

  • Users can install the Barae GitHub App from the dashboard (redirect to GitHub)
  • Barae handles the installation callback (including OAuth code exchange for email-only users)
  • Email-only users who install the app also get their GitHub account linked automatically
  • Barae discovers existing installations via the GitHub API
  • Installation metadata (account, permissions, repo selection) is stored in the database
  • Repo lists per installation are stored and synced on demand
  • Both personal account and organization installations are supported
  • Users can refresh their installations and repo lists
  • Barae can create new repos in user accounts and organizations
  • Barae can clone, fetch, and push to connected repos via git
  • Installation access tokens are generated on-demand (not stored in DB)
  • GitHub App private key and App ID are configurable via environment variables
  • Suspended/removed installations are handled gracefully with clear messaging
  • All error states have user-facing messages and recovery paths
  • GitHub App features are disabled when credentials are not configured

Scope

In Scope

  • GitHub App installation callback handling (Setup URL redirect with combined OAuth + installation)
  • "Request user authorization during installation" enabled — email-only users get GitHub linked during install
  • Installation discovery via GET /user/installations
  • Installation metadata storage in PostgreSQL (schema, migration)
  • Repo list sync per installation
  • Installation access token generation on-demand (JWT + private key)
  • GitHub service as a Fastify plugin (Octokit App instance)
  • Config env vars: GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY (base64-encoded)
  • Repo creation via GitHub API (POST /user/repos, POST /orgs/{org}/repos)
  • Git operations: clone, fetch, push via simple-git (git CLI wrapper — reliable for image-heavy repos)
  • Frontend: installation management UI (list installations, repos, sync, install button)
  • Frontend: installation callback page
  • Error handling for all GitHub API failure modes (rate limits, suspended, removed, auth failures)
  • Conditional feature availability (disabled when App credentials not configured)

NOT in Scope (do not build)

  • Webhooks for installation events (planned for a future focus)
  • Real-time sync or polling strategy
  • Content management (reading/writing markdown files in repos)
  • Astro project scaffolding or templates
  • GitHub Actions or GitHub Pages configuration
  • Modifying installation permissions from within Barae (users manage this on GitHub)
  • Multi-user collaboration on shared installations
  • Site creation or management (repos are tracked, but "sites" come later)

Pitfalls & Anti-Patterns

  • Trusting the installation callback blindly: The installation_id query parameter from GitHub's redirect can be spoofed. Always verify by calling GET /user/installations with the user's OAuth token before storing.
  • Storing installation access tokens in DB: They expire in 1 hour. Let Octokit's built-in caching handle them in memory — generate on-demand.
  • Using JWT directly for API calls: JWTs are only for generating installation tokens. Never call repo endpoints with a JWT — use installation access tokens.
  • Mixing auth methods: User tokens for discovery (list installations/repos), installation tokens for repo operations (git, create repos), JWT only for generating installation tokens.
  • Logging tokens or private keys: Never include auth credentials in log output or error messages.
  • Multiline private key in env vars: Base64-encode the PEM key for env var storage — raw multiline PEM breaks across shells and Docker.
  • Forgetting "Administration" permission: Without "Repository > Administration: Read & Write" on the GitHub App, repo creation will fail. Document this in setup guide.
  • Git binary dependency: simple-git requires the git binary in Docker. Ensure git is installed in the Docker image (apk add git for Alpine, apt-get install git for Debian). This is a one-line Dockerfile change.
  • Blocking on git operations: Clone and push can take seconds. Always use async patterns and consider progress indicators for the user.
  • Ignoring the combined install+auth flow: When "Request user authorization during installation" is enabled, the callback receives both code and installation_id. Must handle both — especially for email-only users who need their GitHub account linked.
  • Ignoring suspended installations: Without webhooks, stored installations can become stale. Always handle 403 (suspended) and 404 (removed) gracefully on API calls.

Constraints for Claude

  • DO NOT use process.env for new config — add GITHUB_APP_ID and GITHUB_APP_PRIVATE_KEY to the TypeBox config schema, access via fastify.config
  • DO NOT use frontend env variables — all GitHub data comes from backend API endpoints
  • DO NOT store installation access tokens in the database — generate on-demand via Octokit
  • DO NOT trust the installation_id from callback URLs without verifying via the user's GitHub API
  • DO NOT build webhook handling — this focus uses on-demand sync only
  • DO NOT build content management or site features — only track installations and repos
  • ALWAYS handle all GitHub API error states (rate limit, suspended, removed, auth failure)
  • ALWAYS use dangerouslyDisableSandbox: true for gh CLI and git remote commands
  • ALWAYS base64-encode the GitHub App private key for env var storage
  • ALWAYS handle the combined install+auth callback — exchange OAuth code if present, then process installation
  • FOLLOW existing patterns: Fastify plugin pattern, Drizzle schema pattern, frontend API pattern (see research doc)

Tasks

  • 42658a-github-config — Add GitHub App config env vars (GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY)
  • 85382f-github-schema — Create database schema and migration for installations and repos
  • b61681-github-service — Create GitHub service Fastify plugin with Octokit App instance
  • 22673e-install-callback — Handle installation callback (backend route + frontend page)
  • 978668-install-discovery — Build installation discovery and sync API routes
  • bc66cc-repo-creation — Add repository creation API route
  • c194e9-git-operations — Build git operations service with simple-git
  • 10fdfe-frontend-installs — Build GitHub installations management UI
  • b8aa34-frontend-repo-create — Build repository creation UI

Add GITHUB_APP_PRIVATE_KEY to the Fastify config TypeBox schema with
an empty string default. The base64-encoded env var is decoded in-place
on process.env before @fastify/env loads, so fastify.config receives
the raw PEM string. GITHUB_APP_ID is not needed — GitHub recommends
using the Client ID (already configured) as the JWT issuer.
Add GitHub installation database schema and migration
Create Fastify plugin at backend/src/github/index.ts that initializes
an Octokit App instance using GITHUB_CLIENT_ID and GITHUB_APP_PRIVATE_KEY.
Credentials are mandatory — app throws at startup if missing.

Exposes GitHubService interface with getInstallationOctokit() and
createUserOctokit() methods. Registered in app.ts after auth plugin.
Move credential validation from the GitHub plugin to the config schema
using minLength: 1 on GITHUB_CLIENT_ID and GITHUB_APP_PRIVATE_KEY.
@fastify/env now rejects missing or empty values at startup.
Add GitHub service Fastify plugin with Octokit App
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant