Skip to main content

uv-pkcs11

PyPI

An unofficial fork of uv — the extremely fast Python package and project manager — with PKCS#11 client-certificate (mTLS) support, so uv can authenticate to package indexes with keys held in PKCS#11 providers that never expose the private key.

The fork never logs in to a token, so it works with providers whose certificate and key are usable without a PIN: software HSMs, network HSMs unlocked out of band, and p11-kit-proxied providers configured for loginless use. Ordinary PIN-protected smart cards and tokens that require C_Login before private-key use are not supported. The PKCS#11 support itself is new (beta): tested end to end against SoftHSM, with limited real-world provider mileage so far.

This project is not affiliated with or endorsed by Astral. The fork lives at github.com/dtrodrigues/uv-pkcs11 and is published on PyPI as uv-pkcs11; for everything except the PKCS#11 additions, see the upstream documentation.

Usage

Client-certificate behavior is controlled entirely by SSL_CLIENT_CERT:

  • A pkcs11: URI (an RFC 7512 subset) selects a PKCS#11 identity:

    $ export SSL_CLIENT_CERT='pkcs11:?module-path=/path/to/pkcs11-module.so'
    $ uv pip install --index-url https://my-mtls-index.example.com/simple/ some-package
    

    The path attributes token, serial, id (percent-encoded CKA_ID), and object (certificate label) narrow the match when tokens hold more than one identity, for example pkcs11:id=%01 or pkcs11:token=MyToken; type=cert is accepted, other attributes are rejected. The module-path query attribute must be an absolute path, and the named module is native code loaded into the process — only point it at a module you trust. Without a module-path query attribute, the p11-kit proxy (p11-kit-proxy.so; p11-kit-proxy.dylib on macOS) is loaded, picking up any module registered with p11-kit. Exactly one identity must match, otherwise uv reports an error naming the candidates.

  • A file path is a PEM client certificate and key, exactly as in upstream uv.

  • Unset means no client certificate — stock uv behavior. PKCS#11 is never activated implicitly.

Notes:

  • No PIN is presented and no login is performed: the certificate/key pair must be visible in a public session (providers unlocked out of band work as-is; PIN-protected devices requiring login will not). pin-value/pin-source URI attributes are rejected.
  • RSA only (PKCS#1 v1.5 and PSS with SHA-256/384/512); the identity applies only to verified HTTPS connections, never to hosts marked --allow-insecure-host.
  • Hash-and-sign mechanisms (CKM_SHA*_RSA_PKCS, CKM_SHA*_RSA_PKCS_PSS) are preferred; providers that only expose the raw CKM_RSA_PKCS and CKM_RSA_PKCS_PSS mechanisms work too, with the handshake transcript hashed by uv and the token padding and signing the digest. A provider without CKM_RSA_PKCS_PSS can only authenticate over TLS 1.2, since TLS 1.3 requires RSA-PSS for client certificates. CKM_RSA_X_509 is not used.
  • A key's CKA_ALLOWED_MECHANISMS, where the provider reports it, narrows the mechanisms offered for that key, so a key restricted to part of the token's mechanism list advertises only the schemes it can actually produce.
  • Only the leaf certificate is sent; intermediates must be known to the server.
  • A certificate is only considered when it can sign as a TLS client: its keyUsage, if present, must include digitalSignature (a keyEncipherment-only RSA key-exchange certificate is passed over), and its extendedKeyUsage, if present, must include clientAuth. Selecting such a certificate explicitly reports the reason; uv-pkcs11-inspect lists it next to the certificate.

Known limitations (kept simple on purpose; both surface as TLS handshake failures rather than discovery-time errors):

  • Pairing trusts the provider's CKA_ID convention: the certificate's public key is not compared against the private key, so a stale or mispaired certificate sharing the key's CKA_ID is selected and fails when the server verifies the handshake signature. Re-provision the token so certificate and key match.
  • Signature schemes are offered based on C_GetMechanismList and the key's CKA_ALLOWED_MECHANISMS: per-mechanism CKF_SIGN flags and key-size ranges from C_GetMechanismInfo are not checked, so a token that lists an RSA mechanism it cannot use with the selected key (for example a key outside the mechanism's supported size range) fails in C_Sign during the handshake.

Diagnosing a token

The wheel installs uv-pkcs11-inspect next to uv and uvx. It lists the tokens, certificates, and private keys a module exposes without login and applies the same selection rules as uv, so its verdict is what uv will do with the same URI. Pass the pkcs11: URI you intend to put in SSL_CLIENT_CERT, or just a module path; with no argument, the p11-kit proxy is inspected. For every identity it finds it prints the URI that names that identity, ready to paste into SSL_CLIENT_CERT. The exit status is 0 when exactly one identity would be selected.

$ uv-pkcs11-inspect 'pkcs11:?module-path=/path/to/pkcs11-module.so'
Module: /path/to/pkcs11-module.so

