Setting up the GitHub App

Gradient's GitHub integration uses a single GitHub App that is registered once per Gradient instance. This guide walks an operator through the manifest flow that registers the App and produces the credentials Gradient needs.

Prerequisites

  • A Gradient account with the superuser flag.
  • Admin rights on the GitHub user or organization that will own the App.
  • An externally reachable HTTPS URL for your Gradient server (GRADIENT_SERVE_URL).
  1. Sign in to Gradient as a superuser.
  2. Navigate to /admin/github-app.
  3. (Enterprise only) Enter the GitHub Enterprise host, e.g. ghe.example.com. Leave blank for github.com.
  4. Click Create on GitHub. Your browser is redirected to GitHub with the manifest pre-filled.
  5. On GitHub, review the App name, permissions, and events; you can edit the name. Click Create GitHub App.
  6. GitHub redirects you back to /admin/github-app?ready=1.
  7. Copy each credential block:
  8. App ID - set as GRADIENT_GITHUB_APP_ID.
  9. Private key (PEM) - write to a file and set the path as GRADIENT_GITHUB_APP_PRIVATE_KEY_FILE (e.g. /run/secrets/gradient-github-app.pem).
  10. Webhook secret - write to a file and set the path as GRADIENT_GITHUB_APP_WEBHOOK_SECRET_FILE.
  11. Restart Gradient. The GitHub App toggle now appears on org Integration pages.

The credentials are returned once. If you navigate away before copying, re-run the manifest flow to generate a fresh App.

Manual flow

If the manifest flow doesn't suit your environment, register the App by hand following GitHub's docs. Match the values Gradient expects:

Setting Value
Webhook URL {serveUrl}/api/v1/hooks/github
Setup URL {serveUrl}/admin/github-app (optional)
Permissions metadata: read, contents: read, pull_requests: write, issues: write, statuses: write, checks: write
Events push, pull_request, release, check_run, issue_comment, pull_request_review (installation and installation_repositories are delivered automatically and are not selectable)

The issues: write permission is what gates issue_comment delivery - GitHub routes every comment on a PR's main conversation tab through that event, so without the permission Gradient never sees /gradient run or /gradient approve triggers posted there. write is required because the post_pr_comment reporter uses the same /repos/{owner}/{repo}/issues/{n}/comments endpoint to reply with build status, and it is also what allows Gradient to leave reactions on the originating comment (👀 on receipt, 👍 / 👎 on terminal eval status, 😕 when a non-maintainer issues a command).

pull_request_review lets a maintainer clear a fork-PR approval gate simply by submitting an approving review - Gradient verifies the reviewer is a repo writer and then releases the parked run, no /gradient approve comment needed.

Then download the private key, generate a webhook secret, and configure the env vars as below.

Required configuration

Env var Nix option Description
GRADIENT_GITHUB_APP_ID services.gradient.githubApp.appId Numeric App ID
GRADIENT_GITHUB_APP_PRIVATE_KEY_FILE services.gradient.githubApp.privateKeyFile Path to the PEM file
GRADIENT_GITHUB_APP_WEBHOOK_SECRET_FILE services.gradient.githubApp.webhookSecretFile Path to the webhook secret file

The nix module also exposes services.gradient.githubApp.enable as the master switch - set it to true to wire the credentials into the systemd unit.

Troubleshooting

Symptom Cause / fix
400 manifest state invalid or expired The CSRF state token is older than 10 minutes; re-start the manifest flow.
404 Pending credentials on /admin/github-app?ready=1 The temporary in-memory store dropped them (server restart, another tab consumed them, or you arrived more than 10 minutes after creation). Re-run the flow.
500 github exchange failed: ... 401 GitHub rejected the temporary code (typically because more than ~10 seconds elapsed before the callback fired, or the App was deleted on GitHub before the callback completed). Re-run the flow.
403 superuser required The signed-in account doesn't have the superuser flag. Set it on the user row directly (no admin UI for this yet).