Skip to main content

GitHub Integration

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.

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 repo, read:org, and read:project 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.
Discussions (discussions)RepositoryReadDiscussion sync stays empty
Commit statuses (statuses)RepositoryReadCommit statuses do not count toward a pull request's check state
Members (members)OrganizationReadOrganization membership and team sync stay empty
Projects (organization_projects)OrganizationReadProjects v2 sync stays 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, Discussions, Members, Projects, 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.
  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 its home page. The installation webhook creates or updates the workspace as before.
  • 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.

Workspace attachment​

An existing GitHub installation stays bound to its workspace after an account rename. A new or reinstalled installation can reuse a matching-name workspace only if that workspace already recorded the same immutable GitHub organization identity. 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.
Discussions (discussions)RepositoryReadDiscussion sync, not discussion delivery
Members (members)OrganizationReadOrganization membership, teams, and member events
Projects (organization_projects)OrganizationReadProjects v2 sync and project event access, not classic repository projects.
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 webhook role 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.
discussionSynchronize discussions.
discussion_commentSynchronize discussion comments.
sub_issuesUpdate parent/child issue relationships.
issue_dependenciesUpdate blocking/blocked-by relationships.
organizationTrack organization changes.
teamSynchronize teams.
membershipSynchronize team membership.
projects_v2Synchronize organization project metadata where delivered.
projects_v2_itemSynchronize project items and field values where delivered.
projects_v2_status_updateSynchronize project status updates where delivered.

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.

Projects v2: GitHub's event reference lists organization-webhook availability for the three project events. Discussions and Projects sync are off by default (HEPHAESTUS_SYNC_DISCUSSIONS_ENABLED=false and HEPHAESTUS_SYNC_PROJECTS_ENABLED=false).

  1. Change a project item.
  2. Verify that the App delivers the projects_v2_item event.
  3. If App delivery is unavailable, add an organization webhook subscribed only to those three events.

Projects read remains required for API synchronization.

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",
"discussions": "read",
"members": "read",
"organization_projects": "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",
"discussion",
"discussion_comment",
"sub_issues",
"issue_dependencies",
"organization",
"team",
"membership",
"projects_v2",
"projects_v2_item",
"projects_v2_status_update"
]
}

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 GH_OAUTH_CLIENT_ID / GH_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 these values in .env:

    GH_APP_ID=123456
    # Multi-line PEM: quote the whole value; Docker Compose .env preserves the newlines.
    GH_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
    ...
    -----END RSA PRIVATE KEY-----"
    # The workspace-creation wizard sends operators to this URL to install the App.
    GH_APP_INSTALLATION_URL=https://github.com/apps/<your-app-slug>/installations/new
    # App settings → General → Client ID; generate a client secret there too.
    GH_APP_CLIENT_ID=Iv23...
    GH_APP_CLIENT_SECRET=...
  5. Restart with the new environment:

    docker compose --env-file .env --env-file release-lock.env up -d

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. Include the three projects_v2* events for organization project boards.

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 webhook runtime role.

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 webhook-role health.
5xxInspect webhook-role and NATS health (webhook ingestion operations). Repair the failure before redelivery.
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.

Verify it works​

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:

    docker compose --env-file .env --env-file release-lock.env logs -f webhook-server
  8. For Projects v2, change a project item.

  9. Verify delivery and synchronization.

  10. Only if App delivery is unavailable, add the narrowly scoped organization webhook.

  11. On the dedicated test App, change the secret in GitHub's App settings only.

  12. Trigger a new real event.

  13. Confirm rejection.

  14. Restore the matching secret.

  15. Select Redeliver for the rejected event.

  16. Verify acceptance and application state.

  17. Sign in through the instance hostname.

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