Token `MyToken` (slot 1)
  Signing: RSA_PSS_SHA512, RSA_PSS_SHA384, RSA_PSS_SHA256, RSA_PKCS1_SHA512, ...
  Certificates: 1
    CKA_ID 3f9a…         label `My Certificate`
  Private keys: 1
    CKA_ID 3f9a…         label `My Key`  RSA_PSS_SHA512, RSA_PSS_SHA384, ...

OK: exactly one usable identity across all tokens: token `MyToken`, CKA_ID 3f9a….
URI: pkcs11:token=MyToken;id=%3f%9a…?module-path=/path/to/pkcs11-module.so

The printed URI names that identity and nothing else: it carries the token label (or the token's serial number, when labels repeat), the CKA_ID, and the certificate's object label when two certificates share the CKA_ID. With several identities it prints AMBIGUOUS and one URI per identity, so you can pass back the one you want; with none it prints NOT USABLE and a hint (for example that nothing is visible without login). uv-pkcs11-inspect --version reports the fork build it came from.

Installation

$ pip install uv-pkcs11

Releases are published to PyPI at pypi.org/project/uv-pkcs11. Wheels are built for Linux (x86_64 and aarch64) and macOS (Apple Silicon); other platforms build from the sdist. The distribution installs the uv and uvx commands and therefore must not be installed alongside the official uv distribution in the same environment — install one or the other.

Versioning

Wheel versions mirror the upstream uv release the fork is built from (e.g. 0.12.9); a fourth component (0.12.9.1, 0.12.9.2, ...) marks a fork-side re-release on the same upstream base. Note that PEP 440 treats 0.12.9.0 as equal to 0.12.9, so re-releases start at .1. uv self update support is intentionally not built in — it would replace this fork with official uv binaries.

The binary identifies itself as the fork: uv --version and uv self version end with a [uv-pkcs11 <fork version>] marker naming the exact fork release (the version itself stays the upstream version so required-version checks keep working), and uv self version --output-format json reports "package_name": "uv-pkcs11" and "fork_version", so you can confirm which build is installed.

$ uv --version
uv 0.12.9 (1a2b3c4d5 2026-09-01 aarch64-apple-darwin) [uv-pkcs11 0.12.9.1]

Release files for uv-pkcs11 0.12.19

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for uv-pkcs11 0.12.19
File Size Uploaded
uv_pkcs11-0.12.19.tar.gz 7.1 MB Details

Built distributions (wheels)

Table of built distributions (wheels) for uv-pkcs11 0.12.19
File Interpreter ABI Platform
uv_pkcs11-0.12.19-py3-none-manylinux_2_28_aarch64.whl Python 3 none Linux glibc 2.28+ ARM64 Details
uv_pkcs11-0.12.19-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details
uv_pkcs11-0.12.19-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details

Total release size: 77.3 MB

Release files / uv_pkcs11-0.12.19.tar.gz

Download URL uv_pkcs11-0.12.19.tar.gz
Size 7.1 MB
Tags Source
SHA-256 checksum
How to use checksums
e07bfc219a1fcf256101818c7b3984cc459e75c3ec3705bf26f8a32687f8afe9
BLAKE2b-256 checksum
How to use checksums
9379effecbfe67a20bc29179d115bf87f4506bfb23c2ccad054a65a6b81a7ef5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / uv_pkcs11-0.12.19-py3-none-manylinux_2_28_aarch64.whl

Download URL uv_pkcs11-0.12.19-py3-none-manylinux_2_28_aarch64.whl
Size 23.9 MB
Tags Linux glibc 2.28+ ARM64 Python 3
SHA-256 checksum
How to use checksums
8d00fca8b5769667b46b7f015b42da16832290e9fb2fb45e2cbcb9258e98a1ba
BLAKE2b-256 checksum
How to use checksums
c0509c11430700e02ac7a78aab09df1cce31ec1f35db84ab569636b2e4b725d7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / uv_pkcs11-0.12.19-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL uv_pkcs11-0.12.19-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 24.9 MB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
77eb9573c598bd9a6ac8636bedb9b1da262fd622cd4fbbaeecb202b6c791c180
BLAKE2b-256 checksum
How to use checksums
15b6a354875eab74a2e335e1d25cd132e30cb547836b0e241afe935c751fbb05
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / uv_pkcs11-0.12.19-py3-none-macosx_11_0_arm64.whl

Download URL uv_pkcs11-0.12.19-py3-none-macosx_11_0_arm64.whl
Size 21.3 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
c34119891ecffd4f104002ffbae80e450fd85b10d2f4f8f5f9b08173bdb158c7
BLAKE2b-256 checksum
How to use checksums
dce383e02be4dae5f27f2af9411066a487c425ab743b9bc54e1dc7a079acbb3c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.12.19 This release

4 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page