Skip to content
← Back to blog
Engineering

GitHub App permissions: what to check before you connect

GitHub App permissions decide what an AI writer can touch in your repo: which scopes to grant, which to refuse, and how to audit or revoke access.

By Mitrasish, Co-founderJul 3, 202611 min read
GitHub App permissions: what to check before you connect

Before you install any GitHub App, one question decides everything else: what can this thing actually do to my repo, and how would you find out. Every AI writer, CI bot, and deploy tool that asks for repo access is really asking you to trust a permission grant you probably will not read carefully. This is how to read it.

What a GitHub App can actually do once you install it

A GitHub App does not get "access to your repo" as a single blob of trust. It gets a specific, named list of permissions, each one a separate opt-in, and a token that expires on its own.

Fine-grained permissions vs OAuth scopes: why GitHub Apps are the safer model

The old model, an OAuth App, asks for a scope like repo, and that one scope bundles read, write, and settings access to everything a repo has. There is no way to grant an OAuth App write access to code without also handing it your webhook config and deploy keys. GitHub's own comparison is direct about the gap: "GitHub Apps use fine-grained permissions, give the user more control over which repositories the app can access, and use short-lived tokens," while OAuth Apps "can't use granular permissions."

A GitHub App instead requests a list of named permissions, each independently set to read, write, or no access, and it only gets what you approve. Refuse a permission at install time and the app's token simply cannot call the endpoints that permission would unlock, not "the app promises not to use it," but the API rejects the call. That is the whole reason GitHub steers new integrations toward Apps: the blast radius of a stolen or misused token is bounded by what you actually checked.

The permission categories that matter: Contents, Pull requests, Metadata, Actions, Workflows, Secrets, Administration

GitHub's docs split permissions into repository, organization, and account categories, each with its own read, write, or no-access setting. For a repo-connected app, these are the ones worth actually reading before you click approve:

PermissionWhat it unlocksWhy it is separate
ContentsRead files, write files, create commits and branchesThe core read/write surface for the repo's code
Pull requestsOpen, comment on, and merge PRsIndependent of Contents; an app can read code without touching PRs, or vice versa
MetadataRead repo name, topics, and basic infoMandatory baseline every GitHub App gets, read-only
ActionsTrigger and manage workflow runsRuns existing workflows; does not let the app edit them
WorkflowsCreate or edit files under .github/workflows/A dedicated permission GitHub added in 2020 specifically so writing to .github/workflows/ requires an explicit, separate opt-in from Contents
SecretsManage Actions secrets at the repo or org levelFully separate from Contents; an app cannot read your secrets through Contents access no matter how broad that grant is
AdministrationRepo settings, branch protection rules, collaborator accessThe permission that can change the rules the other permissions operate under

Notice what is missing from that list if all an app does is draft files and open PRs: Actions, Workflows, Secrets, and Administration. None of them are required to write a blog post.

Installation tokens expire in an hour, and access is scoped to the repos you pick

An installed app authenticates with an installation access token, and that token is short-lived by design. Per GitHub's docs, installation access tokens expire after a predefined amount of time, currently 1 hour, compared to OAuth app tokens that stay valid until someone revokes them. A leaked GitHub App token is a problem for up to an hour. A leaked OAuth token is a problem indefinitely.

Scope works the same way at the repo level. When generating an installation access token, the calling code can optionally pass repositories or repository_ids to restrict that specific token to a subset of the repos the installation covers. Without it, the token can reach every repo the installation was granted. Whether an app actually narrows each token this way is up to how it is built, which is a good reason to check the app's own documentation, not just assume the platform does it for you.

The permissions an AI blog writer needs, and the ones it should never ask for

An AI writer's job is narrow: read your repo's conventions, draft a post, commit it to a branch, open a PR. That job maps to almost none of the permission surface a GitHub App can ask for.

What Lyra actually requests: Contents (read/write), Pull requests (write), Metadata (read)

