6.1.1. GITHUB_TOKEN, Permissions, and PATs
💡 First Principle: GITHUB_TOKEN is not a user credential — it's an ephemeral GitHub App installation token minted for one run, scoped to one repository, and revoked when the job ends — which is why the correct instinct is almost always to shrink it rather than to replace it with a personal access token.
| Property | GITHUB_TOKEN | Personal access token (PAT) |
|---|---|---|
| Lifetime | The job's duration (max 24h), then revoked | Until expiry or manual revocation |
| Scope | The single repository running the workflow | Everything the user can reach |
| Identity in logs | github-actions[bot] | The human who created it |
| Granularity | Per-workflow/job permissions: block | Classic scopes, or fine-grained per-repo |
| Rotation | Automatic every run | Manual |
| Offboarding risk | None | Breaks when the person leaves |
GitHub creates the token automatically — no configuration needed — and exposes it as secrets.GITHUB_TOKEN and github.token. Its default permissions come from repository or organization settings, with modern defaults being read-only for contents and metadata. Widening is explicit and, per 2.1.4, declaring a permissions: block replaces the defaults entirely rather than adding to them:
permissions: # workflow-wide floor
contents: read
jobs:
publish:
permissions:
contents: write # create a release
packages: write # push a package
id-token: write # request an OIDC token (6.1.3)
A PAT is the right answer only when the token genuinely must exceed one repository — pushing to another repository, calling org-level admin APIs, triggering workflows in a different repo. Even then, prefer a fine-grained PAT scoped to specific repositories and permissions, or better, a GitHub App installation token, which brings back short lifetimes and non-human identity. One more mechanism worth recognizing: GITHUB_TOKEN-triggered events do not start new workflow runs, which prevents infinite loops — and is exactly why teams reach for a PAT when they want a workflow to trigger another workflow.
⚠️ Exam Trap: "Resource not accessible by integration" is a GITHUB_TOKEN permissions error, not an authentication failure — the fix is a permissions: entry (or an organization default that isn't read-only), not swapping in a PAT. Reaching for a PAT to fix a 403 is the wrong answer on the exam and a privilege escalation in practice.
Reflection Question: A workflow must trigger a second workflow on push. GITHUB_TOKEN deliberately won't. Name the two supported alternatives and the security trade-off each carries.