From 2fa24694904645bd18ddfd1c877c5af6b2d7a603 Mon Sep 17 00:00:00 2001 From: jgjesdal Date: Thu, 20 Aug 2026 11:54:25 +0200 Subject: [PATCH] ci: publish to crates.io by OIDC instead of a personal API token MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PyPI half of the release already authenticates as this repository; the crates.io half authenticated as whoever created `CARGO_REGISTRY_TOKEN`. That made one person a single point of failure independent of who owns the crate — the token outlives their involvement, or dies with their account, and no amount of adding owners changes it. `crates-io-auth-action` exchanges the run's OIDC identity for a token scoped to this crate and revokes it in its post step, so the credential exists only for the length of the job and there is no secret to rotate. The pairing now matches PyPI's on both sides of the release. Merging this before registering the Trusted Publisher on crates.io breaks the next release at the auth step, so register it first; the setup needs a crate that already has a published version, which is why this follows 0.2.0 rather than shipping with it. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/release.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f757d11..58d10eb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -84,8 +84,20 @@ jobs: needs: [crate, wheels, pypi] if: startsWith(github.ref, 'refs/tags/') && github.repository_owner == 'IntelliStream-DataHub' environment: release + # Job-level permissions replace the workflow's `contents: read` wholesale, so it is repeated + # here alongside the OIDC claim the auth action needs; without it `checkout` has no scope. + permissions: + contents: read + id-token: write steps: - uses: actions/checkout@v6 + # Trades this run's OIDC identity for a crates.io token scoped to this crate and revoked in + # the action's post step, so no long-lived credential exists to leak or to expire. Requires + # a Trusted Publisher registered on the crate naming this repository, workflow and + # environment; crates.io only accepts one on a crate that already has a published version, + # which is why this could not be set up before 0.2.0 went out. + - uses: rust-lang/crates-io-auth-action@v1 + id: auth - run: cargo publish env: - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}