This is Lyra's own footprint, and it is the answer key for what a legitimate blog-writing app should look like: Contents (read and write), Pull requests (write), and Metadata (read, the mandatory baseline). Contents read lets her learn your existing posts' frontmatter and voice. Contents write lets her commit a new Markdown file to a branch. Pull requests write lets her open the PR and tag you. That is the entire list. She also asks you to scope the installation to the one repo your blog lives in, not "all repositories," because there is no reason for a blog writer to see anything else in your GitHub account.

Comparing that against the permission table above: no Actions, no Workflows, no Secrets, no Administration. A writer that only touches Markdown files in a content folder has no legitimate use for any of them, and if you connect a tool like Lyra, the autonomous AI blog writer, or any competing product, that three-permission list is what to check for before you approve the install.

Red flags: Administration, Workflows, Secrets, or "All repositories" access with no reason tied to writing a blog post

Four requests should make you stop and ask why, specifically, before approving:

  • Administration for a content tool has no job to do. This permission can rewrite branch protection rules, which means a compromised token with Administration access could remove the very protections meant to contain it.
  • Workflows access means the app can edit .github/workflows/ files directly, the same files that define what your CI runs and with what permissions. A blog-writing tool never needs to touch CI configuration.
  • Secrets access means the app can manage which Actions secrets exist and which repos can use them, a capability with zero overlap with drafting a Markdown file.
  • "All repositories" instead of "Only select repositories" grants access to every repo the installing account can see, current and future, when the tool's actual job is one blog folder in one repo.

None of these four are inherently malicious; CI and deploy tools have real reasons to request some of them. The red flag is a mismatch: an AI writer, an SEO tool, or anything whose entire job is drafting content asking for permissions that job does not use. GitHub's own guidance is blunt about the default you should hold every app to: "You should select the minimum permissions required for the app."

Why a legitimate writer cannot force-push or read your Actions secrets, even if it wanted to

Two of the scariest-sounding risks are also the ones the platform itself makes structurally hard, not just policy-discouraged.

Force-pushing over your history requires write access to a protected branch, and branch protection rules exist specifically to stop that: with review requirements enabled, "collaborators can only push changes to a protected branch via a pull request that is approved by the required number of reviewers with write permissions." That applies to an app's token exactly as it applies to a human contributor. Turn on branch protection on main and a Contents-write token, no matter who holds it, is forced through the PR path.

Reading your Actions secrets requires the separate Secrets permission most content tools never ask for, and even an app that somehow had it could not read the actual values. GitHub's Actions secrets API lists secret names "without revealing their encrypted values," full stop, by design, at the API level. There is no endpoint that returns a decrypted secret to any caller, permission or not. An app claiming it needs Secrets access "just in case" is asking for a permission that cannot even do the thing you would fear it doing.

How to verify what a connected app has access to, and revoke it

Everything above is about deciding what to grant on install. This part is about auditing what is already installed, on any repo, for any app, not just an AI writer.

Reviewing installed GitHub Apps and their exact permission grants

On a personal account, go to your profile picture, then Settings, then Integrations in the sidebar, then Applications, then Installed GitHub Apps, and click Configure next to any app to see its exact permission list and repository access under one page.

On an organization, go to Your organizations, click Settings next to the org, then Third-party Access in the sidebar, then GitHub Apps, and click Configure next to the app. The same permission and repository-access detail shows up there. This is the page that tells you, for any app you have ever connected, precisely which of Contents, Actions, Workflows, Secrets, and Administration it currently holds, not what it asked for at install time, what it has right now.

Reading the GitHub audit log for what a token actually did

The install screen tells you what an app can do. The audit log tells you what it did. GitHub's organization audit log records a permissions for a GitHub App were updated event whenever a grant changes, along with per-installation detail like repository_selection and application_client_id, and a token_scopes field carried on most audit entries. If an app's access ever widened without you doing it, or a token was used somewhere you did not expect, the audit log is the record, not the app's own dashboard.

The one-click fix for the most common misconfiguration: "All repositories" vs "Only select repositories"

This single toggle is worth checking on every app you have ever installed, because it is the misconfiguration that actually bit someone in production. In an April 2026 GitHub Community discussion, a developer described granting the Vercel GitHub App access intending a single repository, then finding force-pushed changes across multiple repos and branches touching config files like .gitignore, Tailwind, and ESLint setup. The diagnosis from another community member, abbosaliboev, named the likely cause directly the next day: "If 'All repositories' was accidentally selected, Vercel (and its automated bots) will have access to everything." The fix does not require uninstalling anything. From the app's Configure page, under Repository access, switch from All repositories to Only select repositories, choose the repos it actually needs, and save.

How to revoke or uninstall an app cleanly

Uninstalling and de-authorizing are two different actions, and skipping one leaves a stale grant behind:

  1. From the app's Configure page (personal Settings > Integrations > Applications > Installed GitHub Apps, or org Settings > Third-party Access > GitHub Apps), click Uninstall. This removes the app's access to the repo or org immediately.
  2. If you personally authorized the app at any point, also go to Settings, then Integrations, then Applications, then the Authorized GitHub Apps tab, and click Revoke next to it. GitHub's docs note this step is separate from uninstalling, and that a personal authorization can only be revoked by the person who granted it, not by an org owner.
  3. If you are an org owner and a member authorized an app you want gone, you cannot revoke their personal authorization for them, but uninstalling the app from the organization "will prevent the app from accessing resources in that account," which is the control that actually matters at the org level.
  4. If you only want to pause access without losing the install's configuration, use Suspend on the Configure page instead of Uninstall. It blocks the app's API calls while keeping its settings in place for later.

None of this is specific to AI tools. It is the same checklist for a CI bot, a deploy integration, or anything else asking for repo access, and it takes about five minutes to run against everything currently installed on your account.

A repo-scoped, PR-only permission footprint is not a constraint Lyra works around, it is the whole trust model: she reads and drafts through Contents access on a branch, never touches your CI or secrets, and the only thing she ever asks you to do is review the diff and merge it yourself. Pair that scoped access with branch protection and required status checks and a writer physically cannot ship anything without your approval, no matter what its permission grant says on paper. The fact-check gate that runs before the PR even opens is a separate layer on top of that, catching bad claims the permission model was never meant to catch.

Lyra's own GitHub App footprint is exactly the answer key in this post: Contents and Pull requests on the one repo you pick, nothing else, no force-push, no Secrets access, ever.

Talk to the founder → · Join the waitlist

FAQ

Frequently asked

What permissions does a GitHub App actually need to write a blog post?+

Three, and no more: Contents (read and write, to read your repo's conventions and commit a new file), Pull requests (write, to open the PR), and Metadata (read, a mandatory baseline every GitHub App gets). It does not need Administration, Workflows, Secrets, or Actions access to draft a post and open a PR.

Can a connected GitHub App push directly to my main branch?+

Only if you let it. Contents write access lets an app commit to a branch, but a branch protection rule on main blocks any direct push, including from an app, and forces the change through a pull request like any other contributor. Set branch protection once and this becomes structurally true, not just a policy you hope holds.

How do I see what permissions a GitHub App has on my repo?+

For a personal account, go to Settings, then Integrations, then Applications, then Installed GitHub Apps, and click Configure next to the app. For an organization, go to the org's Settings, then Third-party Access, then GitHub Apps, and click Configure. Both show the exact permission grants and whether access is set to all repositories or only selected ones.

How do I revoke a GitHub App's access to my repo?+

Uninstall it from the app's Configure page, which removes its access immediately. If you personally authorized the app, also revoke it under Settings, Integrations, Applications, Authorized GitHub Apps, since uninstalling and de-authorizing are separate steps and skipping the second leaves a stale authorization behind.

Built by the tool you're reading about

This post is the kind of thing Lyra ships on her own.

Lyra finds the topics worth ranking for, writes them in your repo's voice, fact-checks every claim, and opens a pull request scored and ready to merge. You review and hit merge. Want to see what she'd write for you? Tell us about your blog and the founder will walk through it with you.

GitHub App PermissionsGitHub App ScopesAI Tool Repo Access SecurityConnect AI Writer to GitHubRevoke GitHub App AccessTechnical SEO