Skip to main content

Connect GitHub

Hephaestus uses three GitHub mechanisms:

MechanismPurposeRequired?
OAuth App"Continue with GitHub"Yes, unless you use only GitLab login
PAT or GitHub AppRepository data access and feedback delivery per workspaceOne of the two
WebhookLive events: PRs, reviews, pushes, and othersStrongly recommended. Without it, updates wait for scheduled sync, hourly by default in production.

Configure login with the OAuth App setup.

Product feedback uses the instance’s GitHub App independently of workspace connections. Install it on the operator-named public and private repositories with Issues: read and write permission. The private repository must be private and have the same owner as the public one. Configure product feedback owns destination setup, legal steps, and recovery.

Choosing PAT vs GitHub App​

PAT mode fully supports synchronization and is the simplest way to evaluate a single-org installation. Before you enable feedback delivery, use a GitHub App so comments have a distinct app identity.

PATGitHub App
Setup effortPaste a token in the workspace UICreate an App. Install it. Manage a private key.
Environment variablesNoneGH_APP_ID, GH_APP_PRIVATE_KEY, GH_APP_INSTALLATION_URL, GH_APP_CLIENT_ID, GH_APP_CLIENT_SECRET
API rate-limit budgetShared with the user's other API usePer installation. See GitHub's limits.
Practice feedback posted back to GitHub (PR comments, inline notes, approvals)Uses the token's user identity. Do not use for active delivery.Uses the App identity
WebhooksCreate manually, as described belowConfigure once on the App

The database stores tokens encrypted with AES-256-GCM per workspace.

PAT mode​

A classic PAT needs the repo and read:org scopes. repo includes pull request and issue write access and the check state. A fine-grained PAT needs the organization as its resource owner and the permissions below. PAT connections need no GitHub App credentials. Authentication uses each workspace connection's mode.

  1. Create a token with the scopes or permissions for its type.
  2. If other workspaces use existing App configuration, keep it.
  3. When you create or configure the workspace in the UI, paste the token.
  4. Create a manual webhook.

Fine-grained token permissions​

These are the core permissions without Checks, which GitHub does not offer to fine-grained tokens.

Permission (manifest key)ScopeLevelWithout it
Contents (contents)RepositoryReadCommit sync stays empty. Practice reviews cannot check out the repository.
Pull requests (pull_requests)RepositoryRead & writePull request and review sync stays empty. Feedback delivery on pull requests fails.
Issues (issues)RepositoryRead & writeIssue sync stays empty. Feedback delivery as comments fails.
Commit statuses (statuses)RepositoryReadCommit statuses do not count toward a pull request's check state
Members (members)OrganizationReadOrganization membership and team sync stay empty
Issue types (issue_types)OrganizationReadIssue-type sync stays empty
Metadata (metadata)RepositoryReadAlways included
CAUTION: A missing permission does not fail the connection

GitHub accepts a fine-grained token without any of these permissions. Without Contents, Pull requests, Issues, Members, or Issue types, the corresponding sync stays empty, as the table describes. GitHub does not offer Checks to fine-grained tokens (community discussion). A token without it can be refused a private repository's check state. There, only check_suite and status webhook deliveries record the check state. Classic tokens and the GitHub App also read it with each sync.

GitHub App mode​

  1. Create the App at Settings → Developer settings → GitHub Apps → New GitHub App. For an organization, use https://github.com/organizations/<org>/settings/apps.
  2. Use external-app-icon-1024.png as its display image.
  3. Set Homepage URL to a page that describes this deployment. See brand identity and instance routing.
  4. Select Webhook → Active.
  5. Set Webhook URL to https://<APP_HOSTNAME>/webhooks/github.
  6. Set Secret to your .env value of WEBHOOK_SECRET, byte-identical.
  7. Set Where can this GitHub App be installed to Any account. A public personally owned app can be installed on organizations. Only on this account restricts it to its owner. Installation and repository selection remain required. The default workspace creation policy is ADMIN_ONLY. With it, an installation waits until an instance admin adopts it.
  8. Select Identifying and authorizing users → Request user authorization (OAuth) during installation.
  9. Set Callback URL to https://<APP_HOSTNAME>/api/oauth/callback/github. GitHub then returns to Hephaestus after installation and hides Setup URL.

Connecting an installation to a workspace​

The App requests user authorization during installation. When a workspace admin connects GitHub, Hephaestus exchanges GitHub's authorization for a short-lived token of the person who installed the App. It connects the installation only if that person can access it and owns its account. This means an organization owner or the user themselves. Hephaestus uses the token once and never stores it. GitHub documents that the return URL's installation_id can be forged.

Hephaestus never trusts that id alone. These conditions also apply:

  • One workspace per installation. A connected installation cannot connect to another workspace until the first workspace disconnects it. This applies even while the connection is suspended. A refusal names the other workspace only to its workspace admins.
  • Disconnect uninstalls only what the workspace holds. A disconnect removes the installation from GitHub only when the connection recorded it.
  • Installed from GitHub or the workspace wizard. The return has no workspace to connect. Hephaestus sends the person to the setup page of the installation. The person selects Continue, and Hephaestus checks the authorization. The person must have access to the installation. Their GitHub account must be the account that is linked to the signed-in Hephaestus account. The setup page never creates a workspace. It names an existing workspace only to its members and to instance admins. It also tells the person when an instance admin declined the installation. Who creates the workspace of an installation gives the next step. A return after an update of an installation goes to the home page.
  • Installation requests. A member without permission to install the App sends a request to the account's owners instead. After an owner approves it, connect again.

Who creates the workspace of an installation​

The Helm value config.workspaceCreationPolicy sets who creates workspaces. The chart gives it to the server role as HEPHAESTUS_WORKSPACE_CREATIONPOLICY.

ValueGitHub App installationsCreate workspace in Hephaestus
ADMIN_ONLY (default)A new installation waits until an instance admin adopts itInstance admins only
SELF_SERVICEEach new installation creates its own workspace, without approvalEvery signed-in user

Under ADMIN_ONLY, Hephaestus records each new installation without a workspace. The installation webhook, the startup reconciliation and the setup page record it. An installation that already has a workspace keeps it. An installation that allowed-organizations filters out is also recorded, because the filter limits only the sync.

Instance admins decide in Instance administration → GitHub installations:

  • Adopt reads the installation again from GitHub with the App credentials. Hephaestus refuses the adoption and changes nothing if GitHub no longer has the installation. It also refuses a suspended installation, and an installation that moved to a different account. Otherwise, Hephaestus creates the workspace with the adopting admin as its owner and starts the first sync.
  • Decline keeps the installation in the list. An instance admin can adopt a declined installation later.
  • To undo an adoption, delete the workspace. The installation stays adopted, and a later installation event does not create a new workspace for it. For a personal account, Hephaestus then removes the account's login and id from the record. To use the account again, uninstall the App on GitHub and install it again.

Adopt and decline require a recent sign-in. The audit log records each decision as GitHub installation adopted or GitHub installation declined, with the installation id. An uninstall on GitHub removes the record of an installation that no admin adopted.

Workspace attachment​

An existing GitHub installation stays bound to its workspace after an account rename. A new or reinstalled installation on an organization joins the workspace that already recorded the immutable GitHub identity of that organization. Hephaestus never finds that workspace by its account name, because several workspaces can share a name. An installation on a personal account never joins an existing workspace. A PAT connection with a stored token remains in PAT mode.

Personal-account workspaces cannot establish that match from an account name alone. The same applies to older organization workspaces that have not synchronized their organization identity. The installation gets a separate workspace with an available slug. The existing workspace and its access remain unchanged.

Before a migration from PAT mode:

  1. Synchronize the organization's identity.
  2. When ready to switch, remove the PAT credential.

A name match never transfers ownership or private conversations. If an old synchronized organization still holds a reused GitHub name, refresh its identity before you retry installation. Hephaestus rejects conflicting identities rather than merging them.

Permission model​

Core permissions support synchronization and feedback delivery. Provisioned reads precede named features to combine approval rounds. They do not enable those features. Held permissions remain ungranted until their release conditions are met.

Pre-provisioning trades broader access for fewer approval rounds. This differs from GitHub's default advice to request only necessary permissions and events. It applies only to permissions. A subscription follows its consumer, as the next section explains.

  1. Limit the repositories on which you install the App.
  2. Omit any provisioned read you prefer to approve when its feature arrives.
  3. Never pre-provision a write.

Every permission or event change must update this page in the same pull request. The tables, their manifest, and the consent-screen description must agree.

  1. Apply the configuration to each environment.
  2. Verify installation approvals.

The manifest template contains the same set in machine-readable form. It and the tables stay identical. The tables explain each grant.

