> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pyx.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

## 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](https://docs.astral.sh/uv/getting-started/installation)
with our standalone installers, or with your package manager of choice:

```shell theme={null}
curl -LsSf https://astral.sh/uv/install.sh | sh
```

Once installed, you can authenticate the uv client with:

```shell theme={null}
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](/contact).

<Note>
  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.
</Note>

To verify that you've authenticated successfully, you can install a package from
the pyx PyPI mirror with:

```shell theme={null}
uv pip install ruff --default-index https://api.pyx.dev/simple/pypi
```

You can un-authenticate the uv client with:

```shell theme={null}
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](https://app.pyx.dev) and authenticate the client by setting the
`PYX_API_KEY` environment variable:

```shell theme={null}
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

<Note>
  Trusted Access is currently in preview. If you're interested in using Trusted
  Access for your CI/CD systems, please [get in touch](/contact).
</Note>

**Trusted Access** is an authentication method that uses
[OpenID Connect](https://openid.net/connect/) to allow CI/CD systems to read
from pyx without needing to manage long-lived API tokens. It's like
[Trusted Publishing](/publishing#trusted-publishing), but for read instead of
write access.

### Supported providers

pyx supports three Trusted Access providers:

* [GitHub Actions](https://github.com/features/actions)
* [GitLab CI/CD](https://docs.gitlab.com/ee/ci/)
* [Generic OIDC IdPs](https://openid.net/connect/)

<Note>
  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.
</Note>

### Enrolling a Trusted Accessor

You can enroll a Trusted Accessor on the [pyx dashboard](https://app.pyx.dev)
under [Team > Trusted Access](https://app.pyx.dev/team/access).

A Trusted Accessor can be given access to one or more non-default views, or to
your entire workspace.

<Note>Only team administrators can enroll Trusted Accessors.</Note>

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.

<Tip>
  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.
</Tip>

<Tabs>
  <Tab title="GitHub Actions" icon="github">
    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](https://docs.python.org/3/library/fnmatch.html)
      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](https://docs.python.org/3/library/fnmatch.html)
      pattern that constrains the OIDC subject claim (`sub`) in the token
      presented by GitHub Actions.
      See GitHub's [example subject claims](https://docs.github.com/en/actions/reference/security/oidc#example-subject-claims)
      for details.
  </Tab>

  <Tab title="GitLab CI/CD" icon="gitlab">
    A GitLab CI/CD Trusted Accessor has one mandatory component:

    * The **Project pattern** specifies which projects can use this accessor.
      This pattern uses a restricted subset of [fnmatch](https://docs.python.org/3/library/fnmatch.html)
      syntax.

      For example, `acme/example` would allow only the `acme/example` project to use
      this accessor, while `acme/*` would allow any project under the `acme`
      group on GitLab.

    In addition, there are optional constraints you can add to your accessor:

    * The **Subject pattern** is an *optional* [fnmatch](https://docs.python.org/3/library/fnmatch.html)
      pattern that constrains the OIDC subject claim (`sub`) in the token
      presented by GitLab CI/CD.
      See GitLab's [example subject claims](https://docs.gitlab.com/ee/ci/openid_connect_provider/#example-subject-claims)
      for details.
  </Tab>

  <Tab title="Generic OIDC IdP" icon="lock">
    A Generic OIDC Trusted Accessor has two mandatory components:

    * The **Issuer URL** is the URL of the OIDC provider that will be used to
      authenticate to pyx. This must exactly match the `iss` claim in any OIDC
      tokens presented to pyx.

          <Note>
            The Issuer URL must use HTTPS, and pyx must be able to perform
            OpenID Connect discovery on its domain. For example, for an Issuer
            URL of `https://acme.example.com`, pyx will attempt to fetch
            `https://acme.example.com/.well-known/openid-configuration`.
          </Note>

    * The **Subject pattern** is a [fnmatch](https://docs.python.org/3/library/fnmatch.html)
      pattern that constrains the OIDC subject claim (`sub`) in the token
      presented to pyx.

      This pattern is mandatory for "generic" accessors, as there are no
      other mandatory, specific claims to constrain.
  </Tab>
</Tabs>

### 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.

<Note>
  A future version of uv will automatically perform the flow below. Until then,
  beta users of Trusted Access must explicitly perform these exchange steps.
</Note>

<Steps>
  <Step title="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:

    * [GitHub Actions: Methods for requesting the OIDC token](https://docs.github.com/en/actions/reference/security/oidc#methods-for-requesting-the-oidc-token)
    * [Buildkite: `buildkite-agent oidc`](https://buildkite.com/docs/agent/v3/cli/reference/oidc)
    * [GitLab CI/CD: Configure ID tokens in a CI/CD job](https://docs.gitlab.com/ci/secrets/id_token_authentication/#configure-id-tokens-in-a-cicd-job)

    You can use the [id](https://pypi.org/p/id) package to automate this for many
    providers:

    ```shell theme={null}
    uvx --from=id python -m id "pyx:trusted-access"
    ```

    <Tip>
      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:

      ```shell theme={null}
      echo "::add-mask::${oidc_token}"
      ```
    </Tip>
  </Step>

  <Step title="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:

    ```shell theme={null}
    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" \
        --json "{\"token\": \"${oidc_token}\"}"
    )
    auth_token=$(jq -r '.token' <<< "${resp}")
    ```
  </Step>

  <Step title="Read from pyx">
    Finally, use the obtained pyx access token to read
    from the views you've enrolled the accessor against. For example:

    ```shell theme={null}
    PYX_AUTH_TOKEN="${auth_token}" uv sync
    ```
  </Step>
</Steps>

The example below shows how you can use Trusted Access from GitHub Actions:

```yaml ci.yml icon="square-code" theme={null}
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" \
              --json "{\"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:

```shell theme={null}
❯ 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:

```shell theme={null}
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.:

```shell theme={null}
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:

```shell theme={null}
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](https://docs.docker.com/build/building/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:

```shell theme={null}
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:

```dockerfile theme={null}
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:

```shell theme={null}
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:

```dockerfile theme={null}
RUN --mount=type=secret,id=pyx_auth_token,env=PYX_AUTH_TOKEN \
    --mount=type=cache,target=/root/.cache/uv \
    uv sync
```

<Note>
  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.
</Note>

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:

```yaml ci.yml icon="square-code" theme={null}
- 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](https://github.com/bazelbuild/proposals/blob/main/designs/2022-06-07-bazel-credential-helpers.md)
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](#authenticating-the-uv-client), you can use the
following `.bazelrc` to enable the credential helper for all pyx urls:

```bazelrc title=".bazelrc" icon="file" theme={null}
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:

```shell title="bazel/uv-auth-helper" icon="square-code" theme={null}
#!/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](https://docs.github.com/en/code-security/dependabot), allowing
Dependabot to update dependencies from pyx.

To configure Dependabot to use pyx, add the following to your
`.github/dependabot.yml`:

```yaml dependabot.yml icon="file" theme={null}
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).

<Note>
  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.
</Note>

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](https://app.pyx.dev) (`pyx-sk-...`), then create
a PyPI-compatible remote repository with the following settings.

In the **Basic** tab:

| Setting                        |                            Value |
| :----------------------------- | -------------------------------: |
| URL                            | `https://files.astralhosted.com` |
| User Name                      |                      `__token__` |
| Password / Access Token        |                     `pyx-sk-...` |
| Enable Token Authentication    |                                ✅ |
| Registry URL                   |            `https://api.pyx.dev` |
| Registry Index Location Suffix |               `simple/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:

| Setting                     | Value |
| :-------------------------- | ----: |
| Lenient Host Authentication |     ✅ |
| Bypass HEAD Requests        |     ✅ |

The remaining settings can be left at their default values.

<Note>
  If you're configuring a pyx [View](/views) as an upstream, set the URL to
  `https://views.astralhosted.com` instead of `https://files.astralhosted.com`.
  When using [static IPs](/static-ips), use `https://api-static.pyx.dev`,
  `https://files-static.astralhosted.com`, or
  `https://views-static.astralhosted.com` accordingly.
</Note>

## 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.
