Rulebearing Playground Coverage Schemas GitHub

ADR-0068: NuGet publishes by trusted publishing, not a stored API key

Context

The release workflow pushed every NuGet package with NUGET_API_KEY, a nuget.org API key stored as a repository secret. A key like that lasts up to a year, works from anywhere, and has to be rotated by hand before it expires. nuget.org now offers trusted publishing: the owner creates a policy naming a GitHub repository and workflow file, and a run of that workflow exchanges its GitHub OIDC token for an API key that lasts one hour. The exchange is done by the NuGet/login action.

The owner created the policy on nuget.org on 2026-10-10:

Field Value
Package owner bencoding
Repository benbahrenburg/rulebearing
Workflow file release.yml
Environment none
Scope push new packages and package versions, glob Rulebearing*

Decision

nuget-publish gets its key from the policy, not from a secret. The job gains id-token: write beside contents: read, runs NuGet/login pinned to commit 8d196754b4036150537f80ac539e15c2f1028841 (v1.2.0) with user: ${{ secrets.NUGET_USER }}, and passes the action's NUGET_API_KEY output to dotnet nuget push. Nothing else in the job changes.

nuget-credentials proves the same path. A manual run, which is always a dry run, makes the same exchange and pushes the Rulebearing 0.0.1 package already on nuget.org with --skip-duplicate. A missing or mismatched policy, or a wrong NUGET_USER, fails the exchange; a key scoped away from Rulebearing fails the push before nuget.org reports the duplicate. So the dry run checks the path a release takes, and publishes nothing.

The NUGET_API_KEY secret is deleted after the first release that publishes through the policy. Until then it stays, unread, as the way back if the policy has to be withdrawn.

Consequences

Alternatives considered