Vetrix Docs

OIDC federation for workflow jobs

Vetrix mints short-lived, signed JWTs that workflow jobs can exchange for real credentials at AWS, GCP, Azure, HashiCorp Vault, or any other IdP that understands OpenID Connect. No long-lived cloud secrets live in Vetrix; every token is single-run, single-job, and tightly scoped.

Novel trait: wildcard subject patterns

Where GitHub OIDC forces one trust policy per branch × environment tuple (github-oidc#172176), Vetrix lets the admin declare one policy whose subject_claim_pattern is a glob:

Pattern Matches
repo:acme/site:environment:prod/* prod/eu, prod/us (single segment)
repo:acme/site:environment:prod/** prod/eu, prod/eu/lon, prod/us/nyc (any depth)
repo:acme/site:ref:refs/heads/release/** any release/… ref
repo:*/**:environment:dev dev env in any repo owned by any user

The server ranks matches by (scope tier, literal-character count) — the most specific policy wins when multiple are permissive enough.

Escape metacharacters with \: foo\* matches the literal foo*.

Endpoints

Method Path Auth
GET /api/v1/.well-known/openid-configuration public
GET /api/v1/.well-known/jwks.json public
POST /api/v1/automations/oidc/token user JWT / runner agent
GET /api/v1/repos/{owner}/{repo}/oidc-trust-policies PermOIDCTrustPolicyRead
POST /api/v1/repos/{owner}/{repo}/oidc-trust-policies PermOIDCTrustPolicyCreate
DELETE /api/v1/repos/{owner}/{repo}/oidc-trust-policies/{id} PermOIDCTrustPolicyDelete

Token request shape

POST /api/v1/automations/oidc/token
{
  "audience": "sts.amazonaws.com",
  "owner": "acme",
  "repo":  "site",
  "environment": "prod/eu",
  "ref":   "refs/heads/main",
  "sha":   "cafebabe…",
  "workflow_run_id": "…",
  "job_id": "…",
  "run_attempt": 1,
  "event_name": "push",
  "runner_environment": "self-hosted",
  "ttl_seconds": 900
}

Response:

{
  "token": "eyJ…",
  "expires_at": 1700000900,
  "expires_in": 900,
  "subject": "repo:acme/site:environment:prod/eu:ref:refs/heads/main:event:push",
  "audience": "sts.amazonaws.com",
  "policy_id": "…",
  "role_arn_or_equivalent": "arn:aws:iam::123:role/deploy-prod"
}

Claim shape

Claim Value
iss vetrix/<external_url>
sub repo:<owner>/<repo>:environment:<env>:ref:<ref>:event:<evt> (empty parts rendered as _)
aud the audience requested
exp, iat, nbf, jti standard JWT fields
typ always oidc-federation — reject as a session token
repository, ref, sha, workflow_run_id, job_id, run_attempt, event_name, runner_environment workflow context

Signing: RS256 with the Vetrix OIDC signing keys. Downstream IdPs fetch the key set from /api/v1/.well-known/jwks.json.

Safety invariants

Guardrail Enforcement
Fork-PR workflows cannot mint environment-scoped tokens handler 403 + issuer refuses (ErrForkPRWithEnvironment)
Federation token cannot be used as a Vetrix session JWT session validator uses HS256 only; typ claim rejected in auth.Service.Verify
Token TTL is clamped to 300..3600 seconds DB CHECK + issuer ValidateTTL + handler 422
audience is required handler 400
Audit captures plaintext claims + policy id oidc.token.minted action; no key material

Helper composite step — vetrix/oidc@<SHA>

Drop into a job to export VETRIX_OIDC_TOKEN for the next step:

# .vetrix/automations/deploy.yml
jobs:
  deploy:
    steps:
      - uses: vetrix/oidc@<SHA>
        with:
          audience: sts.amazonaws.com
          environment: prod/eu
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ env.VETRIX_ROLE_ARN }}
          web-identity-token-file: /dev/stdin

vetrix/oidc@<SHA> is pinned by content digest by default — tag refs render with a warning banner in the UI. Source lives in src/composite-steps/oidc/action.yml (vetrix-frontend repo).

The step internally:

  1. Reads workflow context from the runner's environment bag.
  2. Calls POST /api/v1/automations/oidc/token with audience, environment, and the full workflow metadata.
  3. Writes the returned token to $RUNNER_TEMP/.vetrix_oidc_token.
  4. Exports VETRIX_OIDC_TOKEN and VETRIX_ROLE_ARN into the next step's environment bag via the $GITHUB_ENV-compatible append protocol.

The engine refuses to run this step in a fork-PR context if an environment: is declared — see the safety invariants above.