Set up CI

Let a CI job prove its identity with OpenID Connect (OIDC) and exchange that proof for a short-lived PackageMaze Token. Choose GitHub Actions, CircleCI, or other CI with OIDC. No long-lived PackageMaze secret is needed.

Before you start

#

Have an Organization Admin grant access to the Feed using the provider-specific instructions below. Replace <organization>/<feed> with its slugs and <package-name> with the name you publish. Each Feed and purpose needs its own access rule: install for dependency jobs, publish for uploads, and docker-build for installs inside Docker images.

The examples use npm and a committed package-lock.json. Configure your repository's Feed URLs first using npm and pnpm setup or pip, uv, and Poetry setup. The Feed Configuration page provides commands for your chosen client. Request credentials in each job that uses them, close to the install or upload step; keep Tokens out of logs, caches, and saved artifacts.

"Configure our CI to use PackageMaze OIDC. Check which install and publish paths our provider supports."

Requires a connected agent. See Connect your agent.

GitHub Actions

#

In Feed settings, add a GitHub Actions CI access rule for the repository and purpose. Restrict publishing to the intended workflow, branch or tag, and Environment where applicable. The rule must match the job's signed identity; a workflow permission alone does not grant Feed access.

Install jobs

#

Add these permissions at the job or workflow level, preserving any others your job needs. Then add the following steps after checkout under jobs.<job>.steps. The action installs maze and exchanges GitHub's identity token automatically.

job or workflow permissions
permissions:
  contents: read
  id-token: write
npm install steps — after checkout
- id: packagemaze-token
  uses: packagemaze/setup-maze@v0.0.5
  with:
    feed: <organization>/<feed>
    purpose: install
- uses: actions/setup-node@v6
  with:
    node-version: "24"
    registry-url: "https://pkg.packagemaze.com/<organization>/<feed>/"
- run: npm ci
  env:
    NODE_AUTH_TOKEN: ${{ steps.packagemaze-token.outputs.token }}

GitHub's OIDC permission reference explains id-token: write, including caller permissions for reusable workflows.

Publish jobs

#

Grant both install and publish access. Use an install Token for dependencies, run your build, then request a fresh publish Token for the upload. The Package name is required and must match what you publish. This example replaces the install steps above and uses the same permissions.

For Python uploads, use the PyPI publish recipe with the publish Token. Package and Build pages show the recorded publishing identity.

npm install, build, and publish steps — after checkout
- id: packagemaze-token
  uses: packagemaze/setup-maze@v0.0.5
  with:
    feed: <organization>/<feed>
    purpose: install
- uses: actions/setup-node@v6
  with:
    node-version: "24"
    registry-url: "https://pkg.packagemaze.com/<organization>/<feed>/"
- run: npm ci
  env:
    NODE_AUTH_TOKEN: ${{ steps.packagemaze-token.outputs.token }}
- run: npm run build --if-present
- id: packagemaze-publish-token
  uses: packagemaze/setup-maze@v0.0.5
  with:
    feed: <organization>/<feed>
    purpose: publish
    package: "<package-name>"
- run: npm publish
  env:
    NODE_AUTH_TOKEN: ${{ steps.packagemaze-publish-token.outputs.token }}

pnpm

#

Docker image builds

#

Docker image builds pass the Token through a BuildKit secret so it never lands in image layers. Add a purpose: docker-build access rule, use one packagemaze/setup-maze step per Feed, and mount the generated secret only for the package-client RUN. The full Docker workflow and Dockerfile lines are rendered on the Feed Configuration page.

CircleCI

#

Prepare the job and access rules

#

In Feed settings, add a CircleCI CI access rule with the exact CircleCI organization ID, project ID, and VCS origin (repository address) from the signed identity. Add branch or tag restrictions as needed. Publish and docker-build rules also require at least one CircleCI context ID; attach every required context to the job and configure its access restrictions in CircleCI.

Install a pinned maze release in the job image or an earlier setup step: choose the archive for the runner's OS and architecture, verify it against the release's maze_checksums.txt, and put the extracted binary on PATH. Confirm maze version succeeds. Customer setup currently uses the CLI; a public PackageMaze CircleCI orb is not released.

Make CircleCI's environment CLI available too. When MAZE_OIDC_TOKEN is unset, maze runs this command inside the job to request the correct audience:

identity request performed by maze
circleci run oidc get --claims '{"aud":"https://api.packagemaze.com"}'

Use the organization-scoped issuer; omit --root-issuer. The default CIRCLE_OIDC_TOKEN variables use the CircleCI organization ID as audience, so do not substitute them for this request. If supplying MAZE_OIDC_TOKENyourself, mint it for https://api.packagemaze.com.

Install jobs

#

After checkout and CLI installation, add this Bash run step to the job's steps in .circleci/config.yml. The temporary npm config references the Token only for this step and is removed when the step exits.

CircleCI npm install step
- run:
    name: Install from PackageMaze
    command: |
      set +x
      set -euo pipefail
      MAZE_TOKEN="$(maze auth exchange-oidc \
        --provider circleci --feed "<organization>/<feed>" \
        --purpose install)"
      export MAZE_TOKEN
      auth_dir="$(mktemp -d)"
      trap 'rm -rf "$auth_dir"' EXIT
      umask 077
      export NPM_CONFIG_USERCONFIG="$auth_dir/npmrc"
      cat > "$NPM_CONFIG_USERCONFIG" <<'EOF'
      registry=https://pkg.packagemaze.com/<organization>/<feed>/
      replace-registry-host=npmjs
      //pkg.packagemaze.com/<organization>/<feed>/:_authToken=${MAZE_TOKEN}
      EOF
      npm ci

Publish jobs

#

Run the install step and your build first, then add this separate upload step. Attach the context required by the publish rule in workflows.<workflow>.jobs; the workflow uses its context name, while PackageMaze matches its signed ID.

CircleCI workflow job entry
- publish:
    context: <publish-context-name>
CircleCI npm publish step
- run:
    name: Publish to PackageMaze
    command: |
      set +x
      set -euo pipefail
      MAZE_TOKEN="$(maze auth exchange-oidc \
        --provider circleci --feed "<organization>/<feed>" \
        --purpose publish --package "<package-name>")"
      export MAZE_TOKEN
      auth_dir="$(mktemp -d)"
      trap 'rm -rf "$auth_dir"' EXIT
      umask 077
      export NPM_CONFIG_USERCONFIG="$auth_dir/npmrc"
      cat > "$NPM_CONFIG_USERCONFIG" <<'EOF'
      registry=https://pkg.packagemaze.com/<organization>/<feed>/
      replace-registry-host=npmjs
      //pkg.packagemaze.com/<organization>/<feed>/:_authToken=${MAZE_TOKEN}
      EOF
      npm publish

Other CI with OIDC

#

Use this path to install when your CI provider can issue a signed job identity token with a public HTTPS OIDC discovery document and signing keys. PackageMaze accepts RS256 or ES256 signatures. Token acquisition depends on your provider; follow its documentation to request an identity token inside the running job.

Grant access

#

As an Organization Admin, open Organization Settings → External integrations → Grant access. Configure these values before running the exchange:

  • Issuer URL: the provider's exact token issuer, with public OIDC discovery and signing keys.
  • Audience: https://api.packagemaze.com. Request that same audience from the provider. If its audience is fixed, use that exact value in both the grant and request below and review the app's replay warning.
  • Claim matchers: exact signed claim values that identify your workload. Include at least one platform-assigned tenant, project, or workspace ID; a user-chosen name or email is not sufficient. Add repository or ref constraints when available.
  • Purpose and Feeds: choose install and select only the Feeds this job needs.

Exchange the identity and install

#

Supply the freshly issued identity as PLATFORM_ID_TOKEN in this Bash step's environment. With Node.js, npm, curl, and jq installed, the following exchanges it for a Feed Token and installs dependencies. The JSON field provider: "oidc" explicitly selects configured OIDC; the maze CLI's automatic provider detection is not this path.

other CI — OIDC exchange and npm install
set +x
set -euo pipefail
: "${PLATFORM_ID_TOKEN:?Request an OIDC identity token from your CI provider first}"
export PLATFORM_ID_TOKEN
MAZE_TOKEN="$(
  jq -n '{
    provider: "oidc",
    feed: "<organization>/<feed>",
    purpose: "install",
    audience: "https://api.packagemaze.com",
    oidc_token: env.PLATFORM_ID_TOKEN
  }' |
    curl --fail --silent --show-error \
      https://api.packagemaze.com/v1/auth/workload-token \
      --header 'Content-Type: application/json' \
      --data-binary @- |
    jq -er '.token'
)"
export MAZE_TOKEN
unset PLATFORM_ID_TOKEN
auth_dir="$(mktemp -d)"
trap 'rm -rf "$auth_dir"' EXIT
umask 077
export NPM_CONFIG_USERCONFIG="$auth_dir/npmrc"
cat > "$NPM_CONFIG_USERCONFIG" <<'EOF'
registry=https://pkg.packagemaze.com/<organization>/<feed>/
replace-registry-host=npmjs
//pkg.packagemaze.com/<organization>/<feed>/:_authToken=${MAZE_TOKEN}
EOF
npm ci

Verify the setup

#

Run the job from an identity allowed by its rule, check that Token exchange and installation succeed, and inspect the Feed's Activity. For GitHub Actions and CircleCI, open the Build report to confirm the recorded CI identity. A successful install alone does not prove publish access; verify the upload in your release workflow separately.

If exchange fails, check the Feed, purpose, audience, and identity constraints. For CircleCI, also check attached contexts and run normally: SSH reruns cannot mint PackageMaze Tokens. For other CI, configure the issuer and claim rules first. See Fix a failing install for package-client failures.

Protect existing dependency hashes

#

These checks are optional. Keep your local install commands; adopt stricter CI modes after committing their required lockfiles or hashed requirements. A registry can replace both a package and its advertised hash. Previously reviewed artifact hashes let your client detect that later substitution. New dependency resolution still trusts the source used when those hashes are recorded.

npm, pnpm, Yarn and Bun

#

Commit a current lockfile containing artifact hashes before using these commands. Keep pnpm current; versions before 11.4 could replace checksum mismatches during non-frozen installs. For Yarn modern with a checked-in cache, consider --immutable-cache --check-cache; checking the cache refetches packages and adds network cost.

optional CI commands — choose your client
npm ci
pnpm install --frozen-lockfile
yarn install --frozen-lockfile  # Yarn Classic
yarn install --immutable        # Yarn modern
test -f bun.lock && bun install --frozen-lockfile

Bun needs the explicit file check because its frozen flag can resolve normally when no lockfile exists. Check for bun.lockb instead if your project still uses that format.

Python clients

#

For uv projects, commit a current uv.lock with artifact hashes before using uv sync --locked. With Poetry 2 and a committed lockfile containing file hashes, use test -f poetry.lock && poetry check --lock && poetry sync. Verify the equivalent for your existing Poetry version before changing its workflow.

For pip or uv pip, prepare requirements that pin every transitive dependency with == and record allowed SHA256 hashes for your target platforms first. Ordinary unhashed requirements will fail in this mode.

optional CI commands — requires fully pinned, hashed requirements
pip install --require-hashes -r requirements.txt
uv pip install --require-hashes -r requirements.txt

PDM was not evaluated in the cited study. Review your pinned client's lock and hash behavior before treating its lockfile mode as an equivalent guarantee.

Minimum Age Policy and CI OIDC credentials do not authenticate downloaded bytes. The upstream PyPI attestation policy currently checks advertised provenance presence, without cryptographic verification. See the Trail of Bits analysis, pip hash-checking mode, and Yarn install options.