Skip to content

Example: a short-lived Cloudflare API token for a CI job

Goal

A release job edits DNS on one zone with a Cloudflare token that lives 15 minutes, and only the one reviewed workflow at a tag can ask for it.

What you need

  • The minter and a disabled prototype with DNS Write on one zone (see R2 credentials).
  • The workflow runs with permissions: id-token: write.

The policy snippet

Service document:

cloudflare:
  presets:
    dns-example:
      account: main
      prototype: <prototype token id>
      description: DNS edits on example.com for the release job
      lifetime: 15m
      rotation: 5m

Policy: the job as a group, pinned by job_workflow_ref and ref, and the grant (a group: row).

groups:
  all:ci:release:
    matchers:
      - github:
          repository: example-org/example-repo
          ref: refs/tags/v*
          job_workflow_ref: example-org/example-repo/.github/workflows/release.yml@refs/tags/v*
cloudflare:
  grants:
    - group: all:ci:release
      presets: [dns-example]

Never grant to a job that anyone who can push a branch can start; the workflow's display name is not a pin.

The exchange / command

- run: echo "CLOUDFLARE_API_TOKEN=$(sluisctl cloudflare token dns-example --format json | jq -r .token)" >> "$GITHUB_ENV"

A person: sluisctl cloudflare token dns-example prints CLOUDFLARE_API_TOKEN=...; --format json --lifetime 5m prints {"token","expires_on"}.

Verify

The token works against the zone and is refused elsewhere; a run from a branch is refused (exit 4). The audit trail has roster.cloudflare.token.minted with the caller.

Undo

Remove the grant row or the group; delete the preset. A live token expires on its own, or revoke it by id.

Source: the how-to mint short-lived Cloudflare tokens and the sluisctl cloudflare reference. The policy snippet renders with sluisctl policy render (checked against master, 2026-10-09).