wif.tax

Chainguard

A Chainguard registry and platform API

Workloads reach cgr.dev and the Chainguard API by trading an OIDC token from any issuer you register (GitHub, GitLab, AWS, Kubernetes, CircleCI, your own) for a token valid up to an hour, matched on issuer, subject, audience and custom claims. Pull tokens remain for clients that cannot do OIDC and always expire within a year; the free Catalog Starter plan gets only those. Outbound, Chainguard signs its webhooks with OIDC and is the OIDC issuer for Checks into AWS, GCP and Azure.

Docs: edu.chainguard.dev · Checked 2026-10-22 · source YAML · dispute via PR

Inbound Arequires Paid plans (Catalog Starter organizations cannot create assumable identities)

How a workload running anywhere authenticates to this product.

Assumable identity: a Chainguard identity with a claim_match on issuer, subject, audience and custom claims (exact or regex), bound to a role. A workload presents its own OIDC token to issuer.enforce.dev/sts/exchange (or chainctl auth login --identity) and gets a Chainguard token valid up to an hour for the API and registry. Static pull tokens remain for clients that cannot run OIDC.

Issuers
any

Bring-your-own-issuer: any OIDC issuer can be named (exact or issuer_pattern), with exact or regex subject, audience and custom-claim matching; documented examples cover GitHub Actions, GitLab, Bitbucket, Buildkite, CircleCI, Jenkins, Keycloak, Entra ID, Kubernetes service account tokens and AWS IAM outbound identity federation (plus a legacy AWS GetCallerIdentity identity type). Issuers that are not reachable from the internet can register their JWKS as a "static" identity, capped at 30 days. The same exchanged token works for the control-plane API (console-api.enforce.dev), Terraform and cgr.dev, so no API function requires a static secret; per the weakest-path rule this keeps A. Pull tokens are still the documented path for clients that cannot run a token exchange (kubelet imagePullSecrets, ECR pull-through cache in Secrets Manager, Artifactory, Nexus, Cloudsmith mirrors). That is a limitation of those clients, not of Chainguard's API, but it means many real registry-mirror setups hold a static pull token; the hygiene below grades it. Tier gate: the free Catalog Starter plan cannot create assumable identities, so on that plan inbound is pull-token only (would be D).

Outbound A

How this product reaches into your cloud, repos, or other SaaS.

Chainguard reaches customer systems as an OIDC issuer or GitHub App, never with a customer secret: CloudEvents webhooks carry an issuer.enforce.dev OIDC token for the receiver to verify; Chainguard Checks gets short-lived AWS, GCP and Azure credentials through IAM OIDC providers, workload identity pools and Entra federated credentials that trust issuer.enforce.dev; Guardener and Checks act on GitHub through the Chainguard GitHub App.

Checked: CloudEvents subscriptions (registry push/pull and IAM events to a webhook you run; Chainguard signs each delivery with an OIDC JWT, so the receiver holds no shared secret); the image-mirroring examples (copy to Artifact Registry/ECR are customer-run services triggered by events, which pull from cgr.dev with an assumable identity and push with their own cloud identity; Chainguard never holds registry credentials); registry pull-through guides for ECR, Artifact Registry, Artifactory, Nexus, Harbor and Cloudsmith (all pull direction, so the pull token they store is an inbound credential, covered above); Chainguard Checks workload identities into AWS, GCP, Azure (keyless, Chainguard as OIDC issuer, trust pinned on the exact subject); Guardener and Checks via the Chainguard GitHub App; account associations for AWS, GCP and Azure (account ID, project, tenant/client IDs only, no secret; little current documentation of what uses them). Chainguard Workspaces sessions forward the developer's own laptop credentials (gcloud ADC, GitHub, Claude) and are human dev environments, not graded here. No integration found that asks for a static cloud key.

Hygiene for any static credential the product still issues

✓ Expiry enforceable
Pull tokens default to 30 days and the platform caps them at one year, so a never-expiring pull token cannot exist (a platform-enforced max lifetime passes, as on temporal). Static-JWKS identities are capped at 30 days. No org policy for a shorter cap is documented. The Console's "Custom" expiration date picker was not verified to enforce the same cap.
  • edu.chainguard.dev checked 2026-10-22
    “The maximum valid value is `8760h` (equivalent to 365 days), Valid unit strings range from nanoseconds to hours and are `ns`, `us`, `ms`, `s`, `m`, and `h`, for example `--ttl=24h`.”
  • edu.chainguard.dev checked 2026-10-22
    “Maximum value is 8760h or one year. (default 720h0m0s)”
  • edu.chainguard.dev checked 2026-10-22
    “Defaults to / Maximum of 30 days after creation time.”
✓ Rotation via API
Pull tokens are created with chainctl auth pull-token create and revoked by deleting their identity (chainctl iam identities delete, or the /iam/v1/identities/{id} API), all scriptable from an assumable identity. On Catalog Starter the limited_owner role cannot delete identities; you delete the token's role binding instead.
✓ Scoped keys
Each pull token is its own machine identity limited to pulling one repository type, under an organization or folder you choose (--parent), not a copy of the creator's access.
  • edu.chainguard.dev checked 2026-10-22
    “Pulls authenticated in this way are associated with a Chainguard identity, which is associated with the organization selected when the pull token was created.”
  • edu.chainguard.dev checked 2026-10-22
    “**Access** sets what the token can pull: **Container Images**, **Packages**, **Java Libraries**, **JavaScript Libraries**, or **Python Libraries**.”
  • edu.chainguard.dev checked 2026-10-22
    “For example, CI systems should have a role like `registry.pull`, not `editor`.”
✗ Last-used visible
The Identity API object carries lastSeen ("last federated"), but nothing documents whether a registry login with a pull token updates it, and the Console's Access Tokens list shows no last-used column. Judged a fail as not publicly verifiable; a documented last-used for pull tokens would flip it.
  • edu.chainguard.dev checked 2026-10-22
    “The **Access Tokens** page lists your selected organization's pull tokens, with each token's **Name**, **Description**, **Access**, **Created** date, and **Expiration** date.”
  • edu.chainguard.dev checked 2026-10-22
    “last_seen is the timestamp for when the identity was last federated.”
✗ Leak revocation
Chainguard does not appear in GitHub's supported secret scanning patterns table, so a leaked pull token is not reported to Chainguard for revocation.

Notes

One card for the Chainguard platform: the registry (cgr.dev, apk.cgr.dev, Libraries indexes) and the console-api.enforce.dev control plane share one IAM model and one STS. Checks, Guardener and Workspaces are newer products on the same IAM; they appear only in outbound. Inbound is A on paid plans and would be D on Catalog Starter (pull tokens only, three of five hygiene checks pass). Hygiene applies to pull tokens. A documented last-used timestamp and GitHub partner revocation for pull tokens would complete hygiene; the grades would not change.

Badge

wif.tax grade A

[![wif.tax grade](https://wif.tax/badge/chainguard-platform.svg)](https://wif.tax/p/chainguard-platform/)