Back to BlogSecurity

GitHub OIDC for AI Agent Guardrails Without CI Secrets

Use GitHub OIDC for the default hosted repository passport. Understand signed decisions, fork behavior and when managed audit adds a scoped secret.

7 min read
by APort

Summary: A developer adding repository guardrails should not need to distribute a broad APort API key to every workflow. The default hosted path in the GitHub Actions integration uses GitHub OIDC to issue or reuse a repository-bound passport and request a signed policy decision. It requires no APort API key in that default workflow.

That statement has a boundary: customer-owned managed audit uses a managed passport and a scoped API key as well as OIDC. This guide follows identity from the GitHub job into the verifier and explains the choice between those paths.

The architecture: GitHub OIDC plus Open Agent Passport

Layer What it establishes What remains to be decided
GitHub OIDC A signed identity assertion for the workflow context Whether that identity is bound to the intended repository passport
Open Agent Passport Agent or workflow owner, capabilities, limits and status Whether this proposed repository action meets policy
Repository policy verification A decision about the submitted repository context Whether branch rules and runtime enforcement apply the result

The flow is useful because a workflow's identity can be checked without distributing an APort API key in the default path. It does not remove the job's GitHub token, runner permissions or downstream deployment credentials. Keep each credential scoped to the operation it serves.

GitHub job requests an OIDC token
             |
             v
APort validates token and resolves repository-bound passport
             |
             v
Action collects diff, commits and workflow evidence
             |
             v
Repository policy verifier returns signed allow/deny decision
             |
             v
Action verifies signature; configured mode determines check result
             |
             v
Required branch rule determines whether merge may proceed

This diagram follows the documented hosted integration. It is an evidence and enforcement flow, not proof that a particular repository has installed the required branch rule. A push-triggered job observes a push that has already landed.

Why not just use a GitHub secret?

A broadly reused static credential can lose the repository context needed to constrain its use. OIDC supplies a short-lived assertion tied to the workflow's GitHub claims. APort must still validate the issuer, audience and repository binding; merely decoding a token is not sufficient.

Managed hosted audit has a separate requirement: the decision belongs to the customer's APort organization. That path adds a scoped API key and managed passport while retaining the repository binding. The distinction is ownership of the hosted audit record, not a claim that an API key alone proves which repository ran the workflow.

What the workflow presents

The Action requests an OIDC token from GitHub with id-token: write. That permission enables identity-token issuance; it does not grant repository content-write permission. The documented workflow also uses contents: read and pull-requests: read for repository evidence.

The token supplies trusted repository and workflow claims. APort binds hosted GitHub passports to this identity and evaluates repository actions using the same OAP verifier model used by connected agent guardrails.

The runner's diff and attribution heuristics have a different trust level. They explain the requested action and can support review, but they must not override a failed trusted OIDC binding.

Issuing an identity is not authorizing the change

The flow has two API calls with different purposes:

  1. POST /api/github/oidc/issue creates or reuses the hosted GitHub passport.
  2. POST /api/verify/policy/code.repository.merge.v1 evaluates the repository action.

The Action verifies the returned hosted decision signature against /.well-known/oap/jwks.json before using it. That supports integrity of the policy result. It does not make runner-provided evidence tamper-resistant or prove that a merge was blocked.

Use the protected-path guide to connect that identity flow to repository review and branch rules.

What the GitHub policy verifies

The integration context mapping describes the fields the Action sends for repository policy. Separate identity claims from collected evidence when reviewing them:

Evidence Why it is useful Trust boundary
Repository, workflow, ref and run identity Bind the workflow to the expected repository Verified OIDC claims and passport binding
Changed files and commits Explain the proposed repository change Runner and GitHub API evidence; check completeness
PR number or push before/after range Locate the change for review Event context and collected comparison
Workflow patches and structural findings Identify permission, trigger and protected-path changes Analysis of available content, not a complete semantic proof
Commit attribution trailers Join a claimed agent or decision reference Corroborate against signed records; text alone is not authentication