Core contract — required​

“Write” in GitHub's manifest includes read access. All unlisted permissions remain No access.

Permission (manifest key)ScopeLevelWhy
Metadata (metadata)RepositoryReadMandatory repository identity and metadata
Contents (contents)RepositoryReadPush and commit sync. Repository checkout for practice reviews. No code writes.
Pull requests (pull_requests)RepositoryRead & writePR and review sync. Delivery of review feedback, inline notes, and approvals.
Issues (issues)RepositoryRead & writeIssue sync. Delivery of feedback as issue or PR comments.
Members (members)OrganizationReadOrganization membership, teams, and member events
Issue types (issue_types)OrganizationReadNative issue-type sync, and the issue-type change that opens a practice review
Checks (checks)RepositoryReadCheck state and check runs of a pull request's head. Read with the pull request and when a suite completes.
Commit statuses (statuses)RepositoryReadThe same state and runs from commit statuses, including non-Actions CI. status events keep them current.

Provisioned — read access ahead of features​

Permission (manifest key)ScopeLevelWhy / roadmap
Actions (actions)RepositoryReadWorkflow, run, and job signals. No workflow dispatch or cancellation: #1403, #1338
Deployments (deployments)RepositoryReadDelivery cadence, outcomes, and approval history: #1676
Merge queues (merge_queues)RepositoryReadQueue usage and ejection signals, never queue control: #1677
Dependabot alerts (vulnerability_alerts)RepositoryReadVulnerability-response times and dismissal practices: #1675
Secret scanning alerts (secret_scanning_alerts)RepositoryReadLeaked-credential response timing: #1675
Code scanning alerts (security_events)RepositoryReadStatic-analysis triage practices: #1675

A provisioned permission grants access, not a subscription. Its events stay unsubscribed until a feature reads them. Security-alert event payloads are sensitive even before a consumer exists. If you subscribe regardless:

  1. Restrict access to delivery logs and queue storage.
  2. Never copy a raw payload or secret value into application logs, support tickets, or practice context.

Held — do not grant​

The manifest grants none of these at the level shown or above.

Permission (manifest key)ScopeLevelRelease trigger / why it is held
Checks (checks)RepositoryRead & writeOnly with non-blocking annotation delivery (#1678). Requires an installation approval round. Read is core.
Administration (administration)RepositoryReadHeld until GitHub offers a narrower branch-protection grant. Broad administration access is not justified for that signal.
Issue fields (issue_fields)OrganizationReadCustom issue fields have no reader. Issue-type sync uses Issue types above. Revoke it where an existing registration grants it.
Copilot metrics (organization_copilot_metrics)OrganizationReadPrivacy assessment first (#1679). Provision only if approved.

Event subscriptions​

Subscribe only to the events below. This is the complete set Hephaestus consumes. GitHub's event reference lists each webhook type's permissions and availability.

A subscription without a consumer still has a cost. The receiver stores every delivery in its queue before a consumer check. The bounded queue removes the oldest delivery first. A high-volume event family can remove pull-request and review deliveries that the queue needs to hold. For example, workflow_job alone can outnumber pull_request a hundredfold.

Those deliveries then wait for hourly production reconciliation to fetch them again. See webhook ingestion operations. Provision the permission now, because permission increases need an approval round. Add the subscription in the release with the feature that reads it.

EventRationale
pushRefresh commits and repository activity.
pull_requestSynchronize PR lifecycle and changes.
pull_request_reviewSynchronize submitted and updated reviews.
pull_request_review_commentSynchronize inline review comments.
pull_request_review_threadTrack review-thread resolution, and when it happened.
check_suiteKeep the head's check state and runs current. Start reviews when checks finish.
statusThe same for commit statuses.
issuesSynchronize issue lifecycle and content.
issue_commentSynchronize issue and PR conversation comments.
labelKeep label metadata current.
milestoneKeep milestone metadata current.
memberTrack repository collaborator changes.
repositoryTrack repository metadata and lifecycle.
sub_issuesUpdate parent/child issue relationships.
issue_dependenciesUpdate blocking/blocked-by relationships.
organizationTrack organization changes.
teamSynchronize teams.
membershipSynchronize team membership.

GitHub delivers installation and installation_repositories automatically. Do not add them to the manifest's default_events. The server also handles account renames through installation_target when delivered.

Deliberately not subscribed​

Hephaestus subscribes to no GitHub event absent from the table above. These include three groups:

Not subscribedWhy
The provisioned families — check_run, workflow_run, workflow_job, deployment, deployment_status, deployment_review, merge_group, merge_queue_entry, release, commit_comment, create, delete, dependabot_alert, secret_scanning_alert, code_scanning_alertTheir permissions are provisioned. Their consumers do not yet exist. Subscribe with the feature, not before it. check_run is not needed: the suite event reports its runs' conclusion.
deployment_protection_rule, exemption_request_push_rulesetEach requests an approval decision from Hephaestus. Hephaestus observes work and never gates it. exemption_request_push_ruleset also needs Administration read, which is held, so it stays unsubscribed.
deploy_key, team_addAccess management, not practice context. Team and membership sync use the events above.

GitHub App manifest template​

This reference uses GitHub's native manifest format. Hephaestus does not implement GitHub's manifest registration flow.

  1. Replace the app name for each environment.
  2. Replace <APP_HOSTNAME> for each environment.
  3. Unless you have tools for that registration flow, use the manual setup above.
{
"name": "your-hephaestus",
"url": "https://<APP_HOSTNAME>",
"description": "Reads repository and organization data, including CI, deployment, merge-queue and security-alert signals. Can write pull-request reviews and issue/PR comments for practice feedback. Never modifies your code.",
"public": true,
"hook_attributes": {
"url": "https://<APP_HOSTNAME>/webhooks/github",
"active": true
},
"callback_urls": ["https://<APP_HOSTNAME>/api/oauth/callback/github"],
"request_oauth_on_install": true,
"default_permissions": {
"metadata": "read",
"contents": "read",
"pull_requests": "write",
"issues": "write",
"members": "read",
"issue_types": "read",
"checks": "read",
"statuses": "read",
"actions": "read",
"deployments": "read",
"merge_queues": "read",
"vulnerability_alerts": "read",
"secret_scanning_alerts": "read",
"security_events": "read"
},
"default_events": [
"push",
"pull_request",
"pull_request_review",
"pull_request_review_comment",
"pull_request_review_thread",
"check_suite",
"status",
"issues",
"issue_comment",
"label",
"milestone",
"member",
"repository",
"sub_issues",
"issue_dependencies",
"organization",
"team",
"membership"
]
}

The GitHub App reads repositories with installation tokens. Its callback URL and client credentials only confirm who installed it. Login uses a separate OAuth App. Only the OAuth App's credentials belong in GITHUB_OAUTH_CLIENT_ID / GITHUB_OAUTH_CLIENT_SECRET. Only the GitHub App's credentials belong in GH_APP_CLIENT_ID / GH_APP_CLIENT_SECRET.

Install and configure​

CAUTION: Preserve the existing webhook secret

On an existing instance, replacing WEBHOOK_SECRET breaks other configured webhooks. Set the new App's webhook secret to the existing WEBHOOK_SECRET. On a new instance, the manifest-generated secret can become WEBHOOK_SECRET.

  1. Generate a private key under App settings → Private keys.
  2. Download the .pem. Alternatively, use the key from manifest registration.
  3. Install the App on your organization, for all repositories or only those to monitor.
  4. Set the GitHub App ID, installation URL and client ID through the chart integration values.
  5. Store the private key and client secret in the release Secret, with the key names that the chart requires. Preserve the PEM file's newlines. Do not put either secret in site.yaml.
  6. Upgrade the verified chart with your site values.

GitHub webhooks — manual setup (PAT mode)​

PAT connections require a manual webhook for live updates. Prefer one organization webhook. Repository webhooks cannot deliver organization, team, or membership. App-only events are unavailable here. A subscription grants the PAT no API access.

  1. Open https://github.com/organizations/<org>/settings/hooks.
  2. Select Add webhook.
  3. Set Payload URL to https://<APP_HOSTNAME>/webhooks/github.
  4. Set Content type to application/json.
  5. Set Secret to your WEBHOOK_SECRET. The receiver verifies it as X-Hub-Signature-256 HMAC.
  6. Under Events, select "Let me select individual events".
  7. Select the listed events that GitHub offers for this webhook type.

Operational mechanics​

Permission changes and approval rounds​

Permission increases take effect per installation, after its account owner approves them under organization → Settings → GitHub Apps. Until approval, that installation keeps its old grants. New installations approve the current set during installation. Reductions and event changes apply immediately without approval. A new subscription cannot bypass a permission increase that awaits approval.

  1. Combine permission increases into approval rounds.
  2. Before you enable dependent features, verify each installation's approval.
  3. After a permission reduction, recheck event availability.

An App from an earlier manifest needs manual updates under Permissions & events:

  1. Subscribe to check_suite and status. These need no approval round.
  2. If the App predates them, grant Checks and Commit statuses read. These need an approval round per installation.

Until an installation grants them, the head's check state reaches Hephaestus only through scheduled sync, which reads it with the pull request.

A homepage URL identifies the deployment. Any page that describes it is suitable. GitHub never delivers anything there. The webhook URL and every OAuth callback must use the instance hostname.

Use the exact callback procedure for login providers. GitHub deliveries do not follow redirects. A redirect loses every event even if the URL works in a browser.

  1. Keep TLS verification enabled.
  2. Route /webhooks/github to the server.

Each environment needs its own GitHub App because an App has one webhook URL.

  1. Use separate keys and secrets for each environment.
  2. Use a -staging name suffix for staging.
  3. Register a separate login OAuth App per environment, with that instance's callback and credentials.

Use the manifest description for consent text. Disclose reads and writes, including provisioned security-alert access. The exact claim “Never modifies your code” refers to Contents read, not an absence of review or comment writes. Describe provisioned signals as access, not available features.

Ownership transfer​

Installations normally stay in place after a transfer. The app ID, client credentials, and private keys stay unchanged. GitHub warns when a transfer would uninstall the app.

  1. Before transfer, check Where can this app be installed → Any account.
  2. Use the app registration's Advanced → Transfer ownership, not deletion and recreation.
  3. Before you proceed, review any warning.
  4. After transfer, check Where can this app be installed → Any account again.
  5. Verify installations, repository selections, and delivery.
  6. Review app managers and access to the secret store.
  7. If the previous owner must lose access, rotate keys and secrets separately.

Transfer does not revoke credential copies.

Failed deliveries and redelivery​

For a GitHub App:

  1. Open its registration, not the organization's installation page.
  2. Open Advanced → Recent deliveries.
  3. Select a delivery GUID to inspect its request and response.
  4. Correct the cause.
  5. Select Redeliver.

For a manual webhook, use Settings → Webhooks → webhook → Recent Deliveries in the organization or repository instead. Hephaestus redelivers failed GitHub App deliveries of the past two hours by itself. Webhook ingestion operations describes the limits. Do not rely on scheduled polling to recover event-only history, such as CI state at merge or security-response timestamps.

SymptomCheck / action
No delivery entryConfirm installation, repository selection, subscription, and accepted permissions. Confirm event availability for this webhook type. Check GitHub Status.
3xxReplace the webhook URL that redirects with the direct instance URL.
401 on a real eventCheck that the app or manual webhook secret matches WEBHOOK_SECRET. Proxies must preserve the body and signature header.
Timeout, TLS error, or 404Check public DNS, certificate chain, reverse-proxy routing, and server health.
2xx, but no application updateCheck server consumption and workspace repository selection. Check the repository's sync error under Administration → Integrations → GitHub → Manage repositories. Events without a feature consumer are acknowledged and discarded without updates.

Follow GitHub's delivery troubleshooting guide.

Check that it worked​

CAUTION: Use a dedicated App for the secret test

The wrong-secret test changes GitHub's secret and rejects real events. Run it only on a dedicated test App, never a shared staging App. Leave the receiver's WEBHOOK_SECRET unchanged.

  1. Compare each environment's registration with the permission tables, including held grants.

  2. Confirm each installation's approved permissions.

  3. Remove grants or subscriptions absent from the tables.

  4. Open a test PR in a monitored repository.

  5. Update it.

  6. Verify 2xx delivery.

  7. Verify the updated state in the correct workspace without a wait for scheduled sync.

    The initial ping returns 200 even with a wrong secret. It tests reachability, not authentication. For failures, use delivery triage and:

    kubectl -n hephaestus logs -f deployment/hephaestus-server
  8. On the dedicated test App, change the secret in GitHub's App settings only.

  9. Trigger a new real event.

  10. Confirm rejection.

  11. Restore the matching secret.

  12. Select Redeliver for the rejected event.

  13. Verify acceptance and application state.

  14. Sign in through the instance hostname.

  15. Verify that the OAuth callback returns to that instance, not the product homepage or another environment.