Skip to content

ci: publish Utxorpc.Spec via NuGet trusted publishing - #212

Merged
scarmuega merged 1 commit into
mainfrom
ci/dotnet-trusted-publishing
Aug 27, 2026
Merged

ci: publish Utxorpc.Spec via NuGet trusted publishing#212
scarmuega merged 1 commit into
mainfrom
ci/dotnet-trusted-publishing

Conversation

@scarmuega

Copy link
Copy Markdown
Member

Migrates the dotnet codegen publish from a long-lived nuget.org API key to trusted publishing (OIDC), matching what publish-node already does for npm in this same workflow.

Changes

.github/workflows/publish-all.ymlpublish-dotnet gains the same permissions block publish-node already carries:

permissions:
  id-token: write
  contents: read

and passes user: ${{ vars.NUGET_USER }} in place of registry-token: ${{ secrets.NUGET_REGISTRY_TOKEN }}.

.github/actions/publish-dotnet/action.yml — swaps the registry-token input for user, adds a NuGet/login@v1 step, and pushes with steps.login.outputs.NUGET_API_KEY.

After this, NUGET_REGISTRY_TOKEN is referenced nowhere in the repo.

Details that shaped the diff

The login step is gated on mode == 'release'. A token exchange is a live call to nuget.org, and since #211 dry-runs deliberately don't contact live registries. Note release.yml invokes publish-all twice — once as dry-run, then as release — so an ungated login would fire on the dry-run pass of every release.

The exchange sits immediately before the push. NuGet's issued keys are valid for one hour and single-use (one OIDC token → one API key), so requesting one earlier in the job risks expiry on a slow pack.

NUGET_USER is a variable, not a secret. It holds the nuget.org profile name (TxPipe), which nuget.org already publishes as the owner of Utxorpc.Spec — so a secret buys no confidentiality while costing verifiability. A wrong value in a variable can be read and checked; in a secret it surfaces only as a failed release.

The permissions propagate. release.yml sets no caller-level permissions, so it's fair to ask whether a job inside a called workflow can request id-token: write. It can, and this repo already proves it: @utxorpc/spec@0.19.2 carries SLSA provenance attestations, which are only produced when npm's OIDC path succeeded from publish-node under exactly this structure.

⚠️ Please confirm the policy's workflow-file field before merging

A trusted publishing policy binds to repository owner + repository + workflow file. For a reusable workflow the OIDC token carries two distinct claims — workflow_ref (the entry workflow, release.yml) and job_workflow_ref (the workflow defining the job, publish-all.yml) — and which one nuget.org validates determines what the policy must name.

I have not been able to establish which from the documentation, and getting it wrong fails the exchange at publish time. The reliable check is the npm trusted publisher already configured for this repo: whatever workflow file that policy names is the claim GitHub is presenting, and the NuGet policy should match it.

Related

  • utxorpc/dotnet-sdk already publishes Utxorpc.Sdk this way (dotnet-sdk#39), verified end-to-end on v1.8.0-alpha.
  • The umbrella contract's §4 still records dotnet auth as an API key with secret NUGET_REGISTRY_TOKEN. It needs amending alongside this; the parity matrix currently marks dotnet-sdk ❌ on "auth = spec" precisely because the SDK moved ahead of this repo.
  • Once merged and exercised, NUGET_REGISTRY_TOKEN can be revoked on nuget.org and deleted from this repo's secrets.

🤖 Generated with Claude Code

@scarmuega
scarmuega merged commit dd11bce into main Aug 27, 2026
7 checks passed
@scarmuega
scarmuega deleted the ci/dotnet-trusted-publishing branch August 27, 2026 12:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant