Trusted Publishing Beyond npm and PyPI - RubyGems, NuGet.org, crates.io, pub.dev, JSR, and Open VSX: Exchanged or Used Directly, What Trust Is Bound To, and What Each Registry Rejects

First Published:
Last Updated:

For a long time, the pipelines used to publish packages from CI systems have been built on a single premise: using long-lived API keys issued by the registry and storing them as CI secrets, then passing them to the publishing job. npm and PyPI have implemented a mechanism called "trusted publishing" that replaces these API keys with ID tokens issued by the CI system's OIDC provider. Similar mechanisms have been introduced in other registries, each at a different time between 2023 and 2026. pub.dev announced its implementation on January 18, 2023; RubyGems.org on December 14, 2023; crates.io on July 11, 2025; NuGet.org on September 22, 2025; and Open VSX on September 30, 2026. JSR already included an example demonstrating publishing using ID tokens from GitHub Actions in its public beta announcement on March 1, 2024.

However, while the underlying concept is the same, the specific implementation varies from registry to registry. RubyGems.org, NuGet.org, crates.io, and Open VSX receive ID tokens from the CI system, validate them, and then issue short-lived credentials in return. The publishing job then uses these short-lived credentials to publish the package. The documentation for pub.dev and JSR outlines procedures for directly using ID tokens from the CI system to authenticate publishing requests. What trust is bound to also differs. RubyGems.org, crates.io, and Open VSX bind trust to individual packages (gems, crates, extensions). NuGet.org binds trust to owners (individuals or organizations). pub.dev, when publishing from GitHub Actions, binds trust to a repository and tag pattern. JSR binds trust to the GitHub repository linked to the package. Lifetimes, accepted CI systems, how the first version is published, and the triggers each registry rejects also differ from registry to registry.

This article outlines the differences among these six registries. It distinguishes between information found in documentation, blog posts, API documentation, a wiki, and repository README files, and values confirmed only in the source code. npm and PyPI are limited to reference rows that point to an existing article. The information presented is based on verification conducted on October 8, 2026 (section 1.2 lists the parts that rest on checks made on October 9, 2026). This article does not rank the registries in terms of security, nor does it provide step-by-step instructions for configuring each registry.

Related articles on this site:

Table of Contents

  1. 1. The Scope of This Article and the Date It Was Verified
  2. 2. The Assumption Table — Registries Outside npm and PyPI
  3. 3. Exchanged or Used Directly — What the Publishing Job Authenticates With
  4. 4. What Trust Is Bound To
  5. 5. Lifetime, Accepted CI, and the First Version
  6. 6. Switches, Refused Triggers, and Attestation
  7. 7. What Differs from npm and PyPI
  8. 8. Where the Sources Disagree and Where No Statement Was Found
  9. 9. Frequently Asked Questions about Trusted Publishing Beyond npm and PyPI
  10. 10. Summary
  11. 11. References

1. The Scope of This Article and the Date It Was Verified

This section first checks which lines of a pipeline carry the assumption this article covers. It then sets out the verification date, the materials read, the terms used in this article, and the topics it does not cover.

1.1 The Assumption That Publishing Needs a Long-Lived API Key

In CI pipelines that publish packages, this assumption is reflected in several lines of code. The CI secret configurations contain API keys issued by the registry. The publishing jobs pass these secrets to the publishing command, either as environment variables or command-line arguments. There is also a process where a person creates a new key and replaces the secret before the existing API key expires.

RubyGems.org's announcement on December 14, 2023, described previous automated publishing processes as requiring the use of API keys with MFA disabled, which were stored as long-lived secrets in GitHub and passed to all CI jobs.

You needed an API key with MFA disabled, stored by GitHub as a long-lived secret, and provided to every CI job.

NuGet.org's announcement on August 3, 2026, stated that NuGet.org API keys are essentially passwords used to publish packages.

A NuGet.org API key is effectively a password for publishing packages.

In some registries, the stored information isn't an API key. According to pub.dev's announcement on January 18, 2023, dart pub publish authenticates with a Google account and stores a refresh token in the developer's configuration file. Some users put pub-credentials.json, which contains that refresh token, in a GitHub Actions secret and published from their workflows.

This assumption is beginning to break down from the registry side. NuGet.org's same announcement states that, starting August 17, 2026, newly created API keys will have a maximum validity period of 30 days. Keys created before that date are scheduled to expire on November 1, 2026.

Starting August 17, 2026, new API keys will be limited to 30 days. All existing API keys created before that date will expire on November 1, 2026.

Even if you migrate lines that use long-lived API keys to a "trusted publishing" configuration, if the old lines remain, the old lines may take effect instead. Open VSX's announcement on September 30, 2026, states that if the --pat argument or the OVSX_PAT environment variable remains, those will take precedence. Until the secrets are removed, the migration is not complete.

When you migrate, remember that a --pat or OVSX_PAT still wins if it's present. Delete the secret, or you haven't actually switched.

The purpose of this article, which outlines the differences between various registries, is to help you identify which lines in your existing pipelines need to be replaced with the appropriate settings for each registry, before making any configuration changes.

1.2 The Date of Verification and Referenced Materials

The information presented in this article was verified on October 8, 2026 (the two pub.dev rows in section 5.2 were checked on October 9, 2026, and the npm and PyPI reference rows in section 7.1 follow the npm and PyPI article, verified on October 9, 2026). The materials reviewed for each registry are as follows:

  • RubyGems.org: RubyGems Guides pages on Trusted Publishing, Managing owners using UI, and RubyGems.org API; RubyGems blog (announcement from December 14, 2023, security advisory from July 22, 2026, and RubyGems 4.1.0.beta1 release announcement from September 9, 2026); the action.yml of rubygems/release-gem (v1).
  • NuGet.org: Microsoft Learn pages on Trusted Publishing on nuget.org and Scoped API keys; .NET Blog (September 22, 2025, and August 3, 2026).
  • crates.io: crates.io page on Trusted Publishing; Rust Blog posts on crates.io development updates (July 11, 2025, and January 21, 2026); RFC 3691; README for rust-lang/crates-io-auth-action repository.
  • pub.dev: Dart documentation page on Automated publishing of packages to pub.dev; Dart Blog (January 18, 2023); source code and CHANGELOG of the dart-lang/pub-dev repository (section 5.2).
  • JSR: JSR Docs pages on Publishing packages, Packages, Scopes, Provenance and trust, API, FAQ, and Quotas and limits; Deno blog (March 1, 2024).
  • Open VSX: Eclipse Foundation blog (September 30, 2026); Trusted Publishing page in the wiki for eclipse-openvsx/openvsx repository (edited on October 1, 2026).
  • OpenSSF: Securing Software Repositories Working Group document on Trusted Publishers for All Package Repositories.
  • npm (references only): Two announcements from GitHub Changelog, dated October 2, 2026 (both updated on October 5, 2026).

Scripts running in the browser render the Trusted Publishing page on crates.io. This article examines the source code that generates that page, specifically the file svelte/src/routes/docs/trusted-publishing/+page.svelte in the rust-lang/crates.io repository (commit 49cabe4).

Section 5.2 lists values that are confirmed only in the source code, alongside the repository, file, and commit information. The values found in the source code reflect the state of the code at the time of the checked commit and may not necessarily match the version currently running in production.

1.3 Terminology Used in This Article

The same term may refer to different entities depending on the registry. This article uses the following terms to tell them apart.

  • trusted publishing: This article uses trusted publishing as a collective term for mechanisms that accept a CI system's OIDC ID token and authorize publishing, regardless of the name each registry uses. RubyGems.org, NuGet.org, crates.io, and Open VSX refer to this as "Trusted Publishing". pub.dev refers to it as "automated publishing". JSR documentation does not assign a specific name, instead describing it as publishing from GitHub Actions (Publishing from GitHub Actions) or OIDC publishing that uses no stored token (Tokenless OIDC publishing). The OpenSSF document refers to "Trusted Publishers".
  • ID Token: A token that the CI system's OIDC provider (IdP) issues at the request of a CI job (in Google Cloud, a service account). GitHub Actions, GitLab CI/CD, and Google Cloud issue these tokens; the registries do not.
  • Short-Lived Credentials: These are credentials issued and returned by the registry in exchange for an ID token. The terminology varies between registries. This article calls them short-lived API tokens for RubyGems.org (the API page calls them RubyGems API keys, and the key in the response is rubygems_api_key), temporary API keys for NuGet.org, short-lived tokens for crates.io, and publishing tokens for Open VSX. This article does not use the same term for ID tokens and short-lived credentials.
  • Exchange and Direct Use: "Exchange" refers to the process where a publishing job provides an ID token to the registry, which then validates it and returns short-lived credentials. "Direct Use" refers to authenticating the publishing request itself using the ID token, that is, using the ID token directly.
  • Registration: This refers to the configuration within a registry that specifies which CI systems and workflows are authorized to publish. RubyGems.org uses the term "trusted publisher", NuGet.org uses "Trusted Publishing" policies, and Open VSX uses "registration" (also referred to as "trusted publisher"). This article calls all of these registrations.
  • Attestation and Provenance: This refers to signed, verifiable records (such as sigstore attestations or SLSA provenance statements) that indicate from which workflow and execution a published version was created. RubyGems.org uses the term "sigstore attestation", while JSR uses "provenance statement".

1.4 Topics Not Covered

The topic of trusted publishing for npm and PyPI is addressed in Publishing npm and PyPI Packages Without Long-Lived Tokens. That article excludes trusted publishing for other registries, such as RubyGems.org. This article focuses solely on registries outside that scope, and limits npm and PyPI to the reference rows in section 7.

The secure use of permissions (including id-token: write) and triggers within GitHub Actions workflows is covered in Hardening GitHub Actions for AWS Deployments. If AWS receives an OIDC ID token from a CI system, AWS IAM Inbound Workload Federation addresses that topic. This article specifically deals with scenarios where package registries receive ID tokens. Signing, attestation verification, and admission control in general are covered in Software Supply Chain Security on AWS.

Installing dependencies (and what runs during installation) is covered in Install-Time Scripts by Package Manager. This article focuses exclusively on the publishing side. The assumption about the length and format of GitHub App installation tokens (including GITHUB_TOKEN) that a CI job receives and passes on is covered in GitHub App Installation Tokens and Token Length and Format Assumptions. The history and timeline of security incidents targeting package registries are detailed in Major Security Vulnerabilities History and Timeline. This article does not cover either the history of incidents or attack methods.

This article does not cover the steps on each registry's settings screens, subscription plans and pricing, or which registry is better.

2. The Assumption Table — Registries Outside npm and PyPI

This section presents the conclusions of this article in the form of a table. This article calls this table the Assumption Table. One row per registry, it lists when and how the assumption that pipelines silently relied on (publishing needs a long-lived API key) changed in that registry, and what the publisher configures when moving to trusted publishing.

2.1 How to Read the Assumption Table

The Assumption Table has six columns.

  • Tool or Service: Enter the name of the registry.
  • Before: Describe what was in place prior to trusted publishing, when publishing from CI.
  • Now: Describe the behavior of trusted publishing as of the verification date. Include any associated conditions.
  • Since: Record the date when trusted publishing began, along with the significance of that date (e.g., announcement date). Use the format <YYYY-MM-DD> (<significance>). For registries that have had trusted publishing from the beginning, record the date of the earliest available source and note that it is the earliest found.
  • What You Must Declare: List the items that the publisher configures. This includes the items registered with the registry and what is written in the workflow.
  • Where the Source Says So: List the sources that support the information in this row.

Only include statements (or summaries) from the sources in the Before, Now, and What You Must Declare columns. If the sources do not state something, the cell says The source does not say. Before writing it, search the full text of the sources the row cites, the pages those sources link to, and the same registry's blog, API documentation, and FAQ for terms naming the row's subject and for token, API key, long-lived, secret, and before. If the sources conflict, present both descriptions, each with its source, without favoring one over the other.

This article presents two tables: one for registries that exchange ID tokens, and another for registries that use ID tokens directly. The reason for this separation is explained in section 3.

2.2 Registries That Exchange the ID Token

Tool or ServiceBeforeNowSinceWhat You Must DeclareWhere the Source Says So
RubyGems.orgPreviously, it was necessary to store an MFA-disabled API key as a long-lived secret on GitHub and pass it to all CI jobs. RubyGems.org's normal API tokens are long-lived.GitHub Actions workflows now exchange an ID token for a short-lived API token from RubyGems.org. The API token can be used to push only to the gems configured to trust that trusted publisher, and only for a short period of time.2023-12-14 (announced)A trusted publisher per gem (repository owner, repository name, workflow filename, and optionally an environment). For a new gem, a pending trusted publisher. Workflow requires id-token: write. Example in Guides: rubygems/release-gem.RubyGems Guides (Trusted Publishing), RubyGems blog (2023-12-14)
NuGet.orgLong-lived API keys were used. The Scoped API keys page describes legacy (non-scoped) API keys with it never expires, and also says that legacy keys not used to push a package for more than 365 days are retired. It gives an example of a key valid for 365 days. According to the 2026-08-03 announcement, new API keys are limited to 30 days from 2026-08-17, and keys created before that date are scheduled to expire on 2026-11-01.Workflows now send an ID token to nuget.org's token exchange endpoint and, if it matches the policy, receive a temporary API key valid for 1 hour. A single ID token can be used to obtain only one temporary API key.2025-09-22 (announced)A Trusted Publishing policy per owner (individual or organization) (GitHub: repository owner, repository, workflow file, optionally an environment) and its scopes. Workflow requires id-token: write. Example in documentation: NuGet/login (using a nuget.org username for user). GitLab uses id_tokens.Microsoft Learn (Trusted Publishing on nuget.org, Scoped API keys), .NET Blog (2025-09-22, 2026-08-03)
crates.ioPreviously, long-lived API tokens were stored in repository secrets.Workflows now exchange an ID token for a short-lived token that expires in 30 minutes. On GitHub Actions, rust-lang/crates-io-auth-action performs the exchange and revokes the token at the end of the job. Support for GitLab CI/CD is in public beta. The first version still requires an API token.2025-07-11 (announced in a crates.io development update)A Trusted Publishing configuration in the crate's settings (GitHub: repository owner, repository name, workflow filename, optionally an environment). Workflow requires id-token: write. Example in documentation: rust-lang/crates-io-auth-action.crates.io Trusted Publishing page, Rust Blog (2025-07-11, 2026-01-21), crates-io-auth-action README
Open VSXCI configurations previously used long-lived personal access tokens (PATs), either using --pat or OVSX_PAT.GitHub Actions workflows now exchange an ID token for a publishing token that is valid for about 5 minutes and can be used for one extension only. The first version is still published with a long-lived personal access token (PAT).2026-09-30 (announced)A namespace owner registers a trusted publisher under Settings > Trusted Publishers (GitHub: owner, repository, workflow file, optionally an environment). A signed Publisher Agreement and a verified namespace are prerequisites. Workflow requires id-token: write. The official example is npx ovsx publish --trusted-publishing (--trusted-publishing is an optional flag that makes the job fail when no ID token is available). The remaining --pat and OVSX_PAT should be removed.Eclipse Foundation blog (2026-09-30), Open VSX wiki (Trusted Publishing)

2.3 Registries That Use the ID Token Directly

Tool or ServiceBeforeNowSinceWhat You Must DeclareWhere the Source Says So
pub.devdart pub publish authenticated with a Google account and stored a refresh token in the development machine's configuration file. Some users put pub-credentials.json, which contains it, in a GitHub Actions secret.Authenticates using GitHub Actions or Google Cloud-signed ID tokens (referred to as temporary OpenID-Connect tokens in the documentation). No description of an exchange step was found in the documentation. Only workflows triggered by tag pushes can be used to publish from GitHub Actions. Automated publishing is only available for existing packages.2023-01-18 (announced)In the package's Admin tab, you must declare either the GitHub repository and tag pattern (including {{version}}) or the email address of a Google Cloud service account. For GitHub Actions, you must include id-token: write and are strongly encouraged to reuse the workflow dart-lang/setup-dart/.github/workflows/publish.yml@v1.Dart documentation (Automated publishing), Dart Blog (2023-01-18)
JSRThe source does not say.The GitHub Actions ID token is passed in the Authorization header with the githuboidc prefix. The permissions associated with this ID token are limited to publishing specific packages and versions via package/publish. Publishing with an ID token is possible only from GitHub Actions; other CI systems publish with a personal access token.2024-03-01 (public beta announced; the earliest source found)Create a package on jsr.io and link it to a GitHub repository in the package settings. In the workflow, include id-token: write and jsr publish (or deno publish).JSR Docs (Publishing packages, API), Deno blog (2024-03-01)

The Before field for the JSR row was set to The source does not say. The public beta announcement from March 1, 2024, already includes an example of publishing using a GitHub Actions ID token. A search of the JSR Docs pages for "Publishing packages", "Packages", "Provenance and trust", "API", "Frequently Asked Questions", and "Quotas and limits", as well as the March 1, 2024 announcement, using the terms token, API key, long-lived, secret, and before, found no description of how packages were published before ID tokens were used. The JSR FAQ, in its answer about where to report bugs, describes the current period as the open beta (During the open beta).

3. Exchanged or Used Directly — What the Publishing Job Authenticates With

This section outlines how publishing jobs are authenticated against the registries. In four of the registries, publishing jobs exchange ID tokens for short-lived credentials. The documentation of two registries describes using the ID token directly for publishing.

Exchanged or Used Directly: What a Registry Does with a CI OIDC ID Token
Exchanged or Used Directly: What a Registry Does with a CI OIDC ID Token

3.1 Four Registries That Exchange the ID Token

RubyGems.org's API documentation lists an endpoint (POST /api/v1/oidc/trusted_publisher/exchange_token) for exchanging ID tokens for short-lived API tokens. The API documentation calls the short-lived API token a RubyGems API key. The documentation states that this endpoint is intended for use by the rubygems/release-gem GitHub Action for trusted publishing.

Exchange an OIDC ID token for a RubyGems API key. This endpoint is intended to be used by the
release-gem GitHub Action for trusted publishing.

An example response on the same page shows that the scopes are limited to push_rubygem, and includes an expiration time (expires_at). The expires_at value in the example response is a date written as an example and does not indicate the lifetime.

NuGet.org's documentation describes the workflow sending an ID token to nuget.org, where nuget.org's token exchange endpoint checks it against the policy and issues a temporary API key. Only one temporary API key can be obtained from a single ID token.

Each short-lived token can only be used once to obtain a single temporary API key—one token, one API key.

Here, the term short-lived token refers to the ID token generated by the CI system. NuGet.org's documentation indicates that the workflow requests an encrypted OIDC token from the CI platform. In the GitLab CI/CD section, the pipeline sends the GitLab ID token to POST /api/v2/token and receives a temporary API key.

On crates.io, for GitHub Actions, the rust-lang/crates-io-auth-action exchanges the ID token and outputs the resulting token. cargo publish receives this token via the CARGO_REGISTRY_TOKEN environment variable. The README for this action states that when the job completes, the action's post step automatically revokes the token.

The action's post step automatically revokes the token when the job completes.

For GitLab CI/CD, crates.io's documentation provides an example script that sends the ID token to https://crates.io/api/v1/trusted_publishing/tokens to receive a short-lived token.

In Open VSX, the ovsx command sends an ID token, along with the namespace and extension name to be published, to the POST /api/-/trusted-publishing/token endpoint. According to the Open VSX wiki, if the details match, the registry will return a short-lived publishing token (referred to as a "short-lived access token" in the wiki), and ovsx uploads the extension with that token without writing it to the token store.

On a match the registry issues a short-lived access token and returns it. ovsx uploads with it, and never writes it to the token store.

3.2 Two Registries That Use the ID Token Directly

The JSR API documentation lists three types of tokens that JSR accepts: short-lived device access tokens granted through an interactive session, long-lived personal access tokens, and OIDC tokens from GitHub Actions. The documentation states that GitHub Actions OIDC tokens are passed with the githuboidc prefix in the Authorization header.

GitHub Actions OIDC tokens are passed in the Authorization header with a githuboidc prefix.

The same page clarifies that OIDC tokens for GitHub Actions are only authorized for a specific set of permissions, limited to package/publish for particular packages and versions.

GitHub Actions OIDC tokens only support the package/publish permission, with a specific package and version specified.

The Publishing packages page of the JSR documentation also mentions using OIDC ID tokens for JSR authentication, as noted in the comment on the id-token: write line within a workflow example.

id-token: write # The OIDC ID token is used for authentication with JSR.

The pub.dev documentation states that automated publishing relies on temporary OpenID Connect tokens signed by either GitHub Actions or Google Cloud IAM.

Instead, authentication relies on temporary OpenID-Connect tokens signed by either GitHub Actions (See OIDC for GitHub Actions) or Google Cloud IAM.

In the example of publishing from Google Cloud Build, an ID token obtained using gcloud auth print-identity-token is passed to dart pub token add https://pub.dev, followed by the execution of dart pub publish --force. In the example of publishing from GitHub Actions, the documentation says that a temporary OIDC token signed by GitHub is created and configured in the dart-lang/setup-dart step. No description of a step that exchanges the ID token for another pub.dev credential was found in the pub.dev documentation. This article places pub.dev on the direct-use side on the basis of these documented steps. While the page description includes the word directly, this article reads it as referring to direct publishing from GitHub Actions to pub.dev, not to the use of an ID token directly.

3.3 The OpenSSF Document Describes Both

The OpenSSF's Securing Software Repositories Working Group document, "Trusted Publishers for All Package Repositories", addresses both exchange and direct use. The document recommends exchanging ID tokens for another token managed by the registry. However, it also states that this step is not mandatory, and that the ID token may be used directly to authenticate the publish request.

This document recommends exchanging the OIDC ID token for a separate repository-managed token, but this step is not required and the OIDC ID token may be used directly for authenticating the publish request.

A later section of the same document takes a stronger stance. It states that package repositories should not use ID tokens directly to authenticate with package publishing APIs, but should exchange them for API tokens managed by the registry.

Package repositories should not use OIDC ID tokens for directly authenticating with package publishing APIs.

The document cites several reasons for recommending this exchange. It allows existing authentication and authorization implementations to be reused as is. It also allows registries to avoid holding, for extended periods, personal information that ID tokens may contain. If the IdP changes token expiration or other policies without notice, the registry is less affected. Tokens managed by the registry can also benefit from external mechanisms such as secret scanning. This article only places the document's two statements side by side and does not judge which design is more secure.

3.4 Table 1 — Exchange Table

This section summarizes what the publishing job authenticates with in a table. This article calls this table the Exchange Table. The Lifetime Stated by the Registry column contains only statements from each registry's documentation, blog posts, API documentation, wiki, and README files. Values from the source code are in section 5.2.

RegistryExchanged or Used DirectlyWhat the Job Publishes WithLifetime Stated by the RegistryWhat It Can PublishWhere the Source Says So
RubyGems.orgExchangedRubyGems.org's short-lived API token (rubygems_api_key)Documentation doesn't specify a number, but indicates it's only usable for a short period. A blog post from 2026-07-22 states it expires within minutes.Only pushing to gems configured to trust the publisher (scope: push_rubygem).RubyGems Guides, RubyGems.org API, RubyGems blog (2026-07-22)
NuGet.orgExchangedNuGet.org's temporary API key1 hourPackages owned by the policy owner. Can be restricted by scope and glob patterns.Microsoft Learn (Trusted Publishing on nuget.org)
crates.ioExchangedcrates.io's short-lived token (CARGO_REGISTRY_TOKEN passed to cargo publish)30 minutes. The GitHub Actions action revokes the token at the end of the job.The documentation describes it as a short-lived token for publishing; no description was found specifying which crates it can be used with (for RFC 3691 and the source code, see sections 8.7 and 5.2).crates.io's Trusted Publishing page, crates-io-auth-action README
Open VSXExchangedOpen VSX's publishing tokenApproximately 5 minutes (the wiki states it expires within minutes, with a default of 5 minutes).Only one registered extension. Publishes as the user who created the registration.Eclipse Foundation blog (2026-09-30), Open VSX wiki
pub.devUsed DirectlyGitHub Actions or Google Cloud-signed ID tokenThe documentation states that the signature of a Google Cloud ID token expires within 1 hour. No description of the lifetime of the GitHub Actions ID token was found.In GitHub Actions, the package version that matches the registered repository and tag pattern. With Google Cloud, anyone who can impersonate the service account can publish new versions of the package.Dart documentation (Automated publishing)
JSRUsed DirectlyGitHub Actions ID token (with the githuboidc prefix)Documentation states it's only valid for a short period.Only package/publish for a specific package and version.JSR Docs (API)

4. What Trust Is Bound To

This section outlines what registrations are bound to. There are three options: by package, by owner, and by repository (pub.dev also accepts a Google Cloud service account). Of the two registries that bind trust to a repository, pub.dev's registration also includes a tag pattern, while JSR uses the GitHub repository linked to the package.

What Each Registry Binds Trust To
What Each Registry Binds Trust To

4.1 Binding to a Package — RubyGems.org, crates.io, and Open VSX

