If you’re running your own Pipefort deployment (self-hosted, or a fork), you need
your own GitHub App before users can sign in and connect repositories. This
page is for operators. End users who are just installing the hosted Pipefort App
should read GitHub setup instead.
The App is the credential Pipefort uses to mint per-installation tokens — see
Architecture for the auth split between Supabase Auth (login)
and the GitHub App (repo access).
What you need
- A GitHub account or organization that will own the App.
- The public URL of your Pipefort deployment (e.g.
https://pipefort.example.com).
This is your APP_BASE_URL env var.
- 5 minutes.
1. Pick where to register the App
| Owner | Use when | URL |
|---|
| Personal account | Solo, self-host, internal trials | https://github.com/settings/apps/new |
| Organization | Team or company deployment | https://github.com/organizations/YOUR-ORG/settings/apps/new |
Org-owned Apps survive maintainer departures and can be transferred between
orgs. Prefer org ownership for anything production-bound.
2. Register the App
Pipefort ships a manifest at deploy/github-app-manifest.json
that fixes the name, description, and the exact read-only permission set. Use
it as the source of truth — either by pasting the values into the GitHub form,
or by submitting the manifest programmatically.
Open the URL from step 1 and fill the form:| Field | Value |
|---|
| GitHub App name | Pipefort (or Pipefort — Acme if the name is taken) |
| Homepage URL | https://pipefort.example.com |
| Callback URL | (leave blank — Pipefort uses Supabase Auth, not GitHub OAuth, for sign-in) |
| Setup URL (post installation) | https://pipefort.example.com/connect/callback |
| Redirect on update | ✅ enabled |
| Webhook → Active | ❌ unchecked |
| Where can this GitHub App be installed? | Any account (required to publish; pick Only on this account while testing) |
Then set the Repository permissions below (everything else stays No access):| Permission | Access | Why |
|---|
| Metadata | Read-only | Mandatory for every App. |
| Contents | Read and write | Read .github/workflows/*.yml. Write enables the Open fix PR button on workflow-rule findings (commits the rewritten YAML to a new branch). |
| Workflows | Read and write | Also required by Open fix PR: GitHub blocks commits to .github/workflows/* unless the App holds this scope on top of Contents — without it the commit fails with 403 Resource not accessible by integration. |
| Pull requests | Read and write | Required by the Open fix PR button to open and reuse the pull request. |
| Administration | Read and write | Read branch protection (CICD-SEC-1 BP-* rules). Write enables the Auto-fix button for branch protection, secret scanning, and Dependabot remediation. |
| Actions | Read and write | Workflow permissions, allowed-actions policy (CICD-SEC-4, CICD-SEC-5). Write enables the Auto-fix button for WPERM-* remediation. |
| Dependabot alerts | Read-only | Detect alert state (CICD-SEC-3). |
| Secret scanning alerts | Read-only | Detect secret scanning + push protection (CICD-SEC-6). |
If you’d rather not grant write on any of these, set the corresponding row
to Read-only. Scanning still works; the per-finding Auto-fix / Open fix PR
buttons just stay hidden for any rule whose remediation needs the missing
scope. See GitHub App permissions. Click Create GitHub App. GitHub can register the App from a manifest in one round-trip. This is
convenient but requires you to grab the credentials immediately after
creation (the manifest exchange code expires in 1 hour).Save this HTML locally, replace APP_BASE_URL, and open it in a browser:<form action="https://github.com/settings/apps/new" method="post">
<input type="hidden" name="manifest" value='{
"name": "Pipefort",
"url": "APP_BASE_URL",
"redirect_url": "APP_BASE_URL/connect/callback",
"setup_url": "APP_BASE_URL/connect/callback",
"public": true,
"default_permissions": {
"metadata": "read",
"contents": "write",
"workflows": "write",
"pull_requests": "write",
"administration": "write",
"actions": "write",
"vulnerability_alerts": "read",
"secret_scanning_alerts": "read"
},
"default_events": [],
"hook_attributes": { "active": false }
}' />
<button type="submit">Create Pipefort GitHub App</button>
</form>
To register under an organization, change the form action to
https://github.com/organizations/YOUR-ORG/settings/apps/new.After you approve in GitHub, the App appears in your settings. Continue
with step 3 to collect credentials.
3. Collect credentials
On the new App’s settings page, capture five values and stash them in your
deployment’s secret manager:
| GitHub field | Env var |
|---|
| App ID | GITHUB_APP_ID |
| Client ID | GITHUB_APP_CLIENT_ID |
| Client secrets → Generate a new client secret | GITHUB_APP_CLIENT_SECRET |
| Private keys → Generate a private key | GITHUB_APP_PRIVATE_KEY (the full PEM, newlines preserved) |
App’s public slug in the page URL — https://github.com/apps/<slug> | VITE_GITHUB_APP_SLUG (frontend build env) |
The private key is downloaded once. If you lose it, generate a new one and
rotate GITHUB_APP_PRIVATE_KEY — GitHub keeps both valid until you delete the
old one.
Apply the env vars and redeploy:
# API (Go)
export GITHUB_APP_ID=123456
export GITHUB_APP_PRIVATE_KEY="$(cat pipefort.YYYY-MM-DD.private-key.pem)"
export GITHUB_APP_CLIENT_ID=Iv1.xxxxxxxxxxxxxxxx
export GITHUB_APP_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
export APP_BASE_URL=https://pipefort.example.com
# Web (Vite — set at build time)
export VITE_GITHUB_APP_SLUG=pipefort
The Go API tolerates these being unset at boot (HasGitHubApp is checked per
request), so you can deploy first and add the secrets second.
The Go API must also have SUPABASE_URL set to your Supabase project URL
(e.g. https://xxxx.supabase.co). Current Supabase projects sign user tokens
with an asymmetric ES256 key, which the API verifies via that project’s JWKS
endpoint — without the URL it can’t fetch the keys and every authenticated
request fails with 401 invalid token (the dashboard appears not to detect your
installation, and /connect/callback shows “Connection failed”). The frontend
VITE_SUPABASE_URL is a separate build-time var; if only it is set, the API
falls back to it, but setting SUPABASE_URL explicitly on the API is the
canonical configuration. (SUPABASE_PUBLIC_URL, which the local Supabase CLI
exports, also works.)
4. Publish the App
If you want anyone — not just your account — to install it:
- Open the App’s settings → Public page tab.
- Fill in Description, Avatar, and Public link.
- Toggle Make public. GitHub validates the App is installable on any
account before allowing this.
The public install URL becomes https://github.com/apps/<your-slug>. This is
the link the /connect page sends users to.
5. Verify
- Sign in to your Pipefort deployment with GitHub.
- The dashboard renders with an Install the Pipefort GitHub App banner.
- Click it → install the App on a repo → GitHub redirects to
/connect/callback.
- The dashboard reloads with the connected installation and the scan controls
become available.
If the redirect lands on a 404, the Setup URL in step 2 is wrong — fix it
in the App’s settings and reinstall.
Maintenance
When Pipefort adds a new permission (e.g. a new scanner rule requires a new
read scope), update the manifest in your deployment to match
deploy/github-app-manifest.json,
then update the App’s permissions in GitHub. Existing installations must
re-authorize — see
GitHub App permissions → Re-authorizing.