Skip to main content

Authenticating the uv client

pyx is supported as a first-class package registry in the uv client as of uv v0.8.15. You can install uv with our standalone installers, or with your package manager of choice:
curl -LsSf https://astral.sh/uv/install.sh | sh
Once installed, you can authenticate the uv client with:
uv auth login pyx.dev
This will open a browser window to authenticate via the pyx dashboard. By default, uv supports OAuth-based authentication via GitHub and Google. For SSO support, get in touch.
If you’re running in a headless environment, e.g., when SSH’d into a remote server, you’ll be prompted to open a browser window to authenticate.
To verify that you’ve authenticated successfully, you can install a package from the pyx PyPI mirror with:
uv pip install ruff --default-index https://api.pyx.dev/simple/pypi
You can un-authenticate the uv client with:
uv auth logout pyx.dev
Logging out will invalidate the refresh token on the server and remove the cached credentials from the machine.

API key authentication

uv also supports API key-based authentication. You can create API keys in the pyx dashboard and authenticate the client by setting the PYX_API_KEY environment variable:
export PYX_API_KEY=sk-pyx-...
API keys can be set to expire after a validity period. You can configure a validity period while creating an API key; the acceptable validity periods are 7 days, 14 days, 30 days, 90 days, or 1 year (the default).

Trusted Access

Trusted Access is currently in preview. If you’re interested in using Trusted Access for your CI/CD systems, please get in touch.
Trusted Access is an authentication method that uses OpenID Connect to allow CI/CD systems to read from pyx without needing to manage long-lived API tokens. It’s like Trusted Publishing, but for read instead of write access.

Supported providers

pyx supports three Trusted Access providers:
At the moment, support for GitHub Actions and GitLab CI/CD as Trusted Access parties is limited to their hosted offerings, i.e. github.com and gitlab.com. Self-hosted instances may be used via the “generic” provider option.

Enrolling a Trusted Accessor

You can enroll a Trusted Accessor on the pyx dashboard under Team > Trusted Access. A Trusted Accessor can be given access to one or more non-default views, or to your entire workspace.
Only team administrators can enroll Trusted Accessors.
To add a Trusted Accessor, click the “Create Accessor” button and select the views (or the entire workspace) to which the accessor should have read access. Then, select the provider you want to use and complete the steps for that provider.
All Trusted Accessors have access to pyx’s public views, like public/pypi and the GPU indices, regardless of the specific views/workspace you enroll the accessor against.
A GitHub Actions Trusted Accessor has one mandatory component:
  • The Repository pattern specifies which repositories can use this accessor. This pattern uses a restricted subset of the fnmatch syntax. For example, acme/example would allow only the acme/example repository to use this accessor, while acme/* would allow any repository under the acme organization on GitHub.
In addition, there are optional constraints you can add to your accessor:
  • The Subject pattern is an optional fnmatch pattern that constrains the OIDC subject claim (sub) in the token presented by GitHub Actions. See GitHub’s example subject claims for details.

Using a Trusted Accessor

Once you’ve enrolled a Trusted Accessor, you can use it to obtain short-lived read tokens for accessing pyx from your CI/CD.
A future version of uv will automatically perform the flow below. Until then, beta users of Trusted Access must explicitly perform these exchange steps.
1

Obtain an OIDC token from your CI/CD provider

Obtain an OIDC token with audience pyx:trusted-access 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 package to automate this for many providers:
uvx --from=id python -m id "pyx:trusted-access"
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:
echo "::add-mask::${oidc_token}"
2

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:
oidc_token="..." # Step 1

# 'acme' is your workspace on pyx
resp=$(
  curl --silent -X POST \
    "https://api.pyx.dev/v1/trusted-access/acme/mint-token" \
    -d "{\"token\": \"${oidc_token}\"}"
)
auth_token=$(jq -r '.token' <<< "${resp}")
3

Read from pyx

Finally, use the obtained pyx access token to read from the views you’ve enrolled the accessor against. For example:
PYX_AUTH_TOKEN="${auth_token}" uv sync
The example below shows how you can use Trusted Access from GitHub Actions:
ci.yml
jobs:
  test:
    runs-on: ubuntu-latest
    permissions:
      id-token: write # Required to request OIDC tokens
      contents: read # Required to checkout code (in private repos)
    steps:
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          persist-credentials: false
      - name: Trusted Access exchange
        id: trusted-access
        run: |
          oidc_token=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=pyx:trusted-access")
          echo "::add-mask::${oidc_token}"

          resp=$(
            curl --silent -X POST \
              "https://api.pyx.dev/v1/trusted-access/acme/mint-token" \
              -d "{\"token\": \"${oidc_token}\"}"
          )
          auth_token=$(jq -r '.token' <<< "${resp}")
          echo "::add-mask::${auth_token}"
          echo "auth_token=${auth_token}" >> "${GITHUB_OUTPUT}"
      - name: Install uv
        uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7.2.0
      # NOTE: This assumes you're using pyx as your default index in uv.
      # You should also change it to reflect your actual test steps.
      - name: Run tests
        run: uv run --dev pytest
        env:
          PYX_AUTH_TOKEN: ${{ steps.trusted-access.outputs.auth_token }}

Authenticating external tools

pyx uses JWT-based authentication. When authenticating via the uv auth login command, uv will store a JWT token on your machine, alongside a refresh token. When making API requests, uv will include the JWT token in the Authorization header. If the JWT token is expired, uv will automatically refresh it using the refresh token. When authenticating via PYX_API_KEY, uv will perform a similar process, but without the need for a refresh token. If necessary, you can use uv auth token pyx.dev to generate a JWT token for use with the pyx API:
 uv auth token pyx.dev
eyJhbGciOiJIU...
This will print a JWT token (with a 1-hour expiry) that you can use to authenticate external tools. For example, you can use the token to view the package metadata for ruff with:
curl -H "Authorization: Bearer $(uv auth token pyx.dev)" https://api.pyx.dev/simple/pypi/ruff
For compatibility, pyx also supports HTTP Basic authentication by setting the username to __token__ and the password to the JWT token, e.g.:
curl -u __token__:$(uv auth token pyx.dev) https://api.pyx.dev/simple/pypi/ruff
In rare cases, it may also be useful to authenticate uv itself with a short-lived JWT rather than an API key by setting the PYX_AUTH_TOKEN environment variable:
export PYX_AUTH_TOKEN=$(uv auth token pyx.dev)

Authenticating with Docker

In Docker builds, we recommend using Docker’s built-in support for build secrets. When invoking docker buildx build, you can pass an API key as a build secret. For example, assuming that your API key is stored in the PYX_API_KEY environment variable, you can pass it as a build secret with:
docker buildx build --secret id=pyx_api_key,env=PYX_API_KEY
In the Dockerfile, you can then reference the pyx_api_key build secret as follows:
RUN --mount=type=secret,id=pyx_api_key,env=PYX_API_KEY \
    --mount=type=cache,target=/root/.cache/uv \
    uv sync
To authenticate a Docker build without an API key (i.e., from a logged-in uv client), you can generate an access token with uv auth token pyx.dev and pass it to Docker as a build secret:
PYX_AUTH_TOKEN=$(uv auth token pyx.dev) && \
    docker buildx build --secret id=pyx_auth_token,env=PYX_AUTH_TOKEN
In the Dockerfile, you can then reference the pyx_auth_token build secret as follows:
RUN --mount=type=secret,id=pyx_auth_token,env=PYX_AUTH_TOKEN \
    --mount=type=cache,target=/root/.cache/uv \
    uv sync
Build secrets do not persist across builds, and do not invalidate layer caches. As such, re-running a Docker build with a regenerated access token will retain the cached layers from the previous build.
If you use Docker’s docker/build-push-action GitHub Action, you can pass the API key as a build secret to the action directly:
ci.yml
- name: Build and Push
  uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
  with:
    secrets: |
      pyx_api_key=${{ secrets.PYX_API_KEY }}
    context: .
    file: Dockerfile
    push: true
    tags: ...

Authenticating with Bazel

Bazel versions 7 and newer allow using a custom credential helper to inject credentials into remote file requests in a secure way without affecting caching. uv v0.9.16 and newer has built-in support for this protocol. Once uv is logged in to pyx, you can use the following .bazelrc to enable the credential helper for all pyx urls:
.bazelrc
common --credential_helper=*.astralhosted.com=%workspace%/bazel/uv-auth-helper
common --credential-helper=*.pyx.dev=%workspace%/bazel/uv-auth-helper
Where bazel/uv-auth-helper is a script that invokes uv auth helper with the right arguments:
bazel/uv-auth-helper
#!/usr/bin/env bash
exec uv auth helper --protocol=bazel "$@"

Authenticating with Dependabot

pyx can be configured as a private registry in GitHub’s Dependabot, allowing Dependabot to update dependencies from pyx. To configure Dependabot to use pyx, add the following to your .github/dependabot.yml:
dependabot.yml
version: 2
updates:
  - package-ecosystem: "uv"
    directory: "/"
    registries:
      - pyx
      - files
    schedule:
      interval: "weekly"
registries:
  files:
    type: python-index
    url: "https://files.astralhosted.com"
    username: __token__
    password: ${{ secrets.PYX_API_KEY }}

  pyx:
    type: python-index
    url: "https://api.pyx.dev/simple/acme/main"
    username: __token__
    password: ${{ secrets.PYX_API_KEY }}
Replace acme/main with the appropriate pyx index for your organization (e.g., public/pypi, astral-sh/cu126, or your organization’s private index).
Both the pyx and files registries must be included. The pyx registry serves the package index, while the files registry serves the package files from the CDN.
Then, add your pyx API key as a repository secret named PYX_API_KEY in your GitHub repository settings under Settings > Secrets and variables > Dependabot.

Authenticating with Artifactory

pyx can be configured as an upstream (remote) repository in JFrog Artifactory, allowing Artifactory to proxy requests to pyx. To configure pyx as a remote repository in Artifactory, first create a read-only API key in the pyx dashboard (pyx-sk-...), then create a PyPI-compatible remote repository with the following settings. In the Basic tab:
SettingValue
URLhttps://files.astralhosted.com
User Name__token__
Password / Access Tokenpyx-sk-...
Enable Token Authentication
Registry URLhttps://api.pyx.dev
Registry Index Location Suffixsimple/acme/main
To ensure that Artifactory can access both the API (api.pyx.dev) and the CDN (files.astralhosted.com), set the following options in the Advanced tab:
SettingValue
Lenient Host Authentication
Bypass HEAD Requests
The remaining settings can be left at their default values.
If you’re configuring a pyx View as an upstream, set the URL to https://views.astralhosted.com instead of https://files.astralhosted.com. When using static IPs, use https://api-static.pyx.dev, https://files-static.astralhosted.com, or https://views-static.astralhosted.com accordingly.

Migrating off the uv alpha

Prior to v0.8.15, pyx support shipped in a separate, pyx-enabled alpha build of uv. If you’re migrating off the uv alpha (e.g., v0.8.12-alpha.3), note the following changes to the authentication interface:
  • uv auth login is now uv auth login pyx.dev
  • uv auth logout is now uv auth logout pyx.dev
  • uv auth token is now uv auth token pyx.dev
  • UV_API_KEY is now PYX_API_KEY (UV_API_KEY will continue to be supported temporarily for backwards-compatibility)
  • UV_AUTH_TOKEN is now PYX_AUTH_TOKEN (UV_AUTH_TOKEN will continue to be supported temporarily for backwards-compatibility)
If you’re already logged-in via the alpha build, you should remain logged-in when upgrading to uv v0.8.15.