Skip to content

GitHub Action

The kanoniv/auth-action GitHub Action issues delegation tokens for CI/CD pipelines. Every pipeline run gets a scope-confined, time-bounded token - not a long-lived secret.

Quick Start

yaml
- uses: kanoniv/auth-action@v1
  with:
    root_key: ${{ secrets.KANONIV_ROOT_KEY }}
    scopes: deploy.staging,test.run
    ttl: 4h

How It Works

The action runs as a composite step:

  1. Installs kanoniv-auth from PyPI (version configurable)
  2. Loads the root key - either from the root_key secret (direct mode) or via GitHub OIDC token exchange (OIDC mode)
  3. Issues a delegation token with the specified scopes and TTL using delegate()
  4. Verifies the token works by calling verify() on the first scope
  5. Sets KANONIV_TOKEN as an environment variable for all subsequent steps
  6. Exports token and agent_did as step outputs

Subsequent steps can use the token directly:

yaml
- run: kanoniv-auth verify --scope deploy.staging
- run: kanoniv-auth exec --scope deploy.staging -- ./deploy.sh

Inputs

InputRequiredDefaultDescription
root_keyNo*-Base64-encoded root private key
oidcNofalseUse GitHub OIDC for ephemeral root key
api_keyNo-Kanoniv Cloud API key (required if oidc: true)
scopesYes-Comma-separated scopes
ttlNo4hToken time-to-live
versionNo0.1.0kanoniv-auth package version to install
post_auditNofalsePost delegation audit comment on PR

*Either root_key or oidc must be provided.

Outputs

OutputDescription
tokenThe delegation token (base64url-encoded)
agent_didThe delegated agent's did:agent: identifier

The token is also set as the KANONIV_TOKEN environment variable, available to all subsequent steps in the job.

Direct Mode

Use a root key stored as a GitHub secret. This is the simplest setup.

yaml
steps:
  - uses: kanoniv/auth-action@v1
    with:
      root_key: ${{ secrets.KANONIV_ROOT_KEY }}
      scopes: deploy.staging
      ttl: 2h

Generate the root key and base64-encode it:

bash
kanoniv-auth init
cat ~/.kanoniv/root.key | base64
# Add the output as KANONIV_ROOT_KEY secret in GitHub repo settings

INFO

The root key is a JSON file containing the Ed25519 private key. Base64 encoding is required because GitHub secrets are strings.

OIDC Mode

OIDC mode exchanges a GitHub OIDC token for an ephemeral root key from Kanoniv Cloud. No long-lived root key secret needed - the root key is generated per-run and never stored.

yaml
permissions:
  id-token: write  # Required for OIDC

steps:
  - uses: kanoniv/auth-action@v1
    with:
      oidc: "true"
      api_key: ${{ secrets.KANONIV_API_KEY }}
      scopes: deploy.staging,test.run
      ttl: 4h

The OIDC flow:

  1. GitHub issues an OIDC token with audience kanoniv
  2. The action sends this token to https://api.kanoniv.com/v1/auth/oidc/root-key
  3. Kanoniv Cloud verifies the GitHub OIDC token and returns an ephemeral root key
  4. The action uses this root key to issue the delegation token

WARNING

OIDC mode requires a Kanoniv Cloud account. The api_key input authenticates the OIDC token exchange. You must also set permissions: id-token: write in your workflow - without it, the ACTIONS_ID_TOKEN_REQUEST_URL environment variable is not available and the action will fail.

PR Audit Comments

With post_audit: "true", the action posts a comment on pull requests showing the delegation details. This only runs when the triggering event is a pull_request.

yaml
- uses: kanoniv/auth-action@v1
  with:
    root_key: ${{ secrets.KANONIV_ROOT_KEY }}
    scopes: deploy.staging,test.run
    ttl: 4h
    post_audit: "true"

The comment includes:

FieldDescription
Agent DIDThe delegated agent's identifier (truncated)
ScopesComma-separated list of granted scopes
Chain depthNumber of links in the delegation chain
TTLTime remaining on the token

A collapsible "Delegation Chain" section shows the full chain from root to leaf agent.

INFO

The audit comment uses the GITHUB_TOKEN provided by the workflow. No additional permissions are needed beyond the default pull-requests: write permission.

Example: Full Deploy Pipeline

yaml
name: Deploy
on: push

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: kanoniv/auth-action@v1
        with:
          root_key: ${{ secrets.KANONIV_ROOT_KEY }}
          scopes: test.run,deploy.staging
          ttl: 1h

      - run: kanoniv-auth exec --scope test.run -- npm test
      - run: kanoniv-auth exec --scope deploy.staging -- ./deploy.sh staging

Every step is scope-verified. The deploy script cannot access production (no deploy.prod scope). The token expires in 1 hour even if the pipeline hangs.

Example: OIDC with PR Audit

yaml
name: Deploy with Audit
on: pull_request

permissions:
  id-token: write
  pull-requests: write

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: kanoniv/auth-action@v1
        with:
          oidc: "true"
          api_key: ${{ secrets.KANONIV_API_KEY }}
          scopes: test.run,deploy.staging
          ttl: 4h
          post_audit: "true"

      - run: kanoniv-auth exec --scope test.run -- npm test
      - run: kanoniv-auth exec --scope deploy.staging -- ./deploy.sh staging

Scope Design for CI/CD

Scopes are hierarchical. Design them to match your pipeline stages:

ScopeGrants access to
test.runRunning tests
test.run.unitOnly unit tests (covered by test.run)
deploy.stagingStaging deploys
deploy.prodProduction deploys
buildBuild steps
publish.npmnpm package publishing

A token with scopes: test.run,deploy.staging can run any test and deploy to staging, but cannot deploy to production or publish packages.

Next Steps

The identity and delegation layer for AI agents.