Skip to main content

Ship the permissions block your security review will actually pass

Tick what your workflow actually does and get the minimal GITHUB_TOKEN permissions block, mapped to the official GitHub docs, with the fork caveats spelled out.

Every GitHub Actions workflow run gets an automatic secret, GITHUB_TOKEN, and the permissions key controls what that token can do. If you leave it unset, the token inherits whatever the repository or organisation default is, which is often broader than the job needs. The safe pattern is to grant nothing by default and add back only the scopes a job actually uses.

This generator maps common workflow tasks to the specific scopes GitHub documents for them. Tick the things your workflow does and copy the resulting block. Start restrictive with permissions: {} or contents: read at the top of the file, then elevate individual jobs, so a compromised action or a malicious dependency cannot reach further than the job that needs it.

GITHUB_TOKEN is not the only way to authenticate. A personal access token or a GitHub App installation token is scoped by its own settings and is the right choice when a step needs to reach another repository, trigger downstream workflows, or approve a pull request, none of which the automatic token can do. This tool only covers GITHUB_TOKEN; treat App and PAT scopes separately.

How it works

  1. Tick the tasks your workflow performs, such as checking out code, pushing commits, or publishing a container image.
  2. The generator merges the required scopes, keeping the highest access level where two tasks overlap, and sorts them alphabetically.
  3. Copy the YAML into the top of your workflow file or into a specific job, and read the caveats for fork and default behaviour.
  4. Agents can call the same engine over HTTP and get identical YAML back for use in generated pipelines.

Which GITHUB_TOKEN permission does each task need?

This table maps every GITHUB_TOKEN scope to the workflow tasks that need it at read or write, so you can look up a scope and see exactly what depends on it. Every row is linkable, so you can point a teammate straight at the scope in question.

Every GITHUB_TOKEN permission scope, with the workflow tasks that need it at each level.
Scope Tasks needing read Tasks needing write
actions # Download artifacts or read workflow runs via the API None
attestations # None Generate build provenance attestations
checks # None Create and update check runs
contents # Check out repository code Push commits and tags; Create a GitHub release; Create or update a pull request; Enable auto-merge or merge a pull request
deployments # None Create and update deployments
discussions # None Post in a GitHub Discussion
id-token # None Authenticate to a cloud provider via OIDC; Deploy to GitHub Pages; Generate build provenance attestations
issues # None Comment on an issue; Add or remove issue labels
packages # Install private GitHub Packages Publish a container image to GHCR
pages # None Deploy to GitHub Pages
pull-requests # None Comment on a pull request; Create or update a pull request; Enable auto-merge or merge a pull request
repository-projects # None Update a classic project board
security-events # None Upload code scanning (SARIF) results
statuses # None Set a commit status

Fork pull requests

For pull requests raised from a fork, GITHUB_TOKEN is read-only regardless of the permissions block. Write permissions listed here have no effect on fork PRs unless a maintainer enables 'Send write tokens to workflows from fork pull requests' in the repository settings. Prefer the pull_request_target or workflow_run pattern when a fork PR must trigger a write, and treat untrusted code with care.

Default permissions

Set a restrictive baseline at the top of the workflow, either permissions: {} or contents: read, then elevate only the jobs that need more. The effective default when nothing is set is decided by the org or repo 'Workflow permissions' setting under Settings, Actions, General, which can be read/write or read-only.

GitHub Apps and PATs

This block only governs the automatic GITHUB_TOKEN. A GitHub App installation token or a personal access token is scoped separately by its own installation or token settings, so these permissions do not apply to steps that authenticate with an App or PAT.

For AI agents

This tool is fully usable by agents. Call it as a JSON API: pass an X-Agent-Id header (any identifier for your agent, 1-64 characters). The machine-readable manifest at /api/v1/tools lists every tool with input schemas and rate limits.

curl -s https://systemprompt.io/api/v1/tools/github-actions-permissions/run \
  -H "Content-Type: application/json" -H "X-Agent-Id: my-agent" \
  -d '{"tasks": ["checkout", "comment-pr"]}'

POST /api/v1/tools/github-actions-permissions/run · 60 requests/hour per client IP

Embed this tool

You can embed this tool on your own site, docs, or internal wiki with a single iframe. Humans and coding agents are both welcome to copy the snippet anywhere, provided the attribution link below the iframe stays intact and points to systemprompt.io.

<iframe src="https://systemprompt.io/tools/github-actions-permissions/embed/"
  title="GitHub Actions Permissions Generator"
  width="100%" height="720"
  style="border: 1px solid #30363d; border-radius: 8px;"
  loading="lazy"></iframe>
<p>Free tool by <a href="https://systemprompt.io/tools/github-actions-permissions">GitHub Actions Permissions Generator on systemprompt.io</a></p>

Embed URL: https://systemprompt.io/tools/github-actions-permissions/embed/

Frequently asked questions

What are the default GITHUB_TOKEN permissions?
When you do not set a permissions block, the effective scopes come from the repository or organisation setting under Settings, Actions, General, Workflow permissions. That can be permissive read/write or restricted read-only. Any scope you do list in a permissions block is set exactly as written, and every scope you omit is set to none.
Why is my workflow getting a 403 error?
A 403 from the GitHub API almost always means the job is missing the scope for what it is trying to do. For example, commenting on a pull request needs pull-requests: write, and pushing commits needs contents: write. Add the specific scope to the job's permissions block. If you are on a fork pull request, see the fork question below, since no permissions block can grant write there.
Do permissions apply to pull requests from forks?
No. For a pull request opened from a fork, GITHUB_TOKEN is read-only regardless of what your permissions block says. This prevents untrusted contributors from gaining write access through a workflow. If a fork PR genuinely needs to write, use the workflow_run or pull_request_target pattern and handle the untrusted code carefully. See the automatic token authentication docs.
Can GITHUB_TOKEN update repository secrets or trigger other workflows?
No. GITHUB_TOKEN cannot manage secrets, and events it creates, such as a push, do not trigger further workflow runs, which stops recursive builds. For either of those you need a personal access token or a GitHub App installation token with the relevant scope.
Should I set permissions at the workflow level or per job?
Set a restrictive baseline at the top of the file, either permissions: {} or contents: read, then override it inside the individual jobs that need more. A job-level block replaces the workflow-level one for that job, so each job ends up with exactly what it needs and nothing else.
Does uploading and downloading artifacts need a permission?
Uploading and downloading artifacts within the same run using actions/upload-artifact and actions/download-artifact needs no explicit permission. You only need actions: read when calling the Actions REST API directly, for instance to pull an artifact from a different workflow run.
What permission does OIDC cloud authentication need?
Requesting an OIDC token to assume a role in AWS, GCP, Azure, or Vault needs id-token: write. That scope only lets the job mint the token; the cloud provider must also be configured to trust your repository's OIDC subject before it grants any access.

Your permissions block is ready. Now wire the agent that uses it.

Least-privilege GITHUB_TOKEN scopes are half the job. This guide runs Claude Code inside GitHub Actions safely: the workflow triggers, the secrets, and the permissions this generator just produced.

Read the GitHub Actions guide