GitHub announced immutable subject claims for Actions OIDC tokens on 23 April 2026 and updated the post on 10 June. On 15 July 2026 the new format became the default for new repositories, and for any repository that is renamed or transferred after that date. If your cloud role trusts a sub string that contains only names, a repository on the new format will not match it.
The point of the change is sound. Names can be recycled, so a stranger who registers an abandoned org or repo name could once have produced a token with a subject your cloud account still trusts. IDs cannot be recycled.
What's affected
The old default subject looks like this:
repo:octocat/my-repo:ref:refs/heads/main
The immutable one adds numeric owner and repository IDs, joined with an at sign:
repo:octocat@123456/my-repo@456789:ref:refs/heads/main
GitHub's April post originally used a hyphen as the delimiter. The June editor's note and the current docs use @, so write your policies with @.
Who gets the new format:
- Repositories created after 15 July 2026.
- Repositories renamed or transferred after that date.
- Any repository or organization that opts in through the OIDC settings or the REST API.
Older repositories keep the old format unless they opt in. The feature is for github.com only. GitHub Enterprise Server is not covered. If you customize the sub claim with include_claim_keys, the docs say the owner and repository IDs stay in the repo segment and cannot be removed.
How to check if you're exposed
Start with the failure you would see. A deploy job that assumed a role yesterday now fails at the credentials step with an AssumeRoleWithWebIdentity authorization error. That is a trust policy mismatch, not an outage.
Then look at what your roles trust. In AWS, list the roles that trust GitHub's provider and read the sub condition on each:
aws iam list-roles --query 'Roles[].RoleName' --output text | tr '\t' '\n' | while read r; do
aws iam get-role --role-name "$r" \
--query 'Role.AssumeRolePolicyDocument' --output json \
| grep -q token.actions.githubusercontent.com && echo "$r"
done
For every role it prints, open the trust policy and note the sub value. Names only means it matches the old format. A wildcard such as the following will not match an immutable subject, because the IDs sit between the names and the colon:
repo:octo-org/octo-repo:*
Finally, look up the IDs you will need:
gh api repos/octo-org/octo-repo --jq '.id, .owner.id'
What to do now
- Inventory every role, workload identity pool and federated credential that trusts
token.actions.githubusercontent.com. Include Azure and GCP, not only AWS. - For repositories created after 15 July, write the trust policy in the immutable form. GitHub's AWS example is:
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:octo-org@123456/octo-repo@456789:ref:refs/heads/demo-branch"
- Update the trust policy before you opt an older repository in, or before you rename or transfer it. GitHub's changelog says to get the policy ready first. It does not say whether opting in can be undone, so treat it as one way.
- Prefer exact
subvalues for production roles: a branch, or better, a GitHub environment such asrepo:octo-org@123456/octo-repo@456789:environment:prodwith required reviewers on that environment. - Keep
id-token: writeonly on jobs that need it, and pin the action that exchanges the token. The AWS docs pinaws-actions/configure-aws-credentialsto a commit SHA:
permissions:
id-token: write
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502
with:
role-to-assume: arn:aws:iam::123456789012:role/gha-deploy
aws-region: us-east-1
- If you used a custom claim template, remember that AWS does not support custom OIDC claims. GCP and HashiCorp Vault do, per GitHub's docs, so the right fix depends on the provider.
Why it matters
OIDC from CI is the right replacement for long-lived cloud keys, but the trust policy is now your credential. A loose sub condition is the equivalent of a key that anyone who can create a matching repository name can use. GitHub's AWS docs say to evaluate the sub condition key in any role that trusts GitHub's provider, so an audience-only condition is not enough.
The immutable format closes the name-recycling hole, but it does not fix over-broad wildcards. A role that trusts every branch and pull request in a repository still lets anyone who can open a pull request mint a token, subject to your workflow rules. Scope by environment and require reviewers.
Try it yourself
Pick one production deploy role. Write down its current sub condition, look up the repository and owner IDs with the gh api command above, and write the immutable version next to it. Then ask whether a pull request from a fork could ever satisfy the condition you wrote. If the answer is not an obvious no, tighten it.
Click through it at your own pace, or open it full screen.
Quick check
Your trust policy says repo:octo-org/octo-repo:* and a new repository under the same org was created on 20 July 2026. Will its token match?
No. New repositories use the immutable format, which includes owner and repo IDs after an at sign, so the name-only pattern does not match.
Why can a repository name not be trusted as an identity?
Names can be released and registered again by someone else. Numeric IDs cannot, which is why GitHub added them to the subject.