LGTM × GitHub
LGTM's GitHub App: 4 permissions (contents:read, pull-requests:write, checks:write, metadata:read), webhook subscriptions on pull_request + push + installation. No access to secrets, can't push code.
Why a GitHub App (not OAuth, not PAT)
GitHub has three authentication models for third-party tools: Personal Access Tokens (PATs — act as a user, inherit their permissions), OAuth Apps (act as the user with OAuth-granted scopes), and GitHub Apps (act as the App with per-installation granular permissions).
LGTM uses the GitHub App model exclusively. Why: (1) granular permissions — we request only the 4 we need, can't access anything else. (2) Short-lived installation tokens — 1 hour expiry, no long-lived credentials. (3) Per-repo install scope — you choose which repos LGTM sees. (4) Audit trail — every action LGTM takes is attributed to the App, visible in your org's audit log.
OAuth Apps would inherit the full user scope (everything the user can see), making least-privilege impossible. PATs are similarly broad. GitHub Apps are the only model that fits the security goals of a third-party code review tool.
The 4 permissions LGTM requests
Contents: read — fetch the diff at PR time, fetch context symbol bodies at review time. No write access; we cannot push code, cannot modify files. Permission grants happen at installation; you see this list before clicking Install.
Pull requests: write — post review comments, set review verdict (approve/request_changes/comment), update existing reviews when re-running. This is the highest-privilege permission we have; it's the minimum needed to post a code review.
Checks: write — write Check Runs that branch protection rules can require. LGTM's verdict can be set as a required check; a failing review then blocks the merge until resolved.
Metadata: read — required by GitHub on every App. Gives basic repo info (name, visibility, default branch). Cannot be excluded; it's the bootstrap permission.
What we DON'T request: actions:* (no workflow access), secrets:* (no environment secret access), administration:* (no settings modification), members:* (no org member visibility), packages:* (no registry access).
Webhook subscriptions
LGTM subscribes to four event types: pull_request (opened/synchronize/reopened/closed/labeled — the PR lifecycle), push (commits to default branch — drives the runtime watchdog for LGTM Security), installation (when LGTM is installed/uninstalled/permissions changed), installation_repositories (when repos are added/removed from an existing installation).
Each delivery from GitHub arrives signed with HMAC-SHA-256 using a shared secret. LGTM verifies the X-Hub-Signature-256 header before processing. Failed verification → 401 + the event is discarded. Successful → enqueued for processing within milliseconds.
Idempotency: GitHub retries failed deliveries up to 3 times. Each retry has the same X-GitHub-Delivery UUID. LGTM dedupes on that UUID in MongoDB — a duplicate delivery is acknowledged with 200 but not reprocessed.
Installation tokens — the runtime credential
LGTM doesn't store any long-lived credentials for your GitHub. Instead, we hold the App's private key (one key per GitHub App, not per customer) and use it to mint short-lived installation tokens.
Flow per action: (1) Generate a JWT signed with the App's RSA private key. (2) POST /app/installations/{installation_id}/access_tokens with the JWT. (3) GitHub returns a token scoped to that installation's permissions, expiring in 1 hour. (4) LGTM uses that token for the actual operations (fetch diff, post review). (5) Token is cached in worker memory until 55 minutes (5-minute buffer), then refreshed.
The App's private key lives in Fly Secrets. It never touches disk on a customer-facing server, never appears in logs. Compromising LGTM's private key would let an attacker mint installation tokens for all customers — which is why it's stored separately from any other credential and rotated annually.
Installing + revoking
Install from github.com/apps/lgtm-app/installations/new (or via the 'Install on GitHub' button in LGTM's settings). You choose: all repos OR specific repos. You can change this later.
After install, LGTM picks up webhook deliveries automatically. First action: index your repos if you've enabled context indexing. Subsequent: review every PR opened against an enrolled repo.
Revoke: github.com/settings/installations → LGTM → Uninstall. GitHub revokes our installation token immediately. Within 24h, LGTM hard-deletes any data we held about your installation (encrypted BYOK keys, review history, settings). Webhooks stop firing instantly.
Partial revocation: instead of uninstalling, you can change which repos LGTM accesses. Settings on github.com → LGTM → Configure → switch to 'Selected repositories' → pick which ones stay. LGTM webhook deliveries for the removed repos stop immediately.
Implementation examples
// LGTM mints a token per review job
async function getInstallationToken(installationId: number) {
// 1. Sign JWT with App's RSA private key
const jwt = sign({
iss: APP_ID,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 600,
}, APP_PRIVATE_KEY, { algorithm: 'RS256' });
// 2. Exchange JWT for installation token
const res = await fetch(
`https://api.github.com/app/installations/${installationId}/access_tokens`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${jwt}`,
Accept: 'application/vnd.github+json',
},
},
);
const { token, expires_at } = await res.json();
// 3. Cache for ~55 min; refresh proactively
return { token, expiresAt: new Date(expires_at) };
}// HMAC-SHA-256 with shared secret
import crypto from 'crypto';
function verifyGitHubWebhook(rawBody: Buffer, signature: string) {
const expected = 'sha256=' + crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(rawBody)
.digest('hex');
// Timing-safe compare
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected),
);
}Install LGTM on GitHub
4 minimal permissions · 1-hour tokens · per-repo scope
Go to the product pageGitHub integration FAQs
Can LGTM push code to my repo?
No. We request only `contents: read`, not `contents: write`. Our agents can read diff + context, but cannot modify files, cannot create branches, cannot push commits. The permission boundary is enforced by GitHub, not by us — even a compromise of LGTM couldn't grant write access without you reinstalling the App.
What happens if LGTM is breached?
Worst case: attacker gets LGTM's App private key. They could mint installation tokens for any customer, scoped to whatever permissions that customer granted (read diff, post review). They could read source from enrolled repos and post fake reviews. They could NOT push code, modify settings, access secrets, or take administrative action — those permissions aren't requested.
How do I see what LGTM has accessed in my org?
github.com/organizations/{your-org}/settings/installations → LGTM → Configure. Shows: which repos LGTM has access to, when it was installed, when permissions were last updated, last activity timestamp. For deeper audit, GitHub Enterprise Cloud customers can view per-action audit logs.
Can I install LGTM on multiple orgs / personal account?
Yes. Each install is independent — install on Org A, Org B, and your personal account if you want. Each install gets its own installation token, its own permission set, its own webhook subscriptions. Cross-org reviews don't happen automatically; each org's PRs go to its own installation.
Does LGTM work with GitHub Enterprise Server?
Not yet. LGTM is built for github.com (GitHub Cloud). GitHub Enterprise Server (self-hosted) is on the roadmap but requires deploying LGTM into the customer's environment. Contact tarinagarwal@gmail.com for status / interest list.
Related across LGTM
GitHub App permissions glossary
Deeper dive on the App vs OAuth vs PAT model differences.
Pinned action explained
Why LGTM Security flags non-SHA action refs in your workflows.
Multi-repo rollout
How to enroll an entire org while keeping per-repo opt-in control.
CI gate setup
Wire LGTM's Check Run as a required branch-protection check.
Other integrations
OpenAI
BYOK with OpenAI on LGTM: paste your platform.openai.com key, we encrypt with AES-256-GCM, decrypt only inside worker memory at review time. Zero markup, your bill, your model choice.
Anthropic
BYOK with Anthropic on LGTM: connect your console.anthropic.com key, encrypted with AES-256-GCM, decrypted only in worker memory. Use Claude Opus 4.7, Sonnet 4.6, or Haiku 4.5 per repo.
Google Gemini
BYOK with Google Gemini on LGTM: paste your ai.google.dev key, AES-256-GCM at rest, decrypt only in worker memory. Use Gemini 2.5 Pro or Flash for review.
Dodo Payments
Dodo Payments powers LGTM's INR subscriptions: GST-compliant invoicing, Merchant-of-Record for international users, refund + dispute flows, idempotent webhook handling via Svix.