Trusted Publishing on Open VSX

If you publish a VS Code extension to Open VSX through GitHub Actions, Trusted Publishing lets you replace a stored personal access token with short-lived publishing credentials. This reduces credential management and limits the scope and lifetime of the token used to publish each release.

The short version

Instead of storing a token, you tell the registry which CI workflow is allowed to publish your extension. At release time, the workflow shows the registry a signed identity token from the CI provider. The registry checks it and hands back a publishing token that lasts about five minutes and works for that one extension only. You no longer need to store a long-lived Open VSX publishing token in your CI configuration or rotate that credential. The short-lived tokens used during publishing still need to be protected.

Open VSX follows the OpenSSF guidance "Trusted Publishers for All Package Repositories", the same model used by PyPI, npm, RubyGems, NuGet, crates.io and pub.dev. If you've set it up on any of those, this will feel familiar.

Setting it up

There are mandatory prerequisites and preconditions:

  • you must sign the Publisher Agreement (existing publishers already did)
  • you must be the target namespace owner
  • the target namespace must be verified
  • the extension must already have an active version (the first publication still requires a PAT)
  • with these above fulfilled, setup the trusted publishing on web UI
  • finally, remember to remove PAT: An existing --pat argument or OVSX_PAT environment variable takes precedence, so publishers must remove those from the migrated workflow.

In other words, you must own the verified namespace, since being a contributor isn't enough. You also need the signed Publisher Agreement. And the extension must already exist with at least one active version. That last one surprises people: the first release still has to go out with a personal access token. Then go to Settings > Trusted Publishers and pick the namespace. Currently we support only GitHub Actions. For them, enter the owner, the repository, the workflow filename, and optionally a deployment environment.

The workflow only needs permission to request an ID token:

permissions:
  contents: read
  id-token: write
jobs:
  publish:
    runs-on: ubuntu-latest
    environment: publish   # only if the registration pins one
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v5
      - run: npm ci
      - run: npx ovsx publish --trusted-publishing

The --trusted-publishing flag makes the job fail if no ID token is available, instead of quietly falling back. 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.

Details worth knowing

Matching uses numeric IDs, not names. When you register, Open VSX looks up the repository and owner names and stores their immutable IDs. If someone deletes a repo and a stranger recreates it under the same name, the new repo has a different ID and won't match. The workflow filename has no ID, though, so it is matched by name.

Registrations are not tied to a branch or tag. Any ref that runs the registered workflow file is trusted. If that matters to you, and for most extensions it should, pin an environment and require reviewers on it.

The issued token acts as you. Publishing shows the registration's creator as the publisher. Versions published this way get a "trusted publisher" icon next to "Published by."

Registrations clean themselves up. If the creator stops being a namespace owner, their registrations are deleted and any tokens already issued stop working. An extension can have only one trusted publisher at a time. Registrations can't be edited, only deleted and recreated.

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.

What it doesn't fix

Trusted publishing removes the long-lived secret, not the trust. Whoever can change the workflow file, or run it, can publish your extension. Branch protection, review of workflow changes, and environment approvals now carry the security weight the PAT used to. The tokens also still matter while they're valid, so keep them out of logs.

Ready to migrate? Follow the Open VSX Trusted Publishing setup guide to register your GitHub Actions workflow and update your publishing configuration.

Under the hood: a token exchange, not a login

"OIDC" appears everywhere in trusted publishing docs, which suggests the CI job "logs in" to the registry. That isn't really what happens. Nobody authenticates to a relying party through an OpenID Provider's authorization flow. A workload presents a signed JWT about itself and trades it for a registry credential. Architecturally, that is workload identity federation: the JWT-bearer and token-exchange patterns of RFC 7523 and RFC 8693. The OIDC part is mostly plumbing, namely discovery documents and JWKS for checking signatures.

The Open VSX exchange works like this:

  1. The CI job requests an OIDC ID token whose audience is the registry URL.
  2. ovsx posts it, along with the namespace and extension name to server.
  3. The server picks which provider should verify the token.
  4. That provider fetches the issuer's discovery document and keys, then verifies the signature and standard claims.
  5. The claims are compared with the stored registration. If they match, the server returns a short-lived token.

All this happens within ovsx command line tool.

Getting started and references