You can publish packages to your team’s dedicated private registry on pyx.There are two main ways to publish packages to pyx:
With an authenticated client. This approach is
recommended for teams that publish packages from development machines, or
that are using CI/CD systems that do not (yet) support
Trusted Publishing.
With Trusted Publishing. This approach is recommended
for teams that publish packages from GitHub Actions and other supported CI/CD
systems.
Regardless of your publishing method, we recommend setting your registry’s
publish-url in your pyproject.toml to avoid needing to specify URLs on the
command line.For example, if your team is called acme, you can configure publishing to the
acme/main registry with:
Once authenticated, you can publish packages to your team’s
dedicated private registry.For example, if you configured the acme/main registry as main (like above),
you can publish a package with:
Copy
# 'main' is the name of the registry you configured aboveuv publish /path/to/pyarrow-19.0.1.tar.gz --index=main
Alternatively, you can specify the --publish-url directly:
Trusted Publishing is currently in preview. All organizations have access to
it, but you may encounter bugs or missing features. If you have feedback,
please get in touch.
If you’re using Trusted Publishing to publish, you may also want to use
Trusted Access to provide tokenless read-only access to your packages from
CI/CD systems. See Authentication - Trusted
Access for details.
Trusted Publishing is an authentication method that uses
OpenID Connect to allow CI/CD systems to upload
to package indices (in this case pyx) without needing to manage long-lived API
tokens.In other words, with Trusted Publishing, there’s no need to add or persist pyx
credentials in GitHub Actions or other supported CI/CD providers.Trusted Publishing comes with usability and security benefits:
Usability: with Trusted Publishing, there’s no need to create and
provision API tokens on your CI/CD system. The CI/CD system can authenticate
directly to pyx.
Security: Trusted Publishing uses short-lived credentials that are
minimally scoped (down to the exact set of packages configured for
publishing). These qualities reduce the blast radius of a compromised
credential.
At the moment, support for these providers is limited to their hosted
offerings (e.g., github.com). Support for self-hosted instances is not yet
available.
You can enroll a Trusted Publisher against one or more of your team’s packages
(or packages that you’d like created) in the in the
pyx dashboard under
Team > Trusted Publishers.
At the moment, only team administrators can enroll Trusted Publishers.
To add a Trusted Publisher, click the “Create Publisher” button and select the
projects you want to allow the publisher to upload to. Then, select the provider
you want to use and complete the steps for that provider.
Unlike PyPI, pyx does not require a special “pending” publisher state: you can
associate nonexistent packages with a Trusted Publisher, and pyx will create
them automatically when the publisher first uploads a distribution to that
package.
GitHub Actions
BuildKite
GitLab CI/CD
A GitHub Actions Trusted Publisher has two mandatory components:
The Repository is the owner/repo slug on GitHub
from which you’ll be publishing.
The Workflow is the filename of the GitHub Actions workflow
that will be publishing. This should be the base filename, e.g.,
publish.yml (not .github/workflows/publish.yml).
In addition, there are optional constraints you can add to your
publisher:
The Environment is an optional name of the
GitHub Actions environment
that will be used for publishing. This environment must be configured
in the GitHub repository settings and must be used in the workflow
via environment: <name>.
Specifying an environment is optional, but recommended: environments
enable you to enforce additional protections, like required reviewers
and tag protection rules on your publishing workflow.
The Subject pattern is an optionalfnmatch
pattern that the sub claim in the GitHub OIDC token must match.
See GitHub’s example subject claims
for details.
Specifying a subject pattern is optional, but may be useful
for enforcing publishing controls beyond those provided by
an environment’s protection rules.For example, you may wish to
customize the subject claim
to include the job_workflow_ref, and then use a subject pattern
that matches a specific reusable workflow.
BuildKite Trusted Publishing is currently in beta. If you’d like to
participate in the beta, please get in touch.
A BuildKite Trusted Publisher has two mandatory components:
The Organization is the slug of the BuildKite organization
from which you’ll be publishing. This must match the organization_slug
claim in the BuildKite OIDC token.
The Pipeline is the slug of the BuildKite pipeline
that will be publishing.
This must match the pipeline_slug claim in the BuildKite OIDC token.
In addition, you can optionally specify the Subject pattern, which is an
fnmatch pattern that must
match the sub claim in the BuildKite OIDC token.
A GitLab CI/CD Trusted Publisher has two mandatory components:
The Repository is the namespace/project slug on GitLab
from which you’ll be publishing. This slug can include groups and
subgroups, e.g., acme/anvils/my-project.
The Workflow is the filename of the GitLab CI/CD
pipeline configuration file that will be publishing. For most users
this will be .gitlab-ci.yml, but may be a relative path to your
pipeline configuration file if you’re using
a custom location.
In addition, there is an optional constraint you can add to your
publisher:
The Environment is the optional name of the
GitLab environment
that will be used for publishing. This environment must be configured
in the GitLab repository settings and must be used in the pipeline
via environment: <name>.
The Subject pattern is an optionalfnmatch
pattern that the sub claim in the GitLab OIDC token must match.
See GitLab’s example token payload
for details.
Once you’ve enrolled a Trusted Publisher
against one or more packages, you can publish to pyx with it.Trusted Publishing to pyx is built directly into uv as of v0.9.27. To use it,
all you need to do is ensure that your CI/CD environment exposes the proper OIDC
token.
GitHub Actions
BuildKite
GitLab CI/CD
Manual integration
For versions of uv prior to v0.9.27, you can use
astral-sh/pyx-auth-action
to authenticate with pyx via Trusted Publishing. See the action’s
documentation for details.
For isolation reasons, we strongly recommend that you perform
your distribution build in a separate job that your publishing job
depends on. This ensures that your build environment does not have
access to the short-lived credentials used for publishing.
publish.yml
Copy
jobs: build: runs-on: ubuntu-latest permissions: contents: read # for actions/checkout in private repos steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Set up uv uses: astral-sh/setup-uv@2ddd2b9cb38ad8efd50337e8ab201519a34c9f24 # v7.1.1 with: version: ">=0.9.27" - name: Build distributions run: uv build - name: Upload distributions uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: dists path: dist/ if-no-files-found: error pyx-publish: runs-on: ubuntu-latest permissions: id-token: write # for Trusted Publishing to pyx # (Optional) Set the environment to match the environment defined in pyx. environment: pyx.dev steps: - name: Download distributions uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: name: dists path: dist/ # 'main' is the name of the registry you configured above - run: | uv publish --index=main --trusted-publishing=always
Versions of uv prior to v0.9.27 do not directly support Trusted Publishing
to pyx with BuildKite. See the Manual integration
section.
pipeline.yml
Copy
steps: - label: ":python: Publish to pyx" # 'main' is the name of the registry you configured above command: uv publish --index=main --trusted-publishing=always plugins: - docker: image: ghcr.io/astral-sh/uv:python3.14-bookworm-slim # Without these options, Buildkite's Docker plugin does not # propagate the agent (which supplies the OIDC token) into the container. propagate-environment: true mount-buildkite-agent: true
Versions of uv prior to v0.9.27 do not directly support Trusted Publishing
to pyx with GitLab CI/CD. See the Manual integration
section.
.gitlab-ci.yml
Copy
stages: - build - publishpyx-build: stage: build image: ghcr.io/astral-sh/uv:python3.14-bookworm-slim script: - uv build artifacts: paths: - dist/pyx-publish: stage: publish image: ghcr.io/astral-sh/uv:python3.14-bookworm-slim script: # 'main' is the name of the registry you configured above - uv publish --index=main --trusted-publishing=always environment: pyx.dev
You do not need to perform a manual integration if you’re using
uv v0.9.27 or newer, as Trusted Publishing to pyx is built directly into uv.
If you’re using uv prior to v0.9.27 (or another client that does not
directly support Trusted Publishing to pyx, like twine), you can still use Trusted
Publishing by obtaining an OIDC token from your CI/CD provider and
exchanging it for a pyx access token.
Use the audience obtained in the previous step to get an OIDC token
from your CI/CD provider.Every CI/CD provider has a different method for obtaining an OIDC token;
refer to your provider’s documentation for detail. For example:
You can use the id to automate this for many
providers:
Copy
# ${audience} is the audience obtained in the previous step.uvx --from=id python -m id "${audience}"
If your CI/CD provider supports secret masking, we recommend
enabling it for the OIDC token to avoid leaking it in logs.For example, for GitHub Actions:
Copy
echo "::add-mask::${oidc_token}"
3
Exchange the OIDC token for a pyx access token
Once you have the OIDC token from your CI/CD provider, exchange it
for a pyx access token:
Copy
oidc_token="..." # Step 2# acme/main is your workspace/registry on pyxresp=$( curl --silent -X POST \ "https://api.pyx.dev/v1/trusted-publishing/acme/main/mint-token" \ -d "{\"token\": \"${oidc_token}\"}")api_token=$(jq -r '.token' <<< "${resp}")
4
Publish to pyx
Finally, use the obtained pyx access token to publish your
distributions:
Copy
# 'main' is the name of the registry you configured aboveUV_PUBLISH_TOKEN="${api_token}" uv publish --index=main
pyx supports private classifiers, which you
can use to prevent packages from being accidentally published to PyPI (or
another index) instead of pyx.Private classifiers come in two forms:
Private :: pyx allows publishing anywhere on pyx (but only to pyx).
Private :: pyx :: <team> restricts publishing to a specific
team. For example, Private :: pyx :: acme allows
publishing only to the acme team’s registries.
To use private classifiers, add them next to your other classifiers in your
pyproject.toml. For example:
Like PyPI, pyx will reject packages that use the Private :: Do Not Upload
classifier. Additionally, like PyPI, pyx will reject any Private ::
classifiers not recognized by the rules above.
pyx supports publishing from non-uv clients, like
twine. To publish with twine,
point the twine client to the pyx index, and authenticate via
uv auth token pyx.dev: