{
  "generatedAt": "2026-10-12T16:01:05.493Z",
  "effectiveAccess": {
    "rule": "Effective access = what the principal is (role grants) ∩ what the credential allows (token ceiling) ∩ what policy lets through (gates).",
    "notes": [
      "A token never adds access the user does not have. A fine-grained PAT with Contents: write on a repo where the user only has Read can still only read.",
      "App installation tokens and GITHUB_TOKEN have no user behind them, so the role part drops out and the App or workflow permissions are the whole grant.",
      "An OIDC token from id-token: write leaves GitHub. The cloud trust policy is then the only gate, and none of the GitHub grants above apply.",
      "Rulesets apply to everyone except the bypass list, so a bypass entry outranks any role."
    ]
  },
  "vocabularies": [
    {
      "id": "oauth-scope",
      "title": "Classic OAuth scopes",
      "usedBy": "Classic PATs, OAuth Apps",
      "pattern": "A short list of coarse scopes such as repo, workflow and admin:org. The token reaches every organization the user belongs to, limited only by what the user can do there."
    },
    {
      "id": "resource-permission",
      "title": "Resource permissions",
      "usedBy": "Fine-grained PATs, GitHub Apps, GITHUB_TOKEN",
      "pattern": "One permission per resource (Contents, Issues, Administration, Secrets, Workflows) set to none, read or write. Tied to one resource owner and a selection of repositories. GITHUB_TOKEN uses a subset of the same names."
    },
    {
      "id": "role",
      "title": "Roles and role permissions",
      "usedBy": "Enterprise, organization, team and repository roles, including custom roles",
      "pattern": "A ladder of roles (Read, Triage, Write, Maintain, Admin; Member, Owner) plus action-level extras such as Manage webhooks or Delete issues. The names do not match the resource permission list."
    },
    {
      "id": "key",
      "title": "Keys and certificates",
      "usedBy": "SSH keys, SSH certificates, deploy keys, App private keys, webhook secrets",
      "pattern": "No scopes at all. Access is whatever the key is attached to: a user's full Git access, one repository, or every installation of an App."
    },
    {
      "id": "setting",
      "title": "Settings and policies",
      "usedBy": "Enterprise and organization policies, environment rules, account settings",
      "pattern": "Not a grant on their own. They decide which grants are allowed, and they gate every token and role at request time."
    }
  ],
  "credentialModelsByScopeType": {
    "classic-pat-scope": {
      "vocabulary": "oauth-scope",
      "principal": "Human",
      "boundary": "Every organization the user belongs to; SAML SSO orgs also need the token authorized",
      "lifetime": "Expiry optional; an enterprise or org can set a maximum",
      "grantedBy": "The user, for themselves"
    },
    "oauth-app-scope": {
      "vocabulary": "oauth-scope",
      "principal": "Human, through an app",
      "boundary": "Every organization the user grants it, unless the org restricts OAuth app access",
      "lifetime": "Usually no expiry; lives until revoked",
      "grantedBy": "The user, on a consent screen"
    },
    "fine-grained-pat-permission": {
      "vocabulary": "resource-permission",
      "principal": "Human",
      "boundary": "One resource owner (a user or an org) and the repositories picked for the token",
      "lifetime": "Expiry required; 366 days by default, an enterprise or org can lower it",
      "grantedBy": "The user, plus org approval when the org requires it"
    },
    "github-app-permission": {
      "vocabulary": "resource-permission",
      "principal": "App",
      "boundary": "The repositories each installation was given (all, or a selection)",
      "lifetime": "Installation tokens last 1 hour; the private key that mints them does not expire",
      "grantedBy": "Whoever installs the App: an org owner, an App manager, or a repo admin for selected repos"
    },
    "app-token": {
      "vocabulary": "resource-permission",
      "principal": "App, or App acting for a user",
      "boundary": "The App's permissions, cut down to the installation's repositories (and the user's own access for user tokens)",
      "lifetime": "1 hour for installation tokens; 8 hours for user tokens, with a 6-month refresh token",
      "grantedBy": "Minted by the App with its private key, or by a user signing in to the App"
    },
    "actions-token-permission": {
      "vocabulary": "resource-permission",
      "principal": "Workflow run",
      "boundary": "The one repository the workflow runs in",
      "lifetime": "The job's duration",
      "grantedBy": "The workflow file's permissions block, falling back to the org or repo default"
    },
    "ai-agent": {
      "vocabulary": "resource-permission",
      "principal": "Agent working for a user",
      "boundary": "The repository it was assigned to, plus any tools and MCP servers it is configured with",
      "lifetime": "One task session",
      "grantedBy": "A user assigning it work; org and enterprise policy decide whether it may run"
    },
    "enterprise-role": {
      "vocabulary": "role",
      "principal": "Human",
      "boundary": "The whole enterprise, or the part a custom role covers",
      "lifetime": "Until removed, or until the IdP deprovisions the user (EMU)",
      "grantedBy": "An enterprise owner, or the IdP for EMU"
    },
    "org-role": {
      "vocabulary": "role",
      "principal": "Human",
      "boundary": "One organization, often every repository in it",
      "lifetime": "Until removed",
      "grantedBy": "An organization owner"
    },
    "custom-org-role": {
      "vocabulary": "role",
      "principal": "Human or team",
      "boundary": "One organization; a base repository role applies to every repository",
      "lifetime": "Until removed",
      "grantedBy": "An organization owner; the role can also be defined once for the whole enterprise"
    },
    "team-role": {
      "vocabulary": "role",
      "principal": "Human",
      "boundary": "Every repository the team, and its child teams, have been given",
      "lifetime": "Until removed; can be synced from an IdP group",
      "grantedBy": "An organization owner or a team maintainer"
    },
    "repo-role": {
      "vocabulary": "role",
      "principal": "Human or team",
      "boundary": "One repository",
      "lifetime": "Until removed",
      "grantedBy": "A repository admin or an organization owner"
    },
    "custom-repo-role": {
      "vocabulary": "role",
      "principal": "Human or team",
      "boundary": "One repository per assignment",
      "lifetime": "Until removed",
      "grantedBy": "An organization owner defines it; a repository admin assigns it"
    },
    "ruleset-bypass": {
      "vocabulary": "role",
      "principal": "Role, team or App",
      "boundary": "The branches, tags or pushes the ruleset targets",
      "lifetime": "Until removed from the bypass list",
      "grantedBy": "A repository admin, or an organization owner for org rulesets"
    },
    "codeowners": {
      "vocabulary": "role",
      "principal": "Human or team",
      "boundary": "The file paths listed for them, on branches that require code owner review",
      "lifetime": "As long as the CODEOWNERS file names them",
      "grantedBy": "Anyone who can change the CODEOWNERS file on the protected branch"
    },
    "ssh-key": {
      "vocabulary": "key",
      "principal": "Human",
      "boundary": "All Git access the user has, in every organization",
      "lifetime": "No expiry",
      "grantedBy": "The user"
    },
    "ssh-certificate-authority": {
      "vocabulary": "key",
      "principal": "Whoever holds the CA private key",
      "boundary": "Git access as any member of the organization or enterprise that trusts the CA",
      "lifetime": "Each certificate's validity; the CA key itself does not expire",
      "grantedBy": "An organization or enterprise owner uploads the CA"
    },
    "deploy-key": {
      "vocabulary": "key",
      "principal": "Machine",
      "boundary": "One repository, read-only or read-write",
      "lifetime": "No expiry",
      "grantedBy": "A repository admin"
    },
    "gpg-key": {
      "vocabulary": "key",
      "principal": "Human",
      "boundary": "Proves authorship of commits; grants no access",
      "lifetime": "Key's own expiry, if set",
      "grantedBy": "The user"
    },
    "app-credential": {
      "vocabulary": "key",
      "principal": "App",
      "boundary": "Every installation of the App, or every delivery of a webhook",
      "lifetime": "No expiry until rotated",
      "grantedBy": "The App owner or an App manager"
    },
    "runner": {
      "vocabulary": "setting",
      "principal": "Machine",
      "boundary": "The repositories and workflows allowed to use the runner or runner group",
      "lifetime": "Until removed; non-ephemeral runners keep state between jobs",
      "grantedBy": "A repository admin, an org owner or a CI/CD admin"
    },
    "environment-setting": {
      "vocabulary": "setting",
      "principal": "Repository",
      "boundary": "Deployments to one environment",
      "lifetime": "Until changed",
      "grantedBy": "A repository admin"
    },
    "enterprise-policy": {
      "vocabulary": "setting",
      "principal": "Enterprise",
      "boundary": "Every organization in the enterprise",
      "lifetime": "Until changed",
      "grantedBy": "An enterprise owner"
    },
    "account-setting": {
      "vocabulary": "setting",
      "principal": "Human",
      "boundary": "The user's own account",
      "lifetime": "Until changed",
      "grantedBy": "The user, within what the org or enterprise enforces"
    }
  },
  "grantMechanisms": [
    {
      "id": "enterprise-role",
      "mechanism": "Enterprise role: Owner, Billing manager, App manager, Security manager, User",
      "plane": "enterprise",
      "grantedBy": "Enterprise owner, or the IdP for EMU",
      "boundary": "Every organization",
      "lifetime": "Until removed",
      "vocabulary": "role",
      "refs": [
        "role:enterprise-owner",
        "role:enterprise-app-manager",
        "role:enterprise-security-manager",
        "role:enterprise-billing-manager",
        "role:enterprise-user"
      ]
    },
    {
      "id": "enterprise-guest",
      "mechanism": "Guest collaborator (EMU only)",
      "plane": "enterprise",
      "grantedBy": "IdP provisioning, then an org or repo admin grants access",
      "boundary": "Only the repositories and orgs they are added to",
      "lifetime": "Until deprovisioned",
      "vocabulary": "role",
      "refs": [
        "role:enterprise-guest-collaborator"
      ]
    },
    {
      "id": "enterprise-custom-role",
      "mechanism": "Enterprise custom role and enterprise teams",
      "plane": "enterprise",
      "grantedBy": "Enterprise owner",
      "boundary": "The enterprise settings the role covers",
      "lifetime": "Until removed",
      "vocabulary": "role",
      "refs": [
        "page:/tier/roles"
      ]
    },
    {
      "id": "org-role",
      "mechanism": "Organization role: Owner, Member, Moderator, Billing manager",
      "plane": "organization",
      "grantedBy": "Organization owner",
      "boundary": "One organization",
      "lifetime": "Until removed",
      "vocabulary": "role",
      "refs": [
        "role:org-owner",
        "role:org-member",
        "role:org-moderator",
        "role:org-billing-manager"
      ]
    },
    {
      "id": "org-predefined-role",
      "mechanism": "Pre-defined organization role: Security manager, App manager, CI/CD admin, All-repository read to admin",
      "plane": "organization",
      "grantedBy": "Organization owner",
      "boundary": "One organization, every repository for the all-repository roles",
      "lifetime": "Until removed",
      "vocabulary": "role",
      "refs": [
        "role:org-cicd-admin",
        "role:org-security-manager",
        "role:org-app-manager",
        "role:org-all-repo-admin",
        "role:org-all-repo-write"
      ]
    },
    {
      "id": "org-custom-role",
      "mechanism": "Custom organization role: org permissions plus an optional base repository role",
      "plane": "organization",
      "grantedBy": "Organization owner, or defined once for the enterprise",
      "boundary": "One organization",
      "lifetime": "Until removed",
      "vocabulary": "role",
      "refs": [
        "page:/tier/roles"
      ]
    },
    {
      "id": "org-base-permission",
      "mechanism": "Organization base permission: none, read, write or admin",
      "plane": "organization",
      "grantedBy": "Organization owner",
      "boundary": "Every repository, for every member",
      "lifetime": "Until changed",
      "vocabulary": "role",
      "refs": [
        "key:organization-base-permission"
      ]
    },
    {
      "id": "team",
      "mechanism": "Team membership: member or maintainer, inherited through child teams",
      "plane": "organization",
      "grantedBy": "Organization owner, team maintainer, or IdP group sync",
      "boundary": "The repositories the team was given",
      "lifetime": "Until removed",
      "vocabulary": "role",
      "refs": [
        "page:/tier/roles",
        "role:team-maintainer"
      ]
    },
    {
      "id": "repo-role",
      "mechanism": "Direct repository role: Read, Triage, Write, Maintain, Admin",
      "plane": "organization",
      "grantedBy": "Repository admin",
      "boundary": "One repository",
      "lifetime": "Until removed",
      "vocabulary": "role",
      "refs": [
        "role:repo-read",
        "role:repo-triage",
        "role:repo-write",
        "role:repo-maintain",
        "role:repo-admin"
      ]
    },
    {
      "id": "custom-repo-role",
      "mechanism": "Custom repository role: an inherited role plus additional permissions",
      "plane": "organization",
      "grantedBy": "Organization owner defines it, repository admin assigns it",
      "boundary": "One repository per assignment",
      "lifetime": "Until removed",
      "vocabulary": "role",
      "refs": [
        "page:/tier/roles"
      ]
    },
    {
      "id": "outside-collaborator",
      "mechanism": "Outside collaborator",
      "plane": "organization",
      "grantedBy": "Repository admin, if org policy allows it",
      "boundary": "The named repositories only, with any repository role up to Admin",
      "lifetime": "Until removed by hand. SSO is not required for outside collaborators, so IdP offboarding and SCIM do not remove them",
      "vocabulary": "role",
      "refs": [
        "key:outside-collaborator",
        "role:repo-read",
        "role:repo-write",
        "role:repo-admin"
      ]
    },
    {
      "id": "trigger-borrowed",
      "mechanism": "Trigger-borrowed access: a fork pull request or issue comment that starts a workflow",
      "plane": "workload",
      "grantedBy": "Whoever wrote the workflow and chose its trigger (pull_request_target, issue_comment, workflow_run)",
      "boundary": "What that workflow's GITHUB_TOKEN, secrets and runner can reach, used by someone who holds no role at all",
      "lifetime": "One workflow run",
      "vocabulary": "resource-permission",
      "refs": [
        "page:/attack-paths",
        "permission:contents:write",
        "permission:secrets:read"
      ]
    },
    {
      "id": "special-grants",
      "mechanism": "Special grants: ruleset bypass actor, environment required reviewer, code owner",
      "plane": "organization",
      "grantedBy": "Repository admin or org owner; anyone who can edit CODEOWNERS",
      "boundary": "The rules, environments or paths they cover",
      "lifetime": "Until removed",
      "vocabulary": "role",
      "refs": [
        "key:ruleset-bypass",
        "key:environment-reviewer",
        "key:codeowners"
      ]
    },
    {
      "id": "classic-pat",
      "mechanism": "Classic personal access token",
      "plane": "user",
      "grantedBy": "The user",
      "boundary": "Every organization the user belongs to",
      "lifetime": "Expiry optional",
      "vocabulary": "oauth-scope",
      "refs": [
        "scope:repo",
        "scope:workflow",
        "scope:admin:org"
      ]
    },
    {
      "id": "fine-grained-pat",
      "mechanism": "Fine-grained personal access token",
      "plane": "user",
      "grantedBy": "The user, plus org approval when required",
      "boundary": "One resource owner, selected repositories",
      "lifetime": "Expiry required",
      "vocabulary": "resource-permission",
      "refs": [
        "permission:contents:write",
        "permission:administration:write",
        "permission:secrets:write"
      ]
    },
    {
      "id": "oauth-app",
      "mechanism": "OAuth App authorization",
      "plane": "application",
      "grantedBy": "User consent, plus org approval when OAuth access is restricted",
      "boundary": "Every organization the user lets it reach",
      "lifetime": "Usually no expiry",
      "vocabulary": "oauth-scope",
      "refs": [
        "scope:repo",
        "scope:workflow",
        "scope:admin:org"
      ]
    },
    {
      "id": "github-app-installation",
      "mechanism": "GitHub App installation",
      "plane": "application",
      "grantedBy": "Org owner, App manager, or repo admin for selected repos",
      "boundary": "All repositories or a selection",
      "lifetime": "1-hour tokens; the private key never expires",
      "vocabulary": "resource-permission",
      "refs": [
        "page:/tier/tokens#token-types",
        "key:app-private-key",
        "permission:workflows:write"
      ]
    },
    {
      "id": "github-app-user-token",
      "mechanism": "GitHub App user access token",
      "plane": "application",
      "grantedBy": "A user signing in to the App",
      "boundary": "The App's permissions and the user's own access, whichever is smaller",
      "lifetime": "8 hours, refreshable for 6 months",
      "vocabulary": "resource-permission",
      "refs": [
        "page:/tier/tokens#token-types"
      ]
    },
    {
      "id": "github-token",
      "mechanism": "GITHUB_TOKEN",
      "plane": "workload",
      "grantedBy": "The workflow's permissions block, or the org or repo default",
      "boundary": "One repository, one job",
      "lifetime": "The job's duration",
      "vocabulary": "resource-permission",
      "refs": [
        "page:/tier/tokens#token-types",
        "permission:contents:write"
      ]
    },
    {
      "id": "oidc",
      "mechanism": "OIDC token from id-token: write",
      "plane": "workload",
      "grantedBy": "The workflow file, and the cloud trust policy that accepts it",
      "boundary": "Whatever the cloud role allows",
      "lifetime": "Minutes",
      "vocabulary": "resource-permission",
      "refs": [
        "permission:id_token:write"
      ]
    },
    {
      "id": "self-hosted-runner",
      "mechanism": "Self-hosted runner and runner group access",
      "plane": "workload",
      "grantedBy": "Repo admin, org owner or CI/CD admin",
      "boundary": "The repositories and workflows allowed to use the runner",
      "lifetime": "Until removed",
      "vocabulary": "setting",
      "refs": [
        "permission:organization_self_hosted_runners:write"
      ]
    },
    {
      "id": "ai-agent",
      "mechanism": "Copilot cloud agent, MCP servers and AI steps in workflows",
      "plane": "workload",
      "grantedBy": "A user assigning the task, within org and enterprise policy",
      "boundary": "Whatever token and tools it is given",
      "lifetime": "One task or one job",
      "vocabulary": "resource-permission",
      "refs": [
        "key:ai-agent"
      ]
    },
    {
      "id": "ssh-key",
      "mechanism": "SSH key on a user account",
      "plane": "user",
      "grantedBy": "The user",
      "boundary": "All the user's Git access",
      "lifetime": "No expiry",
      "vocabulary": "key",
      "refs": [
        "key:ssh-key"
      ]
    },
    {
      "id": "ssh-ca",
      "mechanism": "SSH certificate authority",
      "plane": "organization",
      "grantedBy": "Organization or enterprise owner",
      "boundary": "Git access as any member the certificate names",
      "lifetime": "Certificate validity",
      "vocabulary": "key",
      "refs": [
        "key:ssh-ca-key"
      ]
    },
    {
      "id": "deploy-key",
      "mechanism": "Deploy key",
      "plane": "organization",
      "grantedBy": "Repository admin",
      "boundary": "One repository, read-only or read-write",
      "lifetime": "No expiry",
      "vocabulary": "key",
      "refs": [
        "key:deploy-key"
      ]
    }
  ],
  "crosswalk": [
    {
      "capability": "Read code",
      "classic": "repo (public_repo for public repos)",
      "fineGrained": "Contents: read",
      "githubToken": "contents: read",
      "role": "Read",
      "refs": [
        "scope:repo",
        "permission:contents:read",
        "role:repo-read"
      ]
    },
    {
      "capability": "Push code",
      "classic": "repo",
      "fineGrained": "Contents: write",
      "githubToken": "contents: write",
      "role": "Write",
      "refs": [
        "permission:contents:write",
        "role:repo-write"
      ]
    },
    {
      "capability": "Change files under .github/workflows",
      "classic": "workflow",
      "fineGrained": "Workflows: write",
      "githubToken": "–",
      "role": "Write",
      "note": "GITHUB_TOKEN has no workflows permission, so a workflow cannot rewrite workflow files with its own token.",
      "refs": [
        "scope:workflow",
        "permission:workflows:write"
      ]
    },
    {
      "capability": "Start or re-run workflows",
      "classic": "repo",
      "fineGrained": "Actions: write",
      "githubToken": "actions: write",
      "role": "Write",
      "refs": [
        "permission:actions:write"
      ]
    },
    {
      "capability": "Open, approve and merge pull requests",
      "classic": "repo",
      "fineGrained": "Pull requests: write",
      "githubToken": "pull-requests: write",
      "role": "Write",
      "note": "GITHUB_TOKEN can only approve when the org or repo allows Actions to create and approve pull requests.",
      "refs": [
        "permission:pull_requests:write"
      ]
    },
    {
      "capability": "Change repository settings and branch protection",
      "classic": "repo, as an admin",
      "fineGrained": "Administration: write",
      "githubToken": "–",
      "role": "Admin",
      "refs": [
        "permission:administration:write",
        "role:repo-admin"
      ]
    },
    {
      "capability": "Delete a repository or change its visibility",
      "classic": "delete_repo (delete); repo, as an admin (visibility)",
      "fineGrained": "Administration: write",
      "githubToken": "–",
      "role": "Admin, if org policy lets admins do it",
      "refs": [
        "scope:delete_repo",
        "role:repo-admin"
      ]
    },
    {
      "capability": "Manage Actions secrets",
      "classic": "repo (repository); admin:org (organization)",
      "fineGrained": "Secrets: write",
      "githubToken": "–",
      "role": "Admin, CI/CD admin, or a custom role with secrets permissions",
      "refs": [
        "permission:secrets:write",
        "role:org-cicd-admin",
        "page:/tier/roles"
      ]
    },
    {
      "capability": "Register self-hosted runners",
      "classic": "repo (repository); admin:org (organization); manage_runners:enterprise",
      "fineGrained": "Administration: write (repository); Self-hosted runners: write (organization)",
      "githubToken": "–",
      "role": "Repository admin, org owner, CI/CD admin",
      "refs": [
        "permission:organization_self_hosted_runners:write",
        "scope:manage_runners:enterprise",
        "role:org-cicd-admin"
      ]
    },
    {
      "capability": "Publish packages",
      "classic": "write:packages",
      "fineGrained": "– (GitHub Packages only accepts classic PATs)",
      "githubToken": "packages: write",
      "role": "Write",
      "note": "One of the few places where a fine-grained PAT is not an option, so teams fall back to long-lived classic tokens.",
      "refs": [
        "scope:write:packages",
        "permission:packages:write"
      ]
    },
    {
      "capability": "Get cloud credentials through OIDC",
      "classic": "–",
      "fineGrained": "–",
      "githubToken": "id-token: write",
      "role": "Anyone who can change a workflow on a branch the cloud trust policy accepts",
      "refs": [
        "permission:id_token:write"
      ]
    },
    {
      "capability": "Manage organization members and teams",
      "classic": "admin:org",
      "fineGrained": "Members: write (organization)",
      "githubToken": "–",
      "role": "Org owner; team maintainer for their own team",
      "refs": [
        "scope:admin:org",
        "role:org-owner",
        "role:team-maintainer"
      ]
    },
    {
      "capability": "Manage organization webhooks",
      "classic": "admin:org_hook",
      "fineGrained": "Webhooks: write (organization)",
      "githubToken": "–",
      "role": "Org owner, or a custom organization role",
      "refs": [
        "permission:organization_hooks:write"
      ]
    },
    {
      "capability": "Install or change GitHub Apps",
      "classic": "–",
      "fineGrained": "–",
      "githubToken": "–",
      "role": "Org owner, App manager; repo admins for Apps that ask for no org or Administration permission, unless the org turns that off",
      "note": "Installing an App is done in the browser, not with a token scope, so only roles control it.",
      "refs": [
        "role:org-app-manager",
        "role:enterprise-app-manager"
      ]
    },
    {
      "capability": "Push past branch rules",
      "classic": "– (follows the user)",
      "fineGrained": "– (follows the user or App)",
      "githubToken": "–",
      "role": "Ruleset bypass actor",
      "refs": [
        "key:ruleset-bypass"
      ]
    },
    {
      "capability": "Enterprise administration",
      "classic": "admin:enterprise",
      "fineGrained": "Limited; check GitHub's list before relying on it",
      "githubToken": "–",
      "role": "Enterprise owner",
      "refs": [
        "scope:admin:enterprise",
        "role:enterprise-owner"
      ]
    }
  ]
}
