Skip to main content

github attestor

Fetches the GitHub Actions OIDC ID token, verifies its signature against the GitHub JWKS, and records the decoded claims alongside a small slice of GITHUB_* / RUNNER_* workflow context.

Namegithub
Predicate typehttps://aflock.ai/attestations/github/v0.1
Lifecycleprematerial
Default binary?No
Categoryci-context (primary)
Recommended traceoff — no syscall tracing needed
Auto-attaches when
  • preenv_equals: GITHUB_ACTIONS=true

The facts in this box are generated from the cilock binary's own catalog (cilock tools list). Do not hand-edit — run npm run gen:catalog.

What it captures

The attestor first fails fast with ErrNotGitHub unless GITHUB_ACTIONS=true. It then fetches an ID token from GitHub's token endpoint (audience witness) and delegates JWT parsing + JWKS verification to the embedded jwt attestor. The remaining fields are sampled directly from GITHUB_* / RUNNER_* env vars.

Top-level fields (the json tags on Attestor):

  • jwt — full nested jwt attestor record, including claims (the entire decoded OIDC payload: sub, aud, iss, repository, ref, sha, workflow, event_name, actor, job_workflow_ref, run_id, runner_environment, etc. — whatever GitHub put in the token) and verifiedBy (jwksUrl plus the JWK that validated the signature).
  • ciconfigpathGITHUB_ACTION_PATH.
  • pipelineidGITHUB_RUN_ID.
  • pipelinenameGITHUB_WORKFLOW.
  • pipelineurl<GITHUB_SERVER_URL>/<GITHUB_REPOSITORY>/actions/runs/<GITHUB_RUN_ID> (also emitted as a subject and the sole back-ref).
  • projecturl<GITHUB_SERVER_URL>/<GITHUB_REPOSITORY> (also emitted as a subject).
  • runneridRUNNER_NAME.
  • cihost — declared but not populated by Attest().
  • ciserverurlGITHUB_SERVER_URL.
  • runnerarchRUNNER_ARCH.
  • runnerosRUNNER_OS.

The OIDC claims themselves live under jwt.claims — the attestor does not duplicate them at the top level.

When to use

In any GitHub Actions workflow that grants permissions: id-token: write. The OIDC token is GitHub's signed assertion of who is running, in what repo, at what ref, in what workflow; capturing and verifying it gives downstream policy a CI-platform-rooted identity that is independent of the cilock binary and any signer it uses.

Flags

None. Two undocumented env-var overrides exist for testing:

Env varPurpose
WITNESS_GITHUB_JWKS_URLOverride JWKS endpoint (default https://token.actions.githubusercontent.com/.well-known/jwks).
ACTIONS_ID_TOKEN_REQUEST_URL / ACTIONS_ID_TOKEN_REQUEST_TOKENSet by the GitHub runner; consumed to fetch the ID token.

Audience is hard-coded to witness.

Output shape

{
"jwt": {
"claims": {
"sub": "repo:aflock-ai/rookery:ref:refs/heads/main",
"aud": "witness",
"iss": "https://token.actions.githubusercontent.com",
"repository": "aflock-ai/rookery",
"ref": "refs/heads/main",
"sha": "deadbeef...",
"workflow": "release",
"event_name": "push",
"actor": "colek42",
"job_workflow_ref": "aflock-ai/rookery/.github/workflows/release.yml@refs/heads/main",
"run_id": "1234567890",
"runner_environment": "github-hosted"
},
"verifiedBy": {
"jwksUrl": "https://token.actions.githubusercontent.com/.well-known/jwks",
"jwk": { "kty": "RSA", "kid": "...", "n": "...", "e": "AQAB" }
}
},
"pipelineid": "1234567890",
"pipelinename": "release",
"pipelineurl": "https://github.com/aflock-ai/rookery/actions/runs/1234567890",
"projecturl": "https://github.com/aflock-ai/rookery",
"ciserverurl": "https://github.com",
"runneros": "Linux"
}

Gotchas

  • Hard-fails outside Actions. If GITHUB_ACTIONS != "true" the attestor returns ErrNotGitHub and produces no record.
  • OIDC permission required. Without permissions: id-token: write on the job, ACTIONS_ID_TOKEN_REQUEST_URL / ACTIONS_ID_TOKEN_REQUEST_TOKEN will be unset and fetchToken will fail.
  • Signature is verified, not just decoded. The embedded jwt attestor fetches the JWKS (response capped at 1 MB), parses the signed JWT, validates the signature against the matching kid, and only then populates claims. A bad signature causes Attest() to fail.
  • Audience is fixed. Token requests always pass audience=witness; this is also the value asserted in the aud claim. There is no flag to change it — only the WITNESS_GITHUB_JWKS_URL env var, intended for test stubs.
  • Token response is size-limited. Both the token-endpoint response and the JWKS response are read through a 1 MB io.LimitReader to prevent OOM from a hostile endpoint.
  • cihost is never populated in this version — it's declared in the struct but Attest() does not assign to it.
  • Subjects + back-refs. pipelineurl and projecturl are both subjects; only pipelineurl is a back-ref.

CLI example

Real GitHub Actions OIDC token. The attestor calls ACTIONS_ID_TOKEN_REQUEST_URL with the ACTIONS_ID_TOKEN_REQUEST_TOKEN bearer and verifies the JWT against GitHub's public JWKS.

# In a workflow with `permissions: id-token: write`:
cilock run --step github-validation \
--signer-file-key-path key.pem --outfile attestation.json --workingdir . \
--attestations environment,git,github,github-action \
-- echo "real GH Actions run $GITHUB_RUN_ID"

Validated via .github/workflows/cilock-ci-attestors.yml in the linked repo. Captures the live OIDC token signed by token.actions.githubusercontent.com. See the full real-data example at https://github.com/aflock-ai/attestor-compliance-examples/tree/main/19-github.

See also


This page is generated from the cilock tool catalog. Don't edit it here — the source is attestation/detection/docs/github.doc.md in aflock-ai/rookery. The same catalog powers cilock tools show github in the CLI, so a single edit keeps the terminal and the website in sync.

Edit this page →  ·  Add a new tool →

When you open the pull request: if you are an AI agent, add the ai-agent label. If you are a human, add the human label.