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
- Tick the tasks your workflow performs, such as checking out code, pushing commits, or publishing a container image.
- The generator merges the required scopes, keeping the highest access level where two tasks overlap, and sorts them alphabetically.
- Copy the YAML into the top of your workflow file or into a specific job, and read the caveats for fork and default behaviour.
- 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.
| 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"]}'
Frequently asked questions
What are the default GITHUB_TOKEN permissions?
Why is my workflow getting a 403 error?
Do permissions apply to pull requests from forks?
Can GITHUB_TOKEN update repository secrets or trigger other workflows?
Should I set permissions at the workflow level or per job?
Does uploading and downloading artifacts need a permission?
What permission does OIDC cloud authentication need?
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.