code.repository.merge.v1 is the repository verification target. Release authorization is a separate operation with its own policy and integration; a repository check does not automatically authorize package publication. Inspect the policy catalog before claiming coverage for another action.

The free hosted passport is repository-scoped, with documented repo.pr.create, repo.merge and repo.push capabilities. These capabilities constrain what the passport represents. They do not replace GitHub's own write permissions or mean a denied push is rolled back.

Why push events matter

The Action collects the comparison between before and after SHAs for push events, rather than inferring the entire change from the event's abbreviated payload. Missing or truncated files and commits need an explicit incomplete-evidence signal. Unseen content cannot be treated as reviewed content.

Direct-push restrictions still belong in branch protection or rulesets. A failed job after a push can alert the team or stop a later required release step; it cannot prevent the push that triggered it. The protected-path article covers the related workflow findings and trusted-base policy behavior.

Connect runtime and repository controls

A Claude Code hook can check a proposed command before execution. A Cursor integration needs its own coverage check. An MCP tool check constrains server/tool dispatch. Repository verification evaluates the change that reaches GitHub.

These points share an OAP vocabulary but have different timing. A CI-time receipt cannot retroactively become evidence that a local command was authorized before it ran. Join runtime decision references where available and preserve missing evidence as unknown.

Choose the passport owner deliberately

Path Identity and credentials Audit implication
Default hosted OIDC GitHub OIDC; no APort API key in the workflow Uses an APort-owned repository passport
Managed hosted audit OIDC, managed passport ID and scoped APort API key Decisions can belong to the customer's APort organization
Local JSON Trusted-base passport supplied to verification Hosted decision persistence, telemetry and replay lookup are skipped
Evidence-only Repository evidence without a hosted decision No signed hosted authorization result

For managed audit, put the passport ID in a GitHub variable and the API key in GitHub Secrets. The verifier checks both key access to the owner and the token's repository binding. A known passport ID alone is insufficient authority.

For an external fork PR, the Action ignores managed inputs and attempts the no-secret hosted OIDC path. OIDC availability and supported event behavior still matter; consult the integration guide rather than assuming forks inherit same-repository credentials.

Configure customer-owned hosted audit

For same-repository jobs where the managed secret is available, the documented Action step is:

- uses: aporthq/policy-verify-action@v1
  with:
    mode: hosted
    agent-id: ${{ vars.APORT_GITHUB_AGENT_ID }}
    api-key: ${{ secrets.APORT_API_KEY }}

This step belongs inside the complete workflow with its event triggers and permissions. The passport ID is a GitHub variable; the key is a GitHub Secret. Do not paste a key into YAML or use a broadly privileged credential to make a binding error disappear.

Before requiring the check, inspect a signed decision and confirm the owner is the intended organization. Test a mismatched repository or inaccessible passport in a disposable setup. A known ID must not be sufficient to write another owner's audit history.

Decide what happens when hosted verification is unavailable

mode: auto tries hosted verification and, on the default unmanaged adoption path, can fall back to labeled evidence-only reporting. Managed configuration does not silently fall back. mode: hosted fails if it cannot obtain a valid signed decision.

A repository needing blocking enforcement must require the relevant check and set its policy deliberately. The decision-versus-enforcement contract explains why a green report-only job is not evidence of an allowed operation.

Protect my GitHub repo with the documented OIDC workflow, then inspect a hosted decision and the branch rule that consumes its result.

Frequently Asked Questions

Common questions about this topic.

Does APort require a long-lived CI secret?

The default hosted GitHub OIDC path needs no APort API key. Customer-owned managed audit adds a scoped APort API key in GitHub Secrets and a managed passport ID while retaining OIDC binding.

Is issuing a passport the same as allowing a change?

No. The issue endpoint creates or reuses the repository identity. A separate policy verification evaluates the repository action and returns a decision.

Is the GitHub Action runner-tamper-resistant?

No. OIDC proves claims about the workflow, while runner-collected evidence has its own trust boundary. Action-only evidence does not make an untrusted runner authoritative.

Does a push check prevent the push?

No. It runs after the push. Use branch protection or rulesets to restrict direct writes, and treat push evidence as detection and input to later gates.