Skip to content

GitHub App Setup

This is the recommended GitHub auth mode for teams sharing one Archon instance. It replaces the shared GITHUB_TOKEN PAT with a registered GitHub App so that:

  • Bot comments appear as archon[bot] with the App badge, not under an operator’s personal account.
  • Installation access tokens rotate automatically every ~1h (smaller blast radius if leaked).
  • Webhooks centralise — one URL per App covers every installation.
  • A team’s repos can span multiple GitHub orgs (or a mix of orgs and personal accounts) — Archon routes per-(owner, repo) to the right installation transparently.

Solo installs that only need the PAT model can ignore this page; see GitHub for the legacy setup.

SituationRecommended mode
Solo developer, single GitHub accountPAT
Team of 2+ sharing one Archon instanceApp
Repos across multiple orgsApp
You want bot comments to attribute as <slug>[bot] with App badgeApp
You want short-lived (1h) tokens instead of long-lived PATApp

Archon refuses to start with both modes configured. Pick one set of env vars.

  1. Go to https://github.com/settings/apps/new (or https://github.com/organizations/<org>/settings/apps/new for an org-owned App).
  2. Fill in:
    • GitHub App name — e.g. Archon Bot. The slug derived from this (visible in the App URL) is what you’ll later set as GITHUB_APP_SLUG. Self-filter compares against <slug>[bot].
    • Homepage URL — your team’s Archon URL, e.g. https://archon.example.com/.
    • Webhook URLhttps://archon.example.com/webhooks/github.
    • Webhook secret — same value as your WEBHOOK_SECRET env var.
  3. Uncheck Active on the user authorisation callback URL — Archon doesn’t use OAuth in PR-B.

Repository permissions:

PermissionAccessUsed for
ContentsReadCloning + reading repo metadata
IssuesRead & WritecreateComment + listComments
Pull requestsRead & Writepulls.get + comment posting
MetadataReadMandatory (auto-included)

Account permissions: none.

Subscribe to:

  • Issue comments
  • Pull request review comments
  • Pull request
  • Issues (used for closed cleanup)
  1. After saving the App, scroll to Private keys and click Generate a private key.
  2. Save the downloaded .pem file in a location only readable by the Archon process — e.g. /etc/archon/github-app.pem.

Install the App on every org or personal account that holds repos your team operates on:

  1. From the App settings page, click Install App.
  2. Pick the org → grant access to all repos (or selected repos).
  3. Repeat for every org/account.

Multi-installation: Archon resolves owner/repo → installation_id via GET /repos/{owner}/{repo}/installation automatically. No per-install config needed unless you’re a single-install team — see GITHUB_APP_INSTALLATION_ID below.

Add the following to your .env (or ~/.archon/.env):

GITHUB_APP_ID=123456 # numeric App ID, visible on the App settings page
GITHUB_APP_PRIVATE_KEY_PATH=/etc/archon/github-app.pem
WEBHOOK_SECRET=<same value as on the GitHub side>
# Optional:
# GITHUB_APP_SLUG=archon-bot # defaults to 'archon'; set this if you named your App
# # differently. The bot's posted-comment login is `<slug>[bot]`.
# GITHUB_APP_INSTALLATION_ID=98765 # skip the per-(owner, repo) installation lookup when you only
# # have one installation. Saves one HTTP round trip per new
# # repo after a restart.

If you can’t write a file (e.g. a managed PaaS), set the PEM contents inline:

GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"

Archon normalises the literal \n sequence in .env-quoted values into real newlines.

Archon refuses to start if both modes are configured. Remove GITHUB_TOKEN from your env when switching to App mode.

  1. Restart the server.
  2. Confirm the startup log shows github.adapter_mode_app with your slug.
  3. Trigger a webhook from a repo where the App is installed (e.g. comment @archon ping on an issue).
  4. Confirm the bot’s reply appears as <slug>[bot] with the App badge in the GitHub UI.

Installation tokens are valid for 1h. Archon caches each installation_id → token pair and refreshes ~5 minutes before expiry on the next access. No background timer; no leaked handles.

event.installation.id short-circuits the lookup

Section titled “event.installation.id short-circuits the lookup”

Every webhook delivery from a GitHub App carries installation.id. Archon primes its owner/repo → installation_id cache from the payload, so the next outbound call to that repo skips the GET /repos/{owner}/{repo}/installation round trip.

A 401 from any installation Octokit evicts the cached token and the call is retried once with a fresh token. Persistent 401s propagate as the original error.

Workflows that span >1h need a fresh token to push from the cloned worktree. Archon installs a git credential helper at clone time (App mode only): the worktree’s .git/config points at ~/.archon/bin/git-credential-archon, which talks back to Archon’s internal endpoint for a fresh installation token on each operation.

Compiled binary builds: the credential helper is installed from scripts/git-credential-archon.sh in the source tree. Compiled binaries that don’t ship scripts/ on disk silently skip the install — workflows up to 1h still succeed via the URL-embedded installation token and the GH_TOKEN env injection, but longer workflows will see git push fail with “Authentication failed” past the 1h mark. Track this when running App mode in a binary deployment.

The credential-helper backend is exposed at POST /internal/git-credential and hands out live installation access tokens. It MUST NOT be reachable from outside the Archon host.

Archon enforces this at startup: with App mode active and the server bound to a non-loopback interface (e.g. 0.0.0.0), the process refuses to start and exits with github_app.internal_endpoint_public_bind_rejected. Two correct configurations:

  1. Recommended — bind Archon to 127.0.0.1 (HOST=127.0.0.1) and put a reverse proxy in front. Configure the proxy to drop /internal/* paths. Operators who use systemd / docker for upstream routing also fall into this category.
  2. Opt-in escape hatch — ARCHON_ALLOW_INTERNAL_ON_PUBLIC_BIND=1 combined with HOST=0.0.0.0 (or unset). Use ONLY when your reverse proxy already drops /internal/* AND your deployment topology genuinely requires the upstream to bind non-loopback (e.g. a container network where loopback isn’t reachable from the proxy). Startup logs github_app.internal_endpoint_exposed_acknowledged so the choice is auditable.

Example Caddy snippet that drops /internal/*:

example.com {
@internal path /internal/*
respond @internal 404
reverse_proxy 127.0.0.1:3090
}
  1. Register the App and install on your orgs (Steps 1–5 above).
  2. Add GITHUB_APP_* env vars (Step 6).
  3. Remove GITHUB_TOKEN from your env (or comment it out). Archon refuses to start if both are set.
  4. Restart Archon.
  5. Webhook URLs configured per-repo against the PAT-mode setup can stay or be removed — the App’s single webhook URL covers everything once it’s installed. New repos auto-join via App installation.

AppPrivateKeyError: Provided value is not a valid PEM-encoded private key

Section titled “AppPrivateKeyError: Provided value is not a valid PEM-encoded private key”
  • Check the file content includes -----BEGIN ... PRIVATE KEY----- and -----END ... PRIVATE KEY-----.
  • For inline keys, ensure the .env value preserves newlines (either literal newlines in a multi-line value or the \n escape inside double quotes).

AppNotInstalledError: The Archon GitHub App is not installed on "<owner>"

Section titled “AppNotInstalledError: The Archon GitHub App is not installed on "<owner>"”
  • The App is not installed on that owner’s org/account. Use the install link in the error message to add it.

Repeated 401s on outbound API calls (createComment, listComments, repos.get, pulls.get) point at installation / token issues, not webhook config. Walk through:

  • Verify the App is installed on the target owner and has not been suspended or uninstalled. Visit https://github.com/settings/installations (or the org equivalent) to confirm.
  • Verify the App’s permissions still include the scopes the operation needs (Contents:Read for clone; Issues:RW + Pull requests:RW for comments and reactions). Permission scope changes require operators to review and accept the new permissions on every installation; Archon will 401 until that’s done.
  • Verify the private key in your env (GITHUB_APP_PRIVATE_KEY or _PATH) matches the same App that GITHUB_APP_ID points at. Mismatched key+ID is a common cause of “401 from JWT” errors at token-issuance time.
  • Verify GITHUB_APP_INSTALLATION_ID (if set) still corresponds to a live installation — uninstall + reinstall assigns a new ID.

Webhook deliveries fail signature verification

Section titled “Webhook deliveries fail signature verification”

A webhook-secret mismatch causes github.signature_mismatch / github.signature_length_mismatch errors at the POST /webhooks/github endpoint — distinct from outbound API 401s. If GitHub’s webhook delivery page shows red ❌ next to the delivery (rather than your bot just silently not responding), check:

  • WEBHOOK_SECRET in Archon’s env matches the value entered in the GitHub App’s webhook configuration page exactly.

Bot comments still appear under your personal account

Section titled “Bot comments still appear under your personal account”
  • You’re still in PAT mode. Check process.env.GITHUB_TOKEN is unset and GITHUB_APP_ID is set; restart.

Server refused to start: github_app.internal_endpoint_public_bind_rejected

Section titled “Server refused to start: github_app.internal_endpoint_public_bind_rejected”
  • App mode is active but the server is bound to a non-loopback interface. This is a fail-fast guard — the /internal/git-credential endpoint hands out live installation access tokens and would leak credentials to the network. Either set HOST=127.0.0.1 (recommended), or, if your reverse proxy already drops /internal/* and you genuinely need a non-loopback bind, set ARCHON_ALLOW_INTERNAL_ON_PUBLIC_BIND=1.

Server log shows github_app.internal_endpoint_exposed_acknowledged

Section titled “Server log shows github_app.internal_endpoint_exposed_acknowledged”
  • You set ARCHON_ALLOW_INTERNAL_ON_PUBLIC_BIND=1. Double-check that your reverse proxy actually drops /internal/* — a curl https://your-archon/internal/git-credential -d '{"host":"github.com","path":"any/repo"}' from outside the host must return 404 or 403 from the proxy (NOT a token from Archon).