On RubyGems.org, trusted publishers are registered for each gem. RubyGems Guides states that the short-lived API tokens a registered workflow obtains from RubyGems.org can push only to that gem.

Once registered, the release.yml workflow on rubygems/sample-gem will be able to generate short-lived API tokens from RubyGems.org that are scoped to push only to this gem.

The same page also states that a single repository and workflow can be registered with multiple gems, and multiple publishers can be registered for a single gem. For example, the release.yml workflow in the rails/rails repository can be registered with both the rails and activerecord gems. It is also possible to register two workflows, release-linux.yml and release-mac.yml, for a single gem. When calling reusable workflows from another repository, you must specify the repository containing the workflow in the fields Workflow Repository Owner and Workflow Repository Name.

The table outlining the roles in RubyGems Guides indicates that OIDC and Trusted Publishing configurations can only be set by the Owner role, and not by the Maintainer role.

Registration on crates.io is also done on a per-crate basis, and is created on the Trusted Publishing page in the crate's settings. Only an owner of the crate can create one.

On Open VSX, registration is done for each extension, and only one trusted publisher can be registered for an extension at a time. The Open VSX wiki states that workflows that publish multiple extensions require registration for each extension. These registrations can refer to the same workflow.

Each extension can have one trusted publisher at a time. A workflow that publishes several extensions needs a registration per extension, all of which may point at the same workflow.

On Open VSX, the issued tokens act on behalf of the user who created the registration, and that user is displayed as the publisher of the published version. If the user who created the registration is no longer an owner of the namespace, that user's registration is deleted, and any tokens that have already been issued stop working immediately.

4.2 Binding to an Owner — NuGet.org

NuGet.org's Trusted Publishing policy is defined on a per-owner basis. An owner can be an individual user or the organization to which that user belongs. NuGet.org's documentation states that the policy applies to all packages associated with the selected owner.

The policy will apply to all packages owned by the selected owner.

The policy can also include scopes and glob patterns. According to NuGet.org's documentation, the scope determines whether specific actions, such as publishing new packages or releasing new versions of existing packages, are permitted. Glob patterns are used to narrow down the packages to which the policy applies.

Policy Scopes determine whether specific publishing actions, such as publishing new packages or publishing new versions of existing packages, are allowed. The associated glob pattern can be used to target specific packages and define which packages the policy applies to.

When the owner is an organization, the policy may become inactive if the ownership status changes. NuGet.org's documentation indicates that the policy becomes inactive if the user who created it leaves the organization, or if the organization is locked or deleted. If the user is added back to the organization, the policy is automatically reactivated.

4.3 Binding to a Repository — pub.dev (Tag Pattern) and JSR (Linked Repository)

On pub.dev, you register the GitHub repository and tag pattern for a package in the Admin tab. The tag pattern is a string that includes {{version}}. According to the documentation, only workflows triggered by pushing a tag that matches this pattern can publish the package from GitHub Actions (for the source code, see section 5.2). For example, with the pattern v{{version}}, a workflow triggered by pushing the tag v1.2.3 can publish version 1.2.3. When a repository contains multiple packages, the documentation recommends using a different tag pattern for each package.

The pub.dev documentation states that simply registering a repository and tag pattern does not prevent anyone with push access to the repository from publishing a new version.

At this point, anyone with push access to your repository can publish new versions of the package.

The documentation further recommends using tag protection rules and GitHub Actions environments to restrict who can publish. If you require an environment in the pub.dev registration, only workflows with that specific environment name can publish. The documentation notes that, because the environment is included in the ID token, even if a user with push access modifies the workflow files, they cannot bypass the environment's protection rules. When publishing from Google Cloud, you register the service account's email address, and anyone who can impersonate that service account can publish.

In JSR, you link the package to a GitHub repository in the package settings. The JSR "Packages" page states that JSR verifies that the linked GitHub repository is under the control of the package author, preventing authors from linking to repositories they do not own. This same page indicates that linking to a GitHub repository also enables publishing from GitHub Actions using OIDC, without requiring stored tokens. The example on the JSR "Publishing packages" page shows a workflow triggered by a push to the main branch. No setting that limits publishing to specific tags or workflow files was found in the JSR documentation. As described in section 3.2, the permissions available through the ID token are limited to package/publish for specific packages and versions.

4.4 Matching by ID Rather Than by Name

Many registrations are entered using names that humans can read, such as the repository owner or name. However, names can change. It's also possible that after a repository is deleted, someone else will recreate it with the same name. The OpenSSF document points out that registrations based solely on names are vulnerable to this type of manipulation, and describes a countermeasure: looking up the immutable ID from the name at registration time and storing it.

Open VSX stores the numerical IDs of both the repository and owner at the time of registration, and performs matching based on those IDs. Because the workflow filename has no ID, it is matched by name.

Matching uses numeric IDs, not names. When you register, Open VSX looks up the repository and owner names and stores their immutable IDs.

The documentation for NuGet.org states that to ensure policies are tied to the original repository and owner, NuGet.org needs the GitHub repository and owner IDs. The same documentation notes that policies may initially have a provisional validity period of seven days, a situation that primarily occurs with private GitHub repositories.

Sometimes when you create a Trusted Publishing policy, it starts out as temporarily active for 7 days. This usually happens with private GitHub repos.

If no publish happens within 7 days, the policy automatically becomes inactive. The 7-day window can be restarted at any time, even after it has expired. After the first successful publish, the repository and owner IDs in the GitHub ID token are locked into the policy, and the policy becomes permanently active. Regarding this seven-day period, the NuGet.org announcement of September 22, 2025, describes it differently than the documentation (section 8.2).

No description of matching by ID was found in the RubyGems.org and crates.io documentation. However, the source code includes a process that stores the numerical ID of the GitHub owner at the time of registration (section 5.2).

4.5 Table 2 — Binding Table

This table summarizes what each registration is bound to, the accepted CI systems, and how the first version is handled. This article calls this table the Binding Table. Sections 5.3 and 5.4 describe the accepted CI systems and the first version.

RegistryTrust Is Bound ToConfigurations per PackageAccepted CIFirst PublishWhere the Source Says So
RubyGems.orgTrusted publisher per gemMultiple registrations can be made for a single gem. No upper limit was found.Documentation only describes GitHub Actions.A pending trusted publisher can also publish the first version.RubyGems Guides
NuGet.orgA policy per owner (individual or organization), narrowed by scopes and glob patterns.The owner's policy applies to its packages. No upper limit was found.GitHub Actions, GitLab CI/CDScope determines whether a new package can be published.Microsoft Learn (Trusted Publishing on nuget.org)
crates.ioRegistration per crateNo mention of limits was found in the documentation.GitHub Actions. GitLab CI/CD is in public beta, currently only for GitLab.com.An API token is required for the first version.crates.io Trusted Publishing page, Rust Blog (2026-01-21)
Open VSXRegistration per extensionOnly one can be registered simultaneously.open-vsx.org only supports GitHub Actions (an announcement indicates plans to allow configuration of GitLab instances).The first version is published with a long-lived PAT.Eclipse Foundation blog (2026-09-30), Open VSX wiki
pub.devGitHub repository and tag patterns, or Google Cloud service accountsNo upper limit was found. Instructions describe entering a single repository and a single tag pattern.GitHub Actions, Google Cloud Build, environments that can impersonate Google Cloud service accounts.Only existing packages. The first version is published using dart pub publish, not automated publishing.Dart documentation (Automated publishing)
JSRGitHub repository linked to the packageNo upper limit was found. Documentation describes linking the package to a single GitHub repository.Publishing with an ID token is supported only from GitHub Actions.Create a package on jsr.io and link it to a repository.JSR Docs (Publishing packages, Packages)

5. Lifetime, Accepted CI, and the First Version

This section compares the lifetimes of short-lived credentials and ID tokens, the CI systems each registry accepts, and how the first version is published. For lifetimes, documentation statements and source-code values are kept apart.

5.1 Lifetimes the Documentation States

Three registries give a number for the lifetime of the short-lived credential issued in the exchange: NuGet.org and crates.io in their documentation, and Open VSX in its announcement and wiki. NuGet.org's temporary API keys are valid for one hour, and the documentation recommends requesting the key shortly before publishing.

NuGet’s temporary API keys are valid for 1 hour, so your workflow should request the key shortly before publishing.

crates.io's documentation states that tokens expire automatically after 30 minutes.

Tokens automatically expire after 30 minutes

An Open VSX announcement from September 30, 2026, states that the publishing token lasts about 5 minutes. Open VSX's wiki states that these tokens expire within minutes, with a default validity of 5 minutes.

a publishing token that lasts about five minutes and works for that one extension only

RubyGems Guides simply describe short-lived API tokens as credentials that can only be used for a limited time.

The API token can be used only to push to the gems that are configured to trust the publisher, and only for a short period of time.

A security advisory from RubyGems' blog, dated July 22, 2026, states that OIDC and trusted publisher keys (the short-lived API tokens discussed in this article) expire naturally within minutes. The corresponding source code value is shown in section 5.2.

Short-lived OIDC and trusted-publisher keys were also left alone, since they come from a separate token exchange, expire on their own within minutes, and can’t leak through this path.

In the two registries that use the ID token directly, the job publishes with the ID token the CI system issued. JSR's API page describes GitHub Actions' OIDC tokens as credentials valid for a short period. pub.dev's documentation, regarding Google Cloud Build examples, states that the signature of the ID tokens created by gcloud auth print-identity-token expires within one hour. No description of the lifetime of the GitHub Actions ID token was found in the pub.dev documentation.

A short lifetime does not make exposing a credential acceptable. The OpenSSF document states that both ID tokens and short-lived credentials issued by registries are sensitive information, and if exposed, can be misused until they expire. Open VSX's wiki also states that while ID tokens and short-lived credentials issued by registries can be used for publication until they expire, they should not be displayed and caution should be exercised when handling CI logs.

5.2 Values Confirmed Only in the Source Code

Some values and behavior for which no description was found in the documentation could be confirmed in the source code. The values presented in the following table are based on the source code of the commits that were examined. The production service may not be running on the exact same version as those commits. This article does not put these values in the Assumption Table or in Tables 1 to 3.

RegistryValue in the Source CodeRepository and FileCommit (Checked)What the Documentation Says
RubyGems.orgexpires_at of the short-lived API token issued in the exchange is set 15 minutes ahead.rubygems/rubygems.org in app/controllers/api/v1/oidc/trusted_publisher_controller.rbb6bc89a (Checked on 2026-10-08)The documentation does not specify a number. The Guides mention a short period, and a blog post from 2026-07-22 says within minutes.
RubyGems.orgexpires_at of a pending trusted publisher is set 12 hours ahead.Same repository in app/controllers/oidc/pending_trusted_publishers_controller.rbb6bc89a (Checked on 2026-10-08)No mention of an expiration time was found.
RubyGems.orgThere is only one trusted publisher type, the one for GitHub Actions (OIDC::TrustedPublisher::GitHubAction).Same repository in app/models/oidc/provider.rbb6bc89a (Checked on 2026-10-08)The documentation only describes GitHub Actions.
RubyGems.orgAt the time of registration, the numerical ID of the owner from the GitHub API (repository_owner_id) is retrieved and stored, and is included in the matching criteria.Same repository in app/models/oidc/trusted_publisher/github_action.rbb6bc89a (Checked on 2026-10-08)No mention was found.
crates.ioUp to 5 registrations per crate, for GitHub and for GitLab separately (MAX_CONFIGS_PER_CRATE is defined once in each of two files).rust-lang/crates.io in src/controllers/trustpub/github_configs/create.rs and src/controllers/trustpub/gitlab_configs/create.rs49cabe4 (Checked on 2026-10-08)No mention of a number was found.
crates.ioFor GitHub registrations, the numerical ID of the owner (repository_owner_id) is stored.rust-lang/crates.io in src/controllers/trustpub/github_configs/create.rs49cabe4 (Checked on 2026-10-08)No mention was found.
crates.ioThe token issued in the exchange records the IDs of the crates whose registrations matched (crate_ids).Same repository in src/controllers/trustpub/tokens/exchange/mod.rs49cabe4 (Checked on 2026-10-08)No mention was found regarding which crates can be used.
pub.devThe package admin page has a Manual publishing setting (Enable manual publishing, enabled by default). When it is disabled, publishing with personal credentials (pub login) is rejected. The service CHANGELOG lists Enabled manual publishing settings on package admin UI. for the 20251028t102000-all release.dart-lang/pub-dev in app/lib/frontend/templates/views/pkg/admin_page.dart, app/lib/package/backend.dart, and CHANGELOG.mdc5db4a7 (Checked on 2026-10-09)No mention was found.
pub.devPublishing from workflow_dispatch events can also be enabled in the package settings (disabled by default). The ref must still be a tag that matches the tag pattern.Same repository in app/lib/frontend/templates/views/pkg/admin_page.dart and app/lib/package/backend.dartc5db4a7 (Checked on 2026-10-09)The documentation describes only workflows triggered by pushing a tag.

The crates.io 30-minute lifetime and the code that refuses pull_request_target and workflow_run are also in the source code at the same crates.io commit (src/controllers/trustpub/tokens/exchange/mod.rs). Because the 30-minute lifetime appears in the crates.io documentation and the refusal of the two triggers appears in the January 21, 2026 blog post, this article cites those.

5.3 Accepted CI Systems and Identity Providers

The accepted CI systems vary depending on the registry.

  • RubyGems.org: The documentation for RubyGems Guides only describes using GitHub Actions. While the section explaining the underlying mechanisms provides a general description using GitHub Actions as an example, no instructions for other CI systems were found. An announcement dated December 14, 2023, mentioned plans to support other trusted publishing platforms in the future.
  • NuGet.org: The documentation includes instructions for both GitHub Actions and GitLab CI/CD. The GitLab CI/CD section states that the namespace_path and project_path claims must match the policy.
  • crates.io: The documentation provides instructions for GitHub Actions and GitLab CI/CD, noting that support for GitLab CI/CD is currently in public beta. A blog post dated January 21, 2026, states that support for GitLab currently only works with GitLab.com and does not yet support self-hosted GitLab instances.
  • pub.dev: The documentation lists GitHub Actions, Google Cloud Build, and publishing from locations that can utilize Google Cloud service accounts. Service accounts can be used through Workload Identity Federation or by authenticating with exported service account keys. The documentation notes that exported keys are long-lived secrets and pose a larger risk if leaked, and suggests using Workload Identity Federation if possible.
  • JSR: Publishing with an ID token is supported only from GitHub Actions; other CI systems publish with personal access tokens.

Tokenless OIDC publishing is only available from GitHub Actions, but token-based publishing works everywhere.

  • Open VSX: An announcement dated September 30, 2026, states that currently only GitHub Actions is supported. The same announcement, under the heading Coming:, indicates plans to allow any GitLab instance, including self-hosted ones, to be configured as a provider (section 8.5).

Currently we support only GitHub Actions.

For GitHub Actions, regardless of the registry, jobs or workflows require the id-token: write permission. The RubyGems Guides strongly recommend granting this permission at the job level, and discourage granting it at the workflow level. Permissions and triggers in general are left to the GitHub Actions article listed in section 1.4.

5.4 How the First Version Is Published

Whether the first version of a package that does not exist yet can be published with trusted publishing also varies by registry.

RubyGems.org allows the release of the first version through a "pending" trusted publisher. Users register by including the gem's name on their profile's "pending" trusted publisher page. Upon a successful first push, the pending registration is converted to a standard trusted publisher status for that gem, and the registering user becomes the gem's owner.

After its first successful push, it will be converted to a "normal" trusted publisher for the new gem,
and you will be added as the owner of the gem.

On NuGet.org, as section 4.2 describes, the policy's scopes determine whether publishing new packages is allowed.

According to their documentation as of the verification date, on crates.io, pub.dev, and Open VSX, the first version cannot be published with trusted publishing. The crates.io documentation lists a prerequisite that the crate must already be published, and states that the first publish requires an API token.

Your crate must already be published to crates.io (initial publish requires an API token)

The pub.dev documentation states that, currently, automated publishing works only for existing packages. For a new package, the first version is published with dart pub publish, not with automated publishing.

Today, you can only automate publishing of existing packages.

The Open VSX announcement and wiki state that a prerequisite is that the extension already has at least one active version. The first version is published in the same way as a standard publication, using a long-lived PAT. The wiki also states that registrations for extensions that do not exist yet are rejected. According to the Open VSX wiki, if all versions of an extension are deleted, the registration will no longer match, even if it remains visible in the settings.

On JSR, you create a scope and package on jsr.io, link the package to a GitHub repository, and then publish it using GitHub Actions. No statement requiring a version to be already published before publishing with an ID token was found in the JSR documentation.

6. Switches, Refused Triggers, and Attestation

This section covers whether there is a switch that stops publishing by other means after trusted publishing is set up, which triggers and refs are refused, and whether published versions get attestation or provenance.

6.1 crates.io's Trusted Publishing Only Mode and JSR's Scope Setting

A crates.io blog post from January 21, 2026, announced that crate owners can now enforce trusted publishing. When enabled in the crate's settings, traditional API token-based publishing is disabled, and only trusted publishing can publish new versions.

When enabled in the crate settings, traditional API token-based publishing is disabled, and only Trusted Publishing can be used to publish new versions.

This setting is not mentioned on the crates.io Trusted Publishing page. That page states that while migrating from API tokens, you can use both API tokens and trusted publishing. Among the crates.io sources this article read, only the January 21, 2026, blog post describes this switch.

JSR has a comparable setting, set per scope rather than per package. The Scopes page of the JSR documentation says that a scope admin can require that all package versions be published from a CI environment (GitHub Actions). With the setting enabled, versions can no longer be published from a local development environment, and every version must be published with an ID token from a CI environment.

As a scope admin you can require that all package versions are published from a CI environment (GitHub Actions). Enabling this option will prevent users from publishing package versions from their local development environment. All package versions must be published with an OIDC token from a CI environment.

Searches of the documentation for the other four registries (RubyGems.org, NuGet.org, pub.dev, and Open VSX), using the terms only, disable, enforce, require, API key, and token, yielded no results indicating a setting that would prevent packages with trusted publishing enabled from being published using long-lived API keys or access tokens. For pub.dev, a setting that disables manual publishing was found only in the source code (section 5.2). The --trusted-publishing option of Open VSX's ovsx causes jobs to fail when an ID token cannot be obtained, but it is a setting on the publishing command itself, rather than a registry-level configuration.

6.2 Refused Triggers and Refs

The crates.io blog post of January 21, 2026, states that crates.io blocks trusted publishing triggered by GitHub Actions' pull_request_target and workflow_run events.

The pull_request_target and workflow_run GitHub Actions triggers are now blocked from Trusted Publishing.

According to its documentation, pub.dev limits publishing from GitHub Actions to workflows triggered by pushing a git tag. It rejects publishing from workflows started without a tag.

Pub.dev rejects publishing from GitHub Actions triggered without a tag.

In the source code, publishing from workflow_dispatch events can also be enabled in the package settings, and the ref must still be a tag (section 5.2).

The GitLab CI/CD section of the NuGet.org documentation lists ref as a non-mandatory claim in a table of ID token claims. The accompanying note specifies that it must represent a branch name, and that tags are not permitted.

Must be a branch name (e.g., no tags)

Open VSX does not associate registrations with branches or tags. Any ref that runs the registered workflow file is trusted. The Open VSX wiki recommends pinning an environment to restrict publishing further. The Open VSX wiki also states that Open VSX rejects ID tokens with the headers x5u, x5c, jku, and jwk.

No statement refusing publishing from specific triggers or refs was found in the RubyGems.org, NuGet.org (GitHub Actions side), or JSR sources. The search terms used were: pull_request, workflow_run, trigger, reject, block, tag.

The JSR Scopes page describes a restriction based on the user who triggered the workflow rather than on triggers or refs. By default, publishing is permitted only if that user is a member of the scope on JSR.

As a scope admin you can additionally restrict publishing to be permitted only if the user that triggered the GitHub Actions workflow is a member of this scope on JSR. This option is enabled by default.

6.3 Attestation and Provenance

JSR automatically generates provenance statements for packages published from GitHub Actions. JSR's Provenance and trust page states that these provenance statements are created using the SLSA framework and stored in Sigstore's Rekor transparency logs. Using the --no-provenance flag prevents their creation. The Publishing packages page indicates that provenance attestation is not generated when publishing with a personal access token, but is only created when publishing through GitHub Actions using OIDC.

Provenance is only available when publishing from GitHub Actions using OIDC.

Through its API, RubyGems.org returns the sigstore attestations published with each gem version. The RubyGems.org API documentation states that the attestations endpoint returns an empty array for versions pushed without attestation. The attestations input for v1 of the rubygems/release-gem action defaults to "true", and the description begins with [EXPERIMENTAL]. The input works only when publishing to RubyGems.org with trusted publishing. For RubyGems versions that sign on their own, the input has no effect, and they add attestation regardless of its value. The release notes for RubyGems 4.1.0.beta1, released on September 9, 2026, include an item titled Push with auto-attestation. This is a beta version.

[EXPERIMENTAL]
Enable experimental support for sigstore attestations.
Only works with RubyGems.org via Trusted Publishing.
No effect on RubyGems versions that sign on their own; they attest
whether or not this is set.

No statement about creating attestation or provenance was found in the trusted publishing sources of NuGet.org, crates.io, pub.dev, or Open VSX. The crates.io RFC 3691 states that verifying the provenance of published crates falls outside the scope of this RFC. The pub.dev documentation states that publication records are stored in pub.dev's audit-log, and that this record should include a link to the corresponding GitHub Actions run. Open VSX marks versions published through trusted publishing with a badge on the extension's page. These are not attestation records.

6.4 Table 3 — Refusal Table

This table summarizes the switches that stop publishing, the refused triggers and refs, and attestation. This article calls this table the Refusal Table. The Switch That Stops Token Publishing column refers to a switch that, for a package with trusted publishing set up, stops publishing with long-lived API keys or access tokens.

RegistryRefused Triggers and RefsSwitch That Stops Token PublishingAttestation or ProvenanceWhere the Source Says So
RubyGems.orgNo description found.No description found.Returns sigstore attestation via API. Input for release-gem v1 is marked as [EXPERIMENTAL]. The release notes of RubyGems (client) 4.1.0.beta1 (beta) have an item for automatic attestation.RubyGems.org API, action.yml of release-gem, RubyGems blog (2026-09-09)
NuGet.orgThe note regarding GitLab CI/CD's ref claim says it must be a branch name, not a tag (not written as a description of refusal behavior).No description found.No description found.Microsoft Learn (Trusted Publishing on nuget.org).
crates.ioGitHub Actions pull_request_target and workflow_run.Trusted Publishing Only Mode (crate setting).No description found.Rust Blog (2026-01-21).
Open VSXDoes not associate with branches or tags. Rejects ID tokens with headers x5u, x5c, jku, and jwk.No description found (--trusted-publishing of ovsx is a setting on the publishing side).No description found (marks published versions with a badge for trusted publishing).Open VSX wiki, Eclipse Foundation blog (2026-09-30).
pub.devGitHub Actions workflows triggered without a tag.No description found (the source code has one; section 5.2).No description found (provides links to GitHub Actions executions in the audit-log).Dart documentation (Automated publishing).
JSRNo description of refusal by trigger or ref was found. By default, publishing is permitted only if the user who triggered the workflow is a member of the scope.A scope setting can require that all versions be published from a CI environment (GitHub Actions) with an ID token.Automatically creates a provenance statement when publishing via GitHub Actions using OIDC.JSR Docs (Scopes, Provenance and trust, Publishing packages).

7. What Differs from npm and PyPI

This section places npm and PyPI as reference rows and checks how they differ from the six registries in this article. For details on npm and PyPI, see the npm and PyPI article listed in section 1.4.

7.1 Reference Rows for npm and PyPI

The npm and PyPI values follow the npm and PyPI article (verified on October 9, 2026). The npm announcements of October 2, 2026, are also cited here from the GitHub Changelog.

  • npm: Exchanged. The npm API documentation gives the lifetime of the token obtained in the exchange as typically 1 hour. The npm "Trusted publishing for npm packages" page also indicates that a single package can have up to 10 trusted publishers registered simultaneously (the npm trust documentation says that the registry supports only one configuration per package; see section 3.3 of the npm and PyPI article). The npm trust documentation lists, as one of its prerequisites, that the package already exists in the registry. A GitHub Changelog entry from October 2, 2026, states that it is now possible to create new packages using npm stage publish and then configure trusted publishing. A separate entry from the same day says that npm now rejects trusted publishing from workflows triggered by the issue_comment event in GitHub Actions, and that the pull_request_target restriction was already in place.

npm also now rejects trusted publishing tokens from GitHub Actions issue_comment events, alongside the existing pull_request_target restriction.

  • PyPI: Exchanged. PyPI's short-lived API tokens are only valid for 15 minutes after creation. With a pending publisher, it is possible to publish the first version of a project that does not yet exist.

Like the six registries mentioned in this article, npm and PyPI also differ in what registrations are bound to, lifetimes, and how the first version is handled. The OpenSSF document lists pending registration as one solution for registries that do not create a package until the first version is published. Both PyPI's pending publisher and RubyGems.org's pending trusted publisher operate in this manner.

7.2 Different Names for the Same Mechanism

Registries refer to the same underlying mechanism by different names. The pub.dev documentation calls it "automated publishing". The JSR documentation doesn't assign a name to the mechanism itself, instead referring to it as "Publishing from GitHub Actions" or "Tokenless OIDC publishing". The NuGet.org documentation uses the term "Trusted Publishing" when discussing GitHub Actions, while referring to the screen for registering policies in the GitLab CI/CD section as "Federated credentials". The Docker Hub documentation uses the term "OIDC connections" for a similar mechanism (see section 8.6). When searching for configuration screens or documentation, it's necessary to search using the specific name used by each registry.

7.3 How This Differs from OIDC Where AWS Is the Receiver

GitHub Actions ID tokens can be passed to AWS, in addition to package registries. When AWS is the recipient, the conditions in the IAM role's trust policy are matched against the claims in the ID token, and the workflow assumes the role using sts:AssumeRoleWithWebIdentity. In this article, trusted publishing refers to the scenario where the recipient is a package registry, and the registry's registration is matched against the claims in the ID token. When a workflow contains configurations for both recipients, it's important to verify which settings apply to which recipient. The inbound workload federation article listed in section 1.4 covers the case where AWS is the recipient.

8. Where the Sources Disagree and Where No Statement Was Found

This section lists where sources from the same registry describe something differently, and where no statement was found after searching. It does not call either description wrong.

8.1 crates.io Support for GitLab CI/CD

The Trusted Publishing page on crates.io states that support for GitLab CI/CD is in public beta.

GitLab CI/CD support is currently in public beta.

A blog post dated January 21, 2026, announces support for GitLab CI/CD without using the term "beta". The same blog post also notes that, currently, this functionality only works with GitLab.com.

Trusted Publishing now supports GitLab CI/CD in addition to GitHub Actions.

This article follows the page as of the verification date and treats GitLab CI/CD support as a public beta.

8.2 NuGet.org's 7-Day Window and API Key Lifetimes

Regarding the 7-day period mentioned in section 4.4, the documentation for NuGet.org and the announcement from September 22, 2025, describe it differently. The documentation says the 7-day provisional state happens sometimes, mainly with private GitHub repositories. It says the policy becomes inactive if no publish happens within 7 days, and becomes permanently active after the first successful publish. The announcement says new policies for private repositories start out as active for 7 days by default. Furthermore, it states that successful authentication with NuGet login, exchanging the job's ID token for a temporary API key, is sufficient; it is not necessary to publish a package.

New policies for private repositories start out as active for 7 days by default.

A successful NuGet login is sufficient — you don’t need to publish a package.

The sources on API key lifetimes also disagree. The Scoped API keys page, as of the verification date, still provides an example of keys that are valid for 365 days. Regarding legacy (non-scoped) API keys, it states that they do not expire, and that legacy keys not used to push a package for more than 365 days are retired. An announcement from August 3, 2026, states that starting August 17, 2026, new keys will have a maximum validity of 30 days, and the option to select a 365-day period will no longer be available. Keys created prior to that date will expire on November 1, 2026.

The descriptions of temporary API keys also differ slightly between the documentation and the announcement. The documentation states that they are valid for one hour. The announcement from September 22, 2025, describes them as lasting approximately one hour (≈ 1 hour, typically last about 1 hour) and specifies that temporary API keys are single-use (single‑use). No mention of the single-use restriction was found in the documentation. What the documentation limits to once is the number of times an ID token can be exchanged for a temporary API key.

8.3 RubyGems.org Token Lifetime

The lifetime of the RubyGems.org short-lived API token is described differently in different sources. The RubyGems Guides state that it is only valid for a short period. A security advisory from July 22, 2026, indicates that it expires within minutes. In the source code at the checked commit, the value is 15 minutes (see section 5.2). While these three points don't contradict each other, the documentation doesn't specify a number. This article lists only the documentation and blog descriptions in Table 1, while the 15-minute timeframe is detailed in section 5.2.

8.4 The Dates and the List in the OpenSSF Document

The OpenSSF's "Trusted Publishers for All Package Repositories" page states Last updated: May 2025 at the top. The "Support" section at the bottom of the same page, dated As of December 2025, lists six registries that support trusted publishing: pub.dev, npm, NuGet, PyPI, RubyGems.org, and crates.io. This list does not include JSR or Open VSX. Open VSX announced its support on September 30, 2026, which is a date later than the dates listed on this page. Publishing to JSR with GitHub Actions ID tokens, however, was possible before the dates listed on this page. This article does not consider this list to be a comprehensive listing of all supported registries.

8.5 The Open VSX Wiki and the open-vsx.org Announcement

The Trusted Publishing page on the Open VSX wiki includes instructions for users, as well as settings for those operating the registry (the For registry operators section). The wiki covers registration fields for both GitHub Actions and GitLab CI/CD, and settings for adding GitLab instances as providers. The wiki states that the available providers depend on those enabled in the registry, and that the default value for ovsx.trusted-publishing.active-providers is github.

The Eclipse Foundation blog announcement dated September 30, 2026, states that open-vsx.org currently supports only GitHub Actions. The same announcement, under the heading Coming:, indicates plans to allow any GitLab instance, including self-hosted ones, to be configured as a provider.

Coming: Self-hosters GitLab flexibility. Soon, any GitLab instance can be configured as a provider, including self-hosted ones. But right now, we are rolling out GitHub support only.

This article reads the wiki as describing what the software can do, and the announcement as describing what is available on open-vsx.org. This article therefore treats GitHub Actions as the only CI system that open-vsx.org accepts.

8.6 Maven Central, Hex, and Docker Hub

These three platforms are not included in the main tables of this article (the Assumption Table and Tables 1 to 3). Instead, the following describes the state as of the verification date, line by line.

  • Maven Central: This article searched the 115 pages in the central.sonatype.org sitemap (all retrieved successfully) for the terms oidc, openid, trusted publish, trusted publisher, id-token, id_token, keyless, short-lived, workload identity, federat, github actions, and token exchange. No description of trusted publishing or of publishing with OIDC was found (the one hit for github actions was a sentence in an FAQ that explains the source IP address of requests). Not finding a description does not prove that Maven Central does not support it.
  • Hex: The "Publishing a package" page on hex.pm recommends using trusted publishers when using GitHub Actions for CI. Hex states that ID tokens are exchanged for publish tokens specific to packages, and that the package must already exist. However, the detailed explanation page linked from this page could not be retrieved as of the verification date. This article does not include Hex in the main tables.
  • Docker Hub: Docker's documentation describes a similar mechanism as "OIDC connections", stating that Docker validates workflow ID tokens and returns access tokens. The documentation page lists Team and Business as the applicable subscriptions. Because it is a container image registry and whether it can be used depends on the subscription, this article does not cover it.

8.7 Where No Statement Was Found

The following were not found in the materials this article read. The absence of such information does not constitute proof that these mechanisms or values do not exist.

  • An upper limit on the number of registrations per package: the RubyGems.org, NuGet.org, pub.dev, and JSR sources. For crates.io, no statement was found in the documentation, and the value was found only in the source code (section 5.2).
  • For a package with trusted publishing set up, a setting that stops publishing with long-lived API keys or access tokens: RubyGems.org, NuGet.org, the pub.dev documentation (pub.dev's source code has one; section 5.2), and Open VSX (section 6.1).
  • Descriptions of mechanisms to reject publishing triggered by specific events or refs: RubyGems.org, NuGet.org (on the GitHub Actions side), and JSR. Open VSX states that it does not associate registrations with refs (section 6.2).
  • Descriptions of mechanisms to create attestation and provenance: NuGet.org, crates.io, pub.dev, and Open VSX (section 6.3).
  • Expiration time for pending trusted publishers on RubyGems.org: RubyGems Guides (the source code indicates a value of 12 hours, section 5.2).
  • Descriptions of storing and matching the numerical ID of the owner at the time of registration: RubyGems.org and crates.io documentation (the source code contains related processing, section 5.2).
  • Date when NuGet.org began supporting GitLab CI/CD: NuGet.org documentation and two announcements (the announcement dated August 3, 2026, encourages GitLab users to migrate).
  • Which crates a token can be used for on crates.io: crates.io documentation. RFC 3691, a design document, states that each registration specifies the crate it may publish and that the scope is initially fixed to publish-update. For the source code, see section 5.2.

The search terms used included the main subjects of each line, as well as expire, minutes, hour, pending, new package, first, only, limit, revoke, attestation, provenance, and regarding triggers, pull_request, workflow_run, trigger, reject, block, and tag. In addition to the main documentation pages for each registry, the search included the blogs, API documentation, the Open VSX wiki, README files, and FAQs that this article read.

9. Frequently Asked Questions about Trusted Publishing Beyond npm and PyPI

This section answers, within the scope of this article, questions that tend to come up when moving a publishing pipeline to trusted publishing.

Q1. Can the first version be published with trusted publishing on every registry?

No, it depends on the registry. On RubyGems.org, the first version can be published with trusted publishing through a pending trusted publisher. On NuGet.org, the policy's scopes determine whether publishing new packages is allowed. According to the JSR documentation, you create a package on jsr.io and link it to a repository, and no statement requiring a version to be already published was found. On crates.io, the first version is published with an API token. On pub.dev, the first version is published with dart pub publish, not with automated publishing. On Open VSX, the first version is published with a long-lived PAT (see section 5.4).

Q2. Does setting up trusted publishing stop publishing with API keys?

Not necessarily. In the sources this article read (other than source code), two switches stop publishing with tokens: crates.io's Trusted Publishing Only Mode (per crate, described in the January 21, 2026 blog post) and JSR's scope setting that requires publishing from CI (per scope, described on the Scopes page). For pub.dev, a setting that disables manual publishing was found only in the source code (section 5.2). crates.io's documentation states that while migrating from API tokens, both API tokens and trusted publishing can be used simultaneously. In Open VSX, if --pat or OVSX_PAT are still present, those will take precedence. Simply removing the key from your CI secrets doesn't invalidate it on the registry's side. Also check whether to revoke it on the registry side (see sections 1.1 and 6.1).

Q3. Can the ID tokens and short-lived credentials that a job handles be written to logs?

No. The OpenSSF document states that both ID tokens and short-lived credentials issued by registries are considered sensitive information, and if exposed, could be misused until they expire. The Open VSX wiki also advises against displaying either type of token. The fact that the tokens have a short lifespan does not justify logging them (see section 5.1).

Q4. Can packages be published from GitLab CI/CD with trusted publishing?

It depends on the registry. The documentation for NuGet.org includes instructions for GitLab CI/CD. The documentation for crates.io refers to support for GitLab CI/CD as a public beta, and a blog post from January 21, 2026, states that it currently works only with GitLab.com. For JSR, publishing with an ID token is supported only from GitHub Actions; from GitLab CI/CD, you publish with a personal access token. As of the verification date, only GitHub Actions can be used on open-vsx.org. The documentation for RubyGems.org only describes using GitHub Actions. The documentation for pub.dev lists GitHub Actions and Google Cloud, and also mentions the possibility of publishing from locations using Google Cloud service accounts (section 5.3).

Q5. Can a workflow triggered by pull_request_target publish?

crates.io blocks trusted publishing from workflows triggered by pull_request_target and workflow_run. The pub.dev documentation says that only workflows triggered by tag pushes can publish. npm already restricts pull_request_target, and its October 2, 2026, announcement says it also rejects workflows triggered by issue_comment. No statement refusing specific triggers was found in the RubyGems.org, NuGet.org (GitHub Actions side), or JSR sources. The Open VSX wiki states that any ref that runs the registered workflow file is trusted. Not finding a refusal does not show that a trigger is safe. Choosing triggers is left to the GitHub Actions article listed in section 1.4 (section 6.2).

Q6. Do versions published with trusted publishing get attestation or provenance automatically?

JSR automatically generates provenance statements for packages published from GitHub Actions using OIDC. (This feature can be disabled with the --no-provenance flag.) RubyGems.org provides attestations via its API, and in v1 of the rubygems/release-gem action, the attestations input is marked [EXPERIMENTAL] and is enabled by default. The input's description says that RubyGems versions that sign on their own add attestation regardless of the input. No statement about creating attestation or provenance was found in the trusted publishing sources of NuGet.org, crates.io, pub.dev, or Open VSX (see section 6.3).

Q7. Does Maven Central support trusted publishing?

On the verification date of this article (October 8, 2026), a search of 115 pages on the sitemap for central.sonatype.org, using 12 keywords, revealed no mentions of trusted publishing or publication via OIDC. The absence of such information does not constitute proof that Maven Central does not support these features. Check the Maven Central documentation for the current state (section 8.6).

10. Summary

The assumption that publishing relies on long-lived API keys is gradually being challenged across registries beyond npm and PyPI. pub.dev announced publishing with a CI ID token on January 18, 2023, followed by RubyGems.org on December 14, 2023, crates.io on July 11, 2025, NuGet.org on September 22, 2025, and Open VSX on September 30, 2026. JSR, since its public beta announcement on March 1, 2024, provides examples of publishing using GitHub Actions ID tokens. NuGet.org announced that, starting August 17, 2026, new API keys will be valid for a maximum of 30 days, and those issued prior to that date are scheduled to expire on November 1, 2026.

What the publishing job authenticates with falls into two groups. RubyGems.org, NuGet.org, crates.io, and Open VSX receive CI-generated ID tokens, verify them, and then issue short-lived credentials in their place. The documentation for pub.dev and JSR outlines procedures for directly using ID tokens to authenticate publishing requests. The OpenSSF document contains both a statement that recommends exchange while allowing direct use and a statement that direct use should not be done.

What trust is bound to also differs. RubyGems.org, crates.io, and Open VSX bind trust to individual packages. NuGet.org binds trust to owners, using scopes and glob patterns to refine access. pub.dev, when used with GitHub Actions, binds trust to a repository and tag pattern, and to a service account when used with Google Cloud. JSR binds trust to the GitHub repository linked to a package. The pub.dev documentation notes that, at the stage of simply registering repository and tag patterns, anyone who can push to the repository can publish new versions. It recommends implementing tag protection rules and using environments to restrict access.

Lifetimes, accepted CI systems, and the handling of the first version also differ by registry. The sources that give a number for the lifetime of the short-lived credential issued in the exchange are NuGet.org at 1 hour and crates.io at 30 minutes (both in documentation), and Open VSX at about 5 minutes (announcement and wiki). The documentation for RubyGems.org does not specify a duration, only stating that the short-lived API token is valid for a short time. In the source code at the checked commit (b6bc89a in rubygems/rubygems.org), it is 15 minutes (section 5.2). For GitLab CI/CD, the NuGet.org documentation provides steps. The crates.io documentation calls its support a public beta, and the January 21, 2026 blog post says that it currently works only with GitLab.com. As of the verification date, only GitHub Actions can be used on open-vsx.org. Of the six registries in this article, those whose documentation says the first version can be published with trusted publishing are RubyGems.org (pending trusted publisher) and NuGet.org (depending on the scopes). JSR requires creating and linking the package, and no statement requiring an already published version was found. On crates.io, pub.dev, and Open VSX, the first version is published by other means.

As of the verification date, few sources describe switches that stop publishing or triggers that are refused. In sources other than source code, switches that stop publication with tokens were found only for crates.io's Trusted Publishing Only Mode (per crate) and JSR's setting that requires publishing from CI (per scope); pub.dev's source code also has a setting that disables manual publishing. As refused triggers, crates.io's pull_request_target and workflow_run were found. The pub.dev documentation says that only workflows triggered by pushing tags on GitHub Actions are accepted and that those initiated without a tag are rejected. The GitLab CI/CD section for NuGet.org includes a note that the ref claim is limited to branch names (this was not described as a rejection behavior). Descriptions of creating attestations and provenance were found for JSR and RubyGems.org (RubyGems.org's API returns an attestation, and the input for release-gem is marked as [EXPERIMENTAL]).

Finally, here are five things to check in a publishing pipeline.

  • Are there any old API keys or access tokens remaining in the CI secrets? In Open VSX, if they exist, those will take precedence.
  • Does the publishing job receive short-lived credentials, or the ID token itself? Regardless, is this being logged anywhere?
  • Is the registration bound to a package, an owner, or a repository (on pub.dev, a repository with a tag pattern, or instead a Google Cloud service account)? If the registration applies to all packages owned by an owner, as with NuGet.org, what scope and glob patterns are being used to filter it?
  • How will the first version be published? After the first version, are there procedures to remove the API keys and access tokens created for that purpose from the CI system, and to invalidate them on the registry side?
  • Does the trigger that starts the publishing workflow fall under a trigger the registry refuses? Even if it does not, is it acceptable to publish with that trigger?

11. References



References:
Tech Blog with curated related content

Written by Hidekazu Konishi