From 709e0ba238e7009916f64c2fcb6fab53d5ab714b Mon Sep 17 00:00:00 2001
From: Zanie Blue
Date: Tue, 2 Sep 2025 08:37:12 -0500
Subject: [PATCH] Remove the native system store from the keyring providers
(#15612)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
We're not sure what the best way to expose the native store to users is
yet and it's a bit weird that you can use this in the `uv auth` commands
but can't use any of the other keyring provider options. The simplest
path forward is to just not expose it to users as a keyring provider,
and instead frame it as a preview alternative to the plaintext uv
credentials store. We can revisit the best way to expose configuration
before stabilization.
Note this pull request retains the _internal_ keyring provider
implementation — we can refactor it out later but I wanted to avoid a
bunch of churn here.
---
.config/nextest.toml | 2 +-
.github/workflows/ci.yml | 6 +-
Cargo.lock | 3 +-
crates/uv-auth/Cargo.toml | 1 +
crates/uv-auth/src/middleware.rs | 35 ++-
crates/uv-auth/src/store.rs | 34 +-
crates/uv-client/src/base_client.rs | 6 +-
crates/uv-configuration/Cargo.toml | 2 -
crates/uv-configuration/src/authentication.rs | 16 +-
crates/uv-keyring/Cargo.toml | 2 +-
crates/uv-keyring/src/lib.rs | 4 +-
crates/uv-keyring/src/macos.rs | 2 +-
crates/uv-keyring/src/secret_service.rs | 2 +-
crates/uv-keyring/src/windows.rs | 2 +-
crates/uv-keyring/tests/basic.rs | 2 +-
crates/uv-keyring/tests/threading.rs | 2 +-
crates/uv-preview/src/lib.rs | 6 +-
crates/uv/Cargo.toml | 2 +-
crates/uv/src/commands/auth/login.rs | 8 +-
crates/uv/src/commands/auth/logout.rs | 8 +-
crates/uv/src/commands/auth/mod.rs | 39 ---
crates/uv/src/commands/auth/token.rs | 8 +-
crates/uv/src/commands/publish.rs | 7 +-
crates/uv/src/lib.rs | 20 +-
crates/uv/src/settings.rs | 48 +--
crates/uv/tests/it/auth.rs | 294 ++++--------------
crates/uv/tests/it/show_settings.rs | 4 +-
docs/concepts/authentication/cli.md | 8 +-
docs/concepts/authentication/http.md | 58 ++--
docs/concepts/preview.md | 2 +
docs/reference/cli.md | 23 --
uv.schema.json | 5 -
32 files changed, 205 insertions(+), 456 deletions(-)
diff --git a/.config/nextest.toml b/.config/nextest.toml
index a6c344d22..5692d4073 100644
--- a/.config/nextest.toml
+++ b/.config/nextest.toml
@@ -7,5 +7,5 @@ slow-timeout = { period = "10s", terminate-after = 12 }
serial = { max-threads = 1 }
[[profile.default.overrides]]
-filter = 'test(native_keyring)'
+filter = 'test(native_auth)'
test-group = 'serial'
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 168b96b06..19ed57211 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -254,7 +254,7 @@ jobs:
UV_HTTP_RETRIES: 5
run: |
cargo nextest run \
- --features python-patch,keyring-tests,secret-service \
+ --features python-patch,native-auth,secret-service \
--workspace \
--status-level skip --failure-output immediate-final --no-fail-fast -j 20 --final-status-level slow
@@ -293,7 +293,7 @@ jobs:
run: |
cargo nextest run \
--no-default-features \
- --features python,python-managed,pypi,git,performance,crates-io,keyring-tests,apple-native \
+ --features python,python-managed,pypi,git,performance,crates-io,native-auth,apple-native \
--workspace \
--status-level skip --failure-output immediate-final --no-fail-fast -j 12 --final-status-level slow
@@ -345,7 +345,7 @@ jobs:
run: |
cargo nextest run \
--no-default-features \
- --features python,pypi,python-managed,keyring-tests,windows-native \
+ --features python,pypi,python-managed,native-auth,windows-native \
--workspace \
--status-level skip --failure-output immediate-final --no-fail-fast -j 20 --final-status-level slow
diff --git a/Cargo.lock b/Cargo.lock
index 5107ca912..991f8a208 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5098,6 +5098,7 @@ dependencies = [
"uv-fs",
"uv-keyring",
"uv-once-map",
+ "uv-preview",
"uv-redacted",
"uv-small-str",
"uv-state",
@@ -5415,9 +5416,7 @@ dependencies = [
"uv-pep440",
"uv-pep508",
"uv-platform-tags",
- "uv-preview",
"uv-static",
- "uv-warnings",
]
[[package]]
diff --git a/crates/uv-auth/Cargo.toml b/crates/uv-auth/Cargo.toml
index 690b3cea0..ba194e801 100644
--- a/crates/uv-auth/Cargo.toml
+++ b/crates/uv-auth/Cargo.toml
@@ -13,6 +13,7 @@ workspace = true
uv-fs = { workspace = true }
uv-keyring = { workspace = true, features = ["apple-native", "secret-service", "windows-native"] }
uv-once-map = { workspace = true }
+uv-preview = { workspace = true }
uv-redacted = { workspace = true }
uv-small-str = { workspace = true }
uv-static = { workspace = true }
diff --git a/crates/uv-auth/src/middleware.rs b/crates/uv-auth/src/middleware.rs
index 443389e2a..841998397 100644
--- a/crates/uv-auth/src/middleware.rs
+++ b/crates/uv-auth/src/middleware.rs
@@ -7,6 +7,7 @@ use reqwest::{Request, Response};
use reqwest_middleware::{Error, Middleware, Next};
use tracing::{debug, trace, warn};
+use uv_preview::{Preview, PreviewFeatures};
use uv_redacted::DisplaySafeUrl;
use crate::providers::HuggingFaceProvider;
@@ -118,6 +119,7 @@ pub struct AuthMiddleware {
/// Set all endpoints as needing authentication. We never try to send an
/// unauthenticated request, avoiding cloning an uncloneable request.
only_authenticated: bool,
+ preview: Preview,
}
impl AuthMiddleware {
@@ -129,6 +131,7 @@ impl AuthMiddleware {
cache: None,
indexes: Indexes::new(),
only_authenticated: false,
+ preview: Preview::default(),
}
}
@@ -165,6 +168,13 @@ impl AuthMiddleware {
self
}
+ /// Configure the [`Preview`] features to use.
+ #[must_use]
+ pub fn with_preview(mut self, preview: Preview) -> Self {
+ self.preview = preview;
+ self
+ }
+
/// Configure the [`CredentialsCache`] to use.
#[must_use]
pub fn with_cache(mut self, cache: CredentialsCache) -> Self {
@@ -598,9 +608,30 @@ impl AuthMiddleware {
debug!("Checking text store for credentials for {url}");
text_store.get_credentials(url, credentials.as_ref().and_then(|credentials| credentials.username())).cloned()
}) {
- debug!("Found credentials in text store for {url}");
+ debug!("Found credentials in plaintext store for {url}");
+ Some(credentials)
+ } else if let Some(credentials) = {
+ if self.preview.is_enabled(PreviewFeatures::NATIVE_AUTH) {
+ let native_store = KeyringProvider::native();
+ let username = credentials.and_then(|credentials| credentials.username());
+ let display_username = if let Some(username) = username {
+ format!("{username}@")
+ } else {
+ String::new()
+ };
+ if let Some(index_url) = maybe_index_url {
+ debug!("Checking native store for credentials for index URL {}{}", display_username, index_url);
+ native_store.fetch(DisplaySafeUrl::ref_cast(index_url), username).await
+ } else {
+ debug!("Checking native store for credentials for URL {}{}", display_username, url);
+ native_store.fetch(url, username).await
+ }
+ } else {
+ None
+ }
+ } {
+ debug!("Found credentials in native store for {url}");
Some(credentials)
-
// N.B. The keyring provider performs lookups for the exact URL then falls back to the host.
// But, in the absence of an index URL, we cache the result per realm. So in that case,
// if a keyring implementation returns different credentials for different URLs in the
diff --git a/crates/uv-auth/src/store.rs b/crates/uv-auth/src/store.rs
index 079bbaf21..43d89363a 100644
--- a/crates/uv-auth/src/store.rs
+++ b/crates/uv-auth/src/store.rs
@@ -7,15 +7,47 @@ use serde::{Deserialize, Serialize};
use thiserror::Error;
use url::Url;
use uv_fs::{LockedFile, with_added_extension};
+use uv_preview::{Preview, PreviewFeatures};
use uv_redacted::DisplaySafeUrl;
use uv_state::{StateBucket, StateStore};
use uv_static::EnvVars;
-use crate::Credentials;
use crate::credentials::{Password, Username};
use crate::realm::Realm;
use crate::service::Service;
+use crate::{Credentials, KeyringProvider};
+
+/// The storage backend to use in `uv auth` commands.
+pub enum AuthBackend {
+ // TODO(zanieb): Right now, we're using a keyring provider for the system store but that's just
+ // where the native implementation is living at the moment. We should consider refactoring these
+ // into a shared API in the future.
+ System(KeyringProvider),
+ TextStore(TextCredentialStore, LockedFile),
+}
+
+impl AuthBackend {
+ pub fn from_settings(preview: Preview) -> Result {
+ // If preview is enabled, we'll use the system-native store
+ if preview.is_enabled(PreviewFeatures::NATIVE_AUTH) {
+ return Ok(Self::System(KeyringProvider::native()));
+ }
+
+ // Otherwise, we'll use the plaintext credential store
+ let path = TextCredentialStore::default_file()?;
+ match TextCredentialStore::read(&path) {
+ Ok((store, lock)) => Ok(Self::TextStore(store, lock)),
+ Err(TomlCredentialError::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => {
+ Ok(Self::TextStore(
+ TextCredentialStore::default(),
+ TextCredentialStore::lock(&path)?,
+ ))
+ }
+ Err(err) => Err(err),
+ }
+ }
+}
/// Authentication scheme to use.
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
diff --git a/crates/uv-client/src/base_client.rs b/crates/uv-client/src/base_client.rs
index 9292b2743..d4efe49cb 100644
--- a/crates/uv-client/src/base_client.rs
+++ b/crates/uv-client/src/base_client.rs
@@ -489,13 +489,15 @@ impl<'a> BaseClientBuilder<'a> {
AuthIntegration::Default => {
let auth_middleware = AuthMiddleware::new()
.with_indexes(self.indexes.clone())
- .with_keyring(self.keyring.to_provider(&self.preview));
+ .with_keyring(self.keyring.to_provider())
+ .with_preview(self.preview);
client = client.with(auth_middleware);
}
AuthIntegration::OnlyAuthenticated => {
let auth_middleware = AuthMiddleware::new()
.with_indexes(self.indexes.clone())
- .with_keyring(self.keyring.to_provider(&self.preview))
+ .with_keyring(self.keyring.to_provider())
+ .with_preview(self.preview)
.with_only_authenticated(true);
client = client.with(auth_middleware);
diff --git a/crates/uv-configuration/Cargo.toml b/crates/uv-configuration/Cargo.toml
index 7560dc926..655a115a9 100644
--- a/crates/uv-configuration/Cargo.toml
+++ b/crates/uv-configuration/Cargo.toml
@@ -25,9 +25,7 @@ uv-normalize = { workspace = true }
uv-pep440 = { workspace = true }
uv-pep508 = { workspace = true, features = ["schemars"] }
uv-platform-tags = { workspace = true }
-uv-preview = { workspace = true }
uv-static = { workspace = true }
-uv-warnings = { workspace = true }
clap = { workspace = true, features = ["derive"], optional = true }
either = { workspace = true }
fs-err = { workspace = true }
diff --git a/crates/uv-configuration/src/authentication.rs b/crates/uv-configuration/src/authentication.rs
index 037266b4f..1ad7b5fbb 100644
--- a/crates/uv-configuration/src/authentication.rs
+++ b/crates/uv-configuration/src/authentication.rs
@@ -1,6 +1,4 @@
use uv_auth::{self, KeyringProvider};
-use uv_preview::{Preview, PreviewFeatures};
-use uv_warnings::warn_user_once;
/// Keyring provider type to use for credential lookup.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
@@ -11,8 +9,6 @@ pub enum KeyringProviderType {
/// Do not use keyring for credential lookup.
#[default]
Disabled,
- /// Use a native integration with the system keychain for credential lookup.
- Native,
/// Use the `keyring` command for credential lookup.
Subprocess,
// /// Not yet implemented
@@ -23,18 +19,9 @@ pub enum KeyringProviderType {
// See for details.
impl KeyringProviderType {
- pub fn to_provider(&self, preview: &Preview) -> Option {
+ pub fn to_provider(&self) -> Option {
match self {
Self::Disabled => None,
- Self::Native => {
- if !preview.is_enabled(PreviewFeatures::NATIVE_KEYRING) {
- warn_user_once!(
- "The native keyring provider is experimental and may change without warning. Pass `--preview-features {}` to disable this warning.",
- PreviewFeatures::NATIVE_KEYRING
- );
- }
- Some(KeyringProvider::native())
- }
Self::Subprocess => Some(KeyringProvider::subprocess()),
}
}
@@ -44,7 +31,6 @@ impl std::fmt::Display for KeyringProviderType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Disabled => write!(f, "disabled"),
- Self::Native => write!(f, "native"),
Self::Subprocess => write!(f, "subprocess"),
}
}
diff --git a/crates/uv-keyring/Cargo.toml b/crates/uv-keyring/Cargo.toml
index 48dadc152..624ddd687 100644
--- a/crates/uv-keyring/Cargo.toml
+++ b/crates/uv-keyring/Cargo.toml
@@ -11,7 +11,7 @@ workspace = true
[features]
default = ["apple-native", "secret-service", "windows-native"]
-keyring-tests = []
+native-auth = []
## Use the built-in Keychain Services on macOS
apple-native = ["dep:security-framework"]
diff --git a/crates/uv-keyring/src/lib.rs b/crates/uv-keyring/src/lib.rs
index 9502c91dd..07f6a92fc 100644
--- a/crates/uv-keyring/src/lib.rs
+++ b/crates/uv-keyring/src/lib.rs
@@ -407,12 +407,12 @@ doc_comment::doctest!("../README.md", readme);
/// passing their store-specific parameters for the generic ones.
mod tests {
use super::{Entry, Error};
- #[cfg(feature = "keyring-tests")]
+ #[cfg(feature = "native-auth")]
use super::{Result, credential::CredentialApi};
use std::collections::HashMap;
/// Create a platform-specific credential given the constructor, service, and user
- #[cfg(feature = "keyring-tests")]
+ #[cfg(feature = "native-auth")]
pub(crate) fn entry_from_constructor(f: F, service: &str, user: &str) -> Entry
where
F: FnOnce(Option<&str>, &str, &str) -> Result,
diff --git a/crates/uv-keyring/src/macos.rs b/crates/uv-keyring/src/macos.rs
index cbaeb463a..3595f82fe 100644
--- a/crates/uv-keyring/src/macos.rs
+++ b/crates/uv-keyring/src/macos.rs
@@ -331,7 +331,7 @@ pub fn decode_error(err: Error) -> ErrorCode {
}
}
-#[cfg(feature = "keyring-tests")]
+#[cfg(feature = "native-auth")]
#[cfg(not(miri))]
#[cfg(test)]
mod tests {
diff --git a/crates/uv-keyring/src/secret_service.rs b/crates/uv-keyring/src/secret_service.rs
index 376e466aa..7ec4535d4 100644
--- a/crates/uv-keyring/src/secret_service.rs
+++ b/crates/uv-keyring/src/secret_service.rs
@@ -634,7 +634,7 @@ fn wrap(err: Error) -> Box {
Box::new(err)
}
-#[cfg(feature = "keyring-tests")]
+#[cfg(feature = "native-auth")]
#[cfg(test)]
mod tests {
use crate::credential::CredentialPersistence;
diff --git a/crates/uv-keyring/src/windows.rs b/crates/uv-keyring/src/windows.rs
index 8ef57d46f..eb644f650 100644
--- a/crates/uv-keyring/src/windows.rs
+++ b/crates/uv-keyring/src/windows.rs
@@ -513,7 +513,7 @@ impl std::error::Error for Error {
}
}
-#[cfg(feature = "keyring-tests")]
+#[cfg(feature = "native-auth")]
#[cfg(test)]
mod tests {
use super::*;
diff --git a/crates/uv-keyring/tests/basic.rs b/crates/uv-keyring/tests/basic.rs
index fc833f8ab..4bdbf26aa 100644
--- a/crates/uv-keyring/tests/basic.rs
+++ b/crates/uv-keyring/tests/basic.rs
@@ -1,4 +1,4 @@
-#![cfg(feature = "keyring-tests")]
+#![cfg(feature = "native-auth")]
use common::{generate_random_bytes_of_len, generate_random_string, init_logger};
use uv_keyring::{Entry, Error};
diff --git a/crates/uv-keyring/tests/threading.rs b/crates/uv-keyring/tests/threading.rs
index e2389755d..a67440934 100644
--- a/crates/uv-keyring/tests/threading.rs
+++ b/crates/uv-keyring/tests/threading.rs
@@ -1,4 +1,4 @@
-#![cfg(feature = "keyring-tests")]
+#![cfg(feature = "native-auth")]
use common::{generate_random_string, init_logger};
use uv_keyring::{Entry, Error};
diff --git a/crates/uv-preview/src/lib.rs b/crates/uv-preview/src/lib.rs
index ad30a4c71..4c122edde 100644
--- a/crates/uv-preview/src/lib.rs
+++ b/crates/uv-preview/src/lib.rs
@@ -18,7 +18,7 @@ bitflags::bitflags! {
const EXTRA_BUILD_DEPENDENCIES = 1 << 6;
const DETECT_MODULE_CONFLICTS = 1 << 7;
const FORMAT = 1 << 8;
- const NATIVE_KEYRING = 1 << 9;
+ const NATIVE_AUTH = 1 << 9;
}
}
@@ -37,7 +37,7 @@ impl PreviewFeatures {
Self::EXTRA_BUILD_DEPENDENCIES => "extra-build-dependencies",
Self::DETECT_MODULE_CONFLICTS => "detect-module-conflicts",
Self::FORMAT => "format",
- Self::NATIVE_KEYRING => "native-keyring",
+ Self::NATIVE_AUTH => "native-auth",
_ => panic!("`flag_as_str` can only be used for exactly one feature flag"),
}
}
@@ -84,7 +84,7 @@ impl FromStr for PreviewFeatures {
"extra-build-dependencies" => Self::EXTRA_BUILD_DEPENDENCIES,
"detect-module-conflicts" => Self::DETECT_MODULE_CONFLICTS,
"format" => Self::FORMAT,
- "native-keyring" => Self::NATIVE_KEYRING,
+ "native-auth" => Self::NATIVE_AUTH,
_ => {
warn_user_once!("Unknown preview feature: `{part}`");
continue;
diff --git a/crates/uv/Cargo.toml b/crates/uv/Cargo.toml
index e38a07ef7..db55b1892 100644
--- a/crates/uv/Cargo.toml
+++ b/crates/uv/Cargo.toml
@@ -152,7 +152,7 @@ ignored = [
[features]
default = ["performance", "uv-distribution/static", "default-tests"]
-keyring-tests = []
+native-auth = []
# Use better memory allocators, etc.
performance = ["performance-memory-allocator"]
performance-memory-allocator = ["dep:uv-performance-memory-allocator"]
diff --git a/crates/uv/src/commands/auth/login.rs b/crates/uv/src/commands/auth/login.rs
index 0c740e5d6..ebe85796b 100644
--- a/crates/uv/src/commands/auth/login.rs
+++ b/crates/uv/src/commands/auth/login.rs
@@ -5,13 +5,12 @@ use console::Term;
use owo_colors::OwoColorize;
use uv_auth::Service;
+use uv_auth::store::AuthBackend;
use uv_auth::{Credentials, TextCredentialStore};
-use uv_configuration::KeyringProviderType;
use uv_distribution_types::IndexUrl;
use uv_pep508::VerbatimUrl;
use uv_preview::Preview;
-use crate::commands::auth::AuthBackend;
use crate::{commands::ExitStatus, printer::Printer};
/// Login to a service.
@@ -20,11 +19,10 @@ pub(crate) async fn login(
username: Option,
password: Option,
token: Option,
- keyring_provider: Option,
printer: Printer,
preview: Preview,
) -> Result {
- let backend = AuthBackend::from_settings(keyring_provider.as_ref(), preview)?;
+ let backend = AuthBackend::from_settings(preview)?;
// If the URL includes a known index URL suffix, strip it
// TODO(zanieb): Use a shared abstraction across `login` and `logout`?
@@ -112,7 +110,7 @@ pub(crate) async fn login(
// TODO(zanieb): Add support for other authentication schemes here, e.g., `Credentials::Bearer`
let credentials = Credentials::basic(Some(username), Some(password));
match backend {
- AuthBackend::Keyring(provider) => {
+ AuthBackend::System(provider) => {
provider.store(&url, &credentials).await?;
}
AuthBackend::TextStore(mut store, _lock) => {
diff --git a/crates/uv/src/commands/auth/logout.rs b/crates/uv/src/commands/auth/logout.rs
index e6c2ac7a6..fe7aef71f 100644
--- a/crates/uv/src/commands/auth/logout.rs
+++ b/crates/uv/src/commands/auth/logout.rs
@@ -4,13 +4,12 @@ use anyhow::{Context, Result, bail};
use owo_colors::OwoColorize;
use uv_auth::Service;
+use uv_auth::store::AuthBackend;
use uv_auth::{Credentials, TextCredentialStore};
-use uv_configuration::KeyringProviderType;
use uv_distribution_types::IndexUrl;
use uv_pep508::VerbatimUrl;
use uv_preview::Preview;
-use crate::commands::auth::AuthBackend;
use crate::{commands::ExitStatus, printer::Printer};
/// Logout from a service.
@@ -19,11 +18,10 @@ use crate::{commands::ExitStatus, printer::Printer};
pub(crate) async fn logout(
service: Service,
username: Option,
- keyring_provider: Option,
printer: Printer,
preview: Preview,
) -> Result {
- let backend = AuthBackend::from_settings(keyring_provider.as_ref(), preview)?;
+ let backend = AuthBackend::from_settings(preview)?;
// TODO(zanieb): Use a shared abstraction across `login` and `logout`?
let url = service.url().clone();
@@ -55,7 +53,7 @@ pub(crate) async fn logout(
// TODO(zanieb): Consider exhaustively logging out from all backends
match backend {
- AuthBackend::Keyring(provider) => {
+ AuthBackend::System(provider) => {
provider
.remove(&url, &username)
.await
diff --git a/crates/uv/src/commands/auth/mod.rs b/crates/uv/src/commands/auth/mod.rs
index a781ef8b9..e446e2b1b 100644
--- a/crates/uv/src/commands/auth/mod.rs
+++ b/crates/uv/src/commands/auth/mod.rs
@@ -1,43 +1,4 @@
-use uv_auth::{KeyringProvider, TextCredentialStore, TomlCredentialError};
-use uv_configuration::KeyringProviderType;
-use uv_fs::LockedFile;
-use uv_preview::Preview;
-
pub(crate) mod dir;
pub(crate) mod login;
pub(crate) mod logout;
pub(crate) mod token;
-
-/// The storage backend to use in `uv auth` commands.
-enum AuthBackend {
- Keyring(KeyringProvider),
- TextStore(TextCredentialStore, LockedFile),
-}
-
-impl AuthBackend {
- fn from_settings(
- keyring: Option<&KeyringProviderType>,
- preview: Preview,
- ) -> Result {
- // For keyring providers, we only support persistence via the native keyring right now
- if let Some(keyring) = match keyring {
- Some(provider @ KeyringProviderType::Native) => provider.to_provider(&preview),
- _ => None,
- } {
- return Ok(Self::Keyring(keyring));
- }
-
- // Otherwise, we'll use the plain text credential store
- let path = TextCredentialStore::default_file()?;
- match TextCredentialStore::read(&path) {
- Ok((store, lock)) => Ok(Self::TextStore(store, lock)),
- Err(TomlCredentialError::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => {
- Ok(Self::TextStore(
- TextCredentialStore::default(),
- TextCredentialStore::lock(&path)?,
- ))
- }
- Err(err) => Err(err),
- }
- }
-}
diff --git a/crates/uv/src/commands/auth/token.rs b/crates/uv/src/commands/auth/token.rs
index 6078de301..fa022c196 100644
--- a/crates/uv/src/commands/auth/token.rs
+++ b/crates/uv/src/commands/auth/token.rs
@@ -4,21 +4,19 @@ use anyhow::{Result, bail};
use uv_auth::Credentials;
use uv_auth::Service;
-use uv_configuration::KeyringProviderType;
+use uv_auth::store::AuthBackend;
use uv_preview::Preview;
-use crate::commands::auth::AuthBackend;
use crate::{commands::ExitStatus, printer::Printer};
/// Show the token that will be used for a service.
pub(crate) async fn token(
service: Service,
username: Option,
- keyring_provider: Option,
printer: Printer,
preview: Preview,
) -> Result {
- let backend = AuthBackend::from_settings(keyring_provider.as_ref(), preview)?;
+ let backend = AuthBackend::from_settings(preview)?;
let url = service.url();
// Extract credentials from URL if present
@@ -43,7 +41,7 @@ pub(crate) async fn token(
};
let credentials = match &backend {
- AuthBackend::Keyring(provider) => provider
+ AuthBackend::System(provider) => provider
.fetch(url, Some(&username))
.await
.ok_or_else(|| anyhow::anyhow!("Failed to fetch credentials for {display_url}"))?,
diff --git a/crates/uv/src/commands/publish.rs b/crates/uv/src/commands/publish.rs
index db159db09..f9de28408 100644
--- a/crates/uv/src/commands/publish.rs
+++ b/crates/uv/src/commands/publish.rs
@@ -12,7 +12,6 @@ use uv_cache::Cache;
use uv_client::{AuthIntegration, BaseClient, BaseClientBuilder, RegistryClientBuilder};
use uv_configuration::{KeyringProviderType, TrustedPublishing};
use uv_distribution_types::{IndexCapabilities, IndexLocations, IndexUrl};
-use uv_preview::Preview;
use uv_publish::{
CheckUrlClient, TrustedPublishResult, check_trusted_publishing, files_for_publishing, upload,
};
@@ -35,7 +34,6 @@ pub(crate) async fn publish(
index_locations: IndexLocations,
cache: &Cache,
printer: Printer,
- preview: Preview,
) -> Result {
if client_builder.is_offline() {
bail!("Unable to publish files in offline mode");
@@ -85,7 +83,6 @@ pub(crate) async fn publish(
check_url.as_ref(),
Prompt::Enabled,
printer,
- preview,
)
.await?;
@@ -200,7 +197,6 @@ async fn gather_credentials(
check_url: Option<&IndexUrl>,
prompt: Prompt,
printer: Printer,
- preview: Preview,
) -> Result<(DisplaySafeUrl, Credentials)> {
// Support reading username and password from the URL, for symmetry with the index API.
if let Some(url_password) = publish_url.password() {
@@ -284,7 +280,7 @@ async fn gather_credentials(
// If applicable, fetch the password from the keyring eagerly to avoid user confusion about
// missing keyring entries later.
- if let Some(provider) = keyring_provider.to_provider(&preview) {
+ if let Some(provider) = keyring_provider.to_provider() {
if password.is_none() {
if let Some(username) = &username {
debug!("Fetching password from keyring");
@@ -354,7 +350,6 @@ mod tests {
None,
Prompt::Disabled,
Printer::Quiet,
- Preview::default(),
)
.await
}
diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs
index c1aaeb76e..8016c7349 100644
--- a/crates/uv/src/lib.rs
+++ b/crates/uv/src/lib.rs
@@ -451,7 +451,6 @@ async fn run(mut cli: Cli) -> Result {
args.username,
args.password,
args.token,
- args.keyring_provider,
printer,
globals.preview,
)
@@ -464,14 +463,7 @@ async fn run(mut cli: Cli) -> Result {
let args = settings::AuthLogoutSettings::resolve(args, filesystem);
show_settings!(args);
- commands::auth_logout(
- args.service,
- args.username,
- args.keyring_provider,
- printer,
- globals.preview,
- )
- .await
+ commands::auth_logout(args.service, args.username, printer, globals.preview).await
}
Commands::Auth(AuthNamespace {
command: AuthCommand::Token(args),
@@ -480,14 +472,7 @@ async fn run(mut cli: Cli) -> Result {
let args = settings::AuthTokenSettings::resolve(args, filesystem);
show_settings!(args);
- commands::auth_token(
- args.service,
- args.username,
- args.keyring_provider,
- printer,
- globals.preview,
- )
- .await
+ commands::auth_token(args.service, args.username, printer, globals.preview).await
}
Commands::Auth(AuthNamespace {
command: AuthCommand::Dir,
@@ -1689,7 +1674,6 @@ async fn run(mut cli: Cli) -> Result {
index_locations,
&cache,
printer,
- globals.preview,
)
.await
}
diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs
index cb14c0bcf..3d3cc55fc 100644
--- a/crates/uv/src/settings.rs
+++ b/crates/uv/src/settings.rs
@@ -3490,28 +3490,14 @@ impl PublishSettings {
pub(crate) struct AuthLogoutSettings {
pub(crate) service: Service,
pub(crate) username: Option,
-
- // Both CLI and configuration.
- pub(crate) keyring_provider: Option,
}
impl AuthLogoutSettings {
/// Resolve the [`AuthLogoutSettings`] from the CLI and filesystem configuration.
- pub(crate) fn resolve(args: AuthLogoutArgs, filesystem: Option) -> Self {
- let Options { top_level, .. } = filesystem
- .map(FilesystemOptions::into_options)
- .unwrap_or_default();
-
- let ResolverInstallerSchema {
- keyring_provider, ..
- } = top_level;
-
- let keyring_provider = args.keyring_provider.combine(keyring_provider);
-
+ pub(crate) fn resolve(args: AuthLogoutArgs, _filesystem: Option) -> Self {
Self {
service: args.service,
username: args.username,
- keyring_provider,
}
}
}
@@ -3521,28 +3507,14 @@ impl AuthLogoutSettings {
pub(crate) struct AuthTokenSettings {
pub(crate) service: Service,
pub(crate) username: Option,
-
- // Both CLI and configuration.
- pub(crate) keyring_provider: Option,
}
impl AuthTokenSettings {
/// Resolve the [`AuthTokenSettings`] from the CLI and filesystem configuration.
- pub(crate) fn resolve(args: AuthTokenArgs, filesystem: Option) -> Self {
- let Options { top_level, .. } = filesystem
- .map(FilesystemOptions::into_options)
- .unwrap_or_default();
-
- let ResolverInstallerSchema {
- keyring_provider, ..
- } = top_level;
-
- let keyring_provider = args.keyring_provider.combine(keyring_provider);
-
+ pub(crate) fn resolve(args: AuthTokenArgs, _filesystem: Option) -> Self {
Self {
service: args.service,
username: args.username,
- keyring_provider,
}
}
}
@@ -3554,30 +3526,16 @@ pub(crate) struct AuthLoginSettings {
pub(crate) username: Option,
pub(crate) password: Option,
pub(crate) token: Option,
-
- // Both CLI and configuration.
- pub(crate) keyring_provider: Option,
}
impl AuthLoginSettings {
/// Resolve the [`AuthLoginSettings`] from the CLI and filesystem configuration.
- pub(crate) fn resolve(args: AuthLoginArgs, filesystem: Option) -> Self {
- let Options { top_level, .. } = filesystem
- .map(FilesystemOptions::into_options)
- .unwrap_or_default();
-
- let ResolverInstallerSchema {
- keyring_provider, ..
- } = top_level;
-
- let keyring_provider = args.keyring_provider.combine(keyring_provider);
-
+ pub(crate) fn resolve(args: AuthLoginArgs, _filesystem: Option) -> Self {
Self {
service: args.service,
username: args.username,
password: args.password,
token: args.token,
- keyring_provider,
}
}
}
diff --git a/crates/uv/tests/it/auth.rs b/crates/uv/tests/it/auth.rs
index f1e9ed7d2..a568c3a82 100644
--- a/crates/uv/tests/it/auth.rs
+++ b/crates/uv/tests/it/auth.rs
@@ -1,16 +1,16 @@
-#[cfg(feature = "keyring-tests")]
+#[cfg(feature = "native-auth")]
use anyhow::Result;
use assert_cmd::assert::OutputAssertExt;
-#[cfg(feature = "keyring-tests")]
+#[cfg(feature = "native-auth")]
use assert_fs::{fixture::PathChild, prelude::FileWriteStr};
+#[cfg(feature = "native-auth")]
use uv_static::EnvVars;
-use crate::common::venv_bin_path;
use crate::common::{TestContext, uv_snapshot};
#[test]
-#[cfg(feature = "keyring-tests")]
-fn add_package_native_keyring() -> Result<()> {
+#[cfg(feature = "native-auth")]
+fn add_package_native_auth() -> Result<()> {
let context = TestContext::new("3.12").with_real_home();
// Clear state before the test
@@ -19,8 +19,7 @@ fn add_package_native_keyring() -> Result<()> {
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
.arg("--username")
.arg("public")
- .arg("--keyring-provider")
- .arg("native")
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth")
.status()?;
// Configure `pyproject.toml` with native keyring provider.
@@ -31,20 +30,17 @@ fn add_package_native_keyring() -> Result<()> {
version = "1.0.0"
requires-python = ">=3.11, <4"
dependencies = []
-
- [tool.uv]
- keyring-provider = "native"
"#
})?;
// Try to add a package without credentials.
- uv_snapshot!(context.add().arg("anyio").arg("--default-index").arg("https://public@pypi-proxy.fly.dev/basic-auth/simple"), @r"
+ uv_snapshot!(context.add().arg("anyio").arg("--default-index").arg("https://public@pypi-proxy.fly.dev/basic-auth/simple")
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
× No solution found when resolving dependencies:
╰─▶ Because anyio was not found in the package registry and your project depends on anyio, we can conclude that your project's requirements are unsatisfiable.
@@ -59,26 +55,26 @@ fn add_package_native_keyring() -> Result<()> {
.arg("--username")
.arg("public")
.arg("--password")
- .arg("heron"), @r"
+ .arg("heron")
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Stored credentials for public@https://pypi-proxy.fly.dev/basic-auth
"
);
// Try to add the original package without credentials again. This should use
// credentials storied in the system keyring.
- uv_snapshot!(context.add().arg("anyio").arg("--default-index").arg("https://public@pypi-proxy.fly.dev/basic-auth/simple"), @r"
+ uv_snapshot!(context.add().arg("anyio").arg("--default-index").arg("https://public@pypi-proxy.fly.dev/basic-auth/simple")
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
× No solution found when resolving dependencies:
╰─▶ Because anyio was not found in the package registry and your project depends on anyio, we can conclude that your project's requirements are unsatisfiable.
@@ -91,25 +87,25 @@ fn add_package_native_keyring() -> Result<()> {
uv_snapshot!(context.auth_logout()
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
.arg("--username")
- .arg("public"), @r"
+ .arg("public")
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Removed credentials for public@https://pypi-proxy.fly.dev/basic-auth
"
);
// Authentication should fail again
- uv_snapshot!(context.add().arg("iniconfig").arg("--default-index").arg("https://public@pypi-proxy.fly.dev/basic-auth/simple"), @r"
+ uv_snapshot!(context.add().arg("iniconfig").arg("--default-index").arg("https://public@pypi-proxy.fly.dev/basic-auth/simple")
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
× No solution found when resolving dependencies:
╰─▶ Because iniconfig was not found in the package registry and your project depends on iniconfig, we can conclude that your project's requirements are unsatisfiable.
@@ -122,8 +118,8 @@ fn add_package_native_keyring() -> Result<()> {
}
#[test]
-#[cfg(feature = "keyring-tests")]
-fn token_native_keyring() -> Result<()> {
+#[cfg(feature = "native-auth")]
+fn token_native_auth() -> Result<()> {
let context = TestContext::new_with_versions(&[]).with_real_home();
// Clear state before the test
@@ -132,21 +128,18 @@ fn token_native_keyring() -> Result<()> {
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
.arg("--username")
.arg("public")
- .arg("--keyring-provider")
- .arg("native")
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth")
.status()?;
// Without persisted credentials
uv_snapshot!(context.auth_token()
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: Failed to fetch credentials for https://pypi-proxy.fly.dev/basic-auth/simple
");
@@ -155,14 +148,12 @@ fn token_native_keyring() -> Result<()> {
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
.arg("--username")
.arg("public")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: Failed to fetch credentials for public@https://pypi-proxy.fly.dev/basic-auth/simple
");
@@ -173,14 +164,12 @@ fn token_native_keyring() -> Result<()> {
.arg("public")
.arg("--password")
.arg("heron")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Stored credentials for public@https://pypi-proxy.fly.dev/basic-auth
"
);
@@ -190,14 +179,12 @@ fn token_native_keyring() -> Result<()> {
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
.arg("--username")
.arg("public")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: Failed to fetch credentials for public@https://pypi-proxy.fly.dev/basic-auth/simple
");
@@ -205,14 +192,12 @@ fn token_native_keyring() -> Result<()> {
// TODO(zanieb): Add a hint here if we can?
uv_snapshot!(context.auth_token()
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: Failed to fetch credentials for https://pypi-proxy.fly.dev/basic-auth/simple
");
@@ -222,14 +207,12 @@ fn token_native_keyring() -> Result<()> {
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
.arg("--username")
.arg("private")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: Failed to fetch credentials for private@https://pypi-proxy.fly.dev/basic-auth/simple
");
@@ -238,14 +221,12 @@ fn token_native_keyring() -> Result<()> {
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
.arg("--token")
.arg("heron")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Stored credentials for https://pypi-proxy.fly.dev/basic-auth
"
);
@@ -253,14 +234,12 @@ fn token_native_keyring() -> Result<()> {
// Retrieve the token without a username
uv_snapshot!(context.auth_token()
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: Failed to fetch credentials for https://pypi-proxy.fly.dev/basic-auth/simple
");
@@ -274,14 +253,12 @@ fn token_native_keyring() -> Result<()> {
// Retrieve token using URL with embedded username (no --username needed)
uv_snapshot!(context.auth_token()
.arg("https://public@pypi-proxy.fly.dev/basic-auth/simple")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: Failed to fetch credentials for public@https://pypi-proxy.fly.dev/basic-auth/simple
");
@@ -290,14 +267,12 @@ fn token_native_keyring() -> Result<()> {
.arg("https://public@pypi-proxy.fly.dev/basic-auth/simple")
.arg("--username")
.arg("different")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: Cannot specify a username both via the URL and CLI; found `--username different` and `public`
");
@@ -305,90 +280,8 @@ fn token_native_keyring() -> Result<()> {
}
#[test]
-fn token_subprocess_keyring() {
- let context = TestContext::new("3.12");
-
- // Without a keyring on the PATH
- uv_snapshot!(context.auth_token()
- .arg("https://public@pypi-proxy.fly.dev/basic-auth/simple")
- .arg("--keyring-provider")
- .arg("subprocess"), @r"
- success: false
- exit_code: 2
- ----- stdout -----
-
- ----- stderr -----
- error: Failed to fetch credentials for public@https://pypi-proxy.fly.dev/basic-auth/simple
- "
- );
-
- // Install our keyring plugin
- context
- .pip_install()
- .arg(
- context
- .workspace_root
- .join("scripts")
- .join("packages")
- .join("keyring_test_plugin"),
- )
- .assert()
- .success();
-
- // Without credentials available
- uv_snapshot!(context.auth_token()
- .arg("https://public@pypi-proxy.fly.dev/basic-auth/simple")
- .arg("--keyring-provider")
- .arg("subprocess")
- .env(EnvVars::PATH, venv_bin_path(&context.venv)), @r"
- success: false
- exit_code: 2
- ----- stdout -----
-
- ----- stderr -----
- error: Failed to fetch credentials for public@https://pypi-proxy.fly.dev/basic-auth/simple
- "
- );
-
- // Without a username
- // TODO(zanieb): Add a hint here if we can?
- uv_snapshot!(context.auth_token()
- .arg("https://public@pypi-proxy.fly.dev/basic-auth/simple")
- .arg("--keyring-provider")
- .arg("subprocess")
- .env(EnvVars::KEYRING_TEST_CREDENTIALS, r#"{"pypi-proxy.fly.dev": {"public": "heron"}}"#)
- .env(EnvVars::PATH, venv_bin_path(&context.venv)), @r"
- success: false
- exit_code: 2
- ----- stdout -----
-
- ----- stderr -----
- error: Failed to fetch credentials for public@https://pypi-proxy.fly.dev/basic-auth/simple
- "
- );
-
- // With the correct username
- uv_snapshot!(context.auth_token()
- .arg("https://public@pypi-proxy.fly.dev/basic-auth/simple")
- .arg("--keyring-provider")
- .arg("subprocess")
- .arg("--username")
- .arg("public")
- .env(EnvVars::KEYRING_TEST_CREDENTIALS, r#"{"pypi-proxy.fly.dev": {"public": "heron"}}"#)
- .env(EnvVars::PATH, venv_bin_path(&context.venv)), @r"
- success: false
- exit_code: 2
- ----- stdout -----
-
- ----- stderr -----
- error: Cannot specify a username both via the URL and CLI; found `--username public` and `public`
- "
- );
-}
-
-#[test]
-#[cfg(feature = "keyring-tests")]
-fn login_native_keyring() -> Result<()> {
+#[cfg(feature = "native-auth")]
+fn login_native_auth() -> Result<()> {
let context = TestContext::new_with_versions(&[]).with_real_home();
// Clear state before the test
@@ -397,8 +290,7 @@ fn login_native_keyring() -> Result<()> {
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
.arg("--username")
.arg("public")
- .arg("--keyring-provider")
- .arg("native")
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth")
.status()?;
// Without a service name
@@ -419,14 +311,12 @@ fn login_native_keyring() -> Result<()> {
// Without a username (or token)
uv_snapshot!(context.auth_login()
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: No username provided; did you mean to provide `--username` or `--token`?
");
@@ -435,14 +325,12 @@ fn login_native_keyring() -> Result<()> {
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
.arg("--username")
.arg("public")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: No password provided; did you mean to provide `--password` or `--token`?
");
@@ -453,14 +341,12 @@ fn login_native_keyring() -> Result<()> {
.arg("public")
.arg("--password")
.arg("heron")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Stored credentials for public@https://pypi-proxy.fly.dev/basic-auth
"
);
@@ -469,8 +355,8 @@ fn login_native_keyring() -> Result<()> {
}
#[test]
-#[cfg(feature = "keyring-tests")]
-fn login_token_native_keyring() -> Result<()> {
+#[cfg(feature = "native-auth")]
+fn login_token_native_auth() -> Result<()> {
let context = TestContext::new_with_versions(&[]).with_real_home();
// Clear state before the test
@@ -479,8 +365,7 @@ fn login_token_native_keyring() -> Result<()> {
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
.arg("--username")
.arg("__token__")
- .arg("--keyring-provider")
- .arg("native")
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth")
.status()?;
// Successful with token
@@ -488,14 +373,12 @@ fn login_token_native_keyring() -> Result<()> {
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
.arg("--token")
.arg("test-token")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Stored credentials for https://pypi-proxy.fly.dev/basic-auth
"
);
@@ -504,8 +387,8 @@ fn login_token_native_keyring() -> Result<()> {
}
#[test]
-#[cfg(feature = "keyring-tests")]
-fn logout_native_keyring() -> Result<()> {
+#[cfg(feature = "native-auth")]
+fn logout_native_auth() -> Result<()> {
let context = TestContext::new_with_versions(&[]).with_real_home();
// Clear state before the test
@@ -514,8 +397,7 @@ fn logout_native_keyring() -> Result<()> {
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
.arg("--username")
.arg("public")
- .arg("--keyring-provider")
- .arg("native")
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth")
.status()?;
// Without a service name
@@ -536,14 +418,12 @@ fn logout_native_keyring() -> Result<()> {
// Logout before logging in
uv_snapshot!(context.auth_logout()
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Removed credentials for https://pypi-proxy.fly.dev/basic-auth
");
@@ -552,14 +432,12 @@ fn logout_native_keyring() -> Result<()> {
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
.arg("--username")
.arg("public")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: Unable to remove credentials for public@https://pypi-proxy.fly.dev/basic-auth
Caused by: No matching entry found in secure storage
");
@@ -571,14 +449,12 @@ fn logout_native_keyring() -> Result<()> {
.arg("public")
.arg("--password")
.arg("heron")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Stored credentials for public@https://pypi-proxy.fly.dev/basic-auth
"
);
@@ -587,14 +463,12 @@ fn logout_native_keyring() -> Result<()> {
// TODO(zanieb): Add a hint here if we can?
uv_snapshot!(context.auth_logout()
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: Unable to remove credentials for https://pypi-proxy.fly.dev/basic-auth
Caused by: No matching entry found in secure storage
");
@@ -604,14 +478,12 @@ fn logout_native_keyring() -> Result<()> {
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
.arg("--username")
.arg("public")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Removed credentials for public@https://pypi-proxy.fly.dev/basic-auth
");
@@ -623,22 +495,19 @@ fn logout_native_keyring() -> Result<()> {
.arg("public")
.arg("--password")
.arg("heron")
- .arg("--keyring-provider")
- .arg("native")
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth")
.assert()
.success();
// Logout with a username in the URL
uv_snapshot!(context.auth_logout()
.arg("https://public@pypi-proxy.fly.dev/basic-auth/simple")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Removed credentials for public@https://pypi-proxy.fly.dev/basic-auth
");
@@ -647,14 +516,12 @@ fn logout_native_keyring() -> Result<()> {
.arg("https://public@pypi-proxy.fly.dev/basic-auth/simple")
.arg("--username")
.arg("foo")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: Cannot specify a username both via the URL and CLI; found `--username foo` and `public`
");
@@ -663,14 +530,12 @@ fn logout_native_keyring() -> Result<()> {
.arg("https://public@pypi-proxy.fly.dev/basic-auth/simple")
.arg("--token")
.arg("foo")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: When using `--token`, a username cannot not be provided; found: public
");
@@ -678,16 +543,15 @@ fn logout_native_keyring() -> Result<()> {
}
#[test]
-#[cfg(feature = "keyring-tests")]
-fn logout_token_native_keyring() -> Result<()> {
+#[cfg(feature = "native-auth")]
+fn logout_token_native_auth() -> Result<()> {
let context = TestContext::new_with_versions(&[]).with_real_home();
// Clear state before the test
context
.auth_logout()
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
- .arg("--keyring-provider")
- .arg("native")
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth")
.status()?;
// Login with a token
@@ -695,14 +559,12 @@ fn logout_token_native_keyring() -> Result<()> {
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
.arg("--token")
.arg("test-token")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Stored credentials for https://pypi-proxy.fly.dev/basic-auth
"
);
@@ -710,14 +572,12 @@ fn logout_token_native_keyring() -> Result<()> {
// Logout without a username
uv_snapshot!(context.auth_logout()
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Removed credentials for https://pypi-proxy.fly.dev/basic-auth
");
@@ -725,8 +585,8 @@ fn logout_token_native_keyring() -> Result<()> {
}
#[test]
-#[cfg(feature = "keyring-tests")]
-fn login_native_keyring_url() {
+#[cfg(feature = "native-auth")]
+fn login_native_auth_url() {
let context = TestContext::new_with_versions(&[]).with_real_home();
// A domain-only service name gets https:// prepended
@@ -736,14 +596,12 @@ fn login_native_keyring_url() {
.arg("test")
.arg("--password")
.arg("test")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Stored credentials for test@https://example.com/
");
@@ -754,8 +612,7 @@ fn login_native_keyring_url() {
.arg("test")
.arg("--password")
.arg("test")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 2
----- stdout -----
@@ -772,14 +629,12 @@ fn login_native_keyring_url() {
.arg("test")
.arg("--password")
.arg("test")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Stored credentials for test@https://example.com/
");
@@ -790,14 +645,12 @@ fn login_native_keyring_url() {
.arg("test")
.arg("--password")
.arg("test")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Stored credentials for test@https://example.com/
");
@@ -808,8 +661,7 @@ fn login_native_keyring_url() {
.arg("test")
.arg("--password")
.arg("test")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 2
----- stdout -----
@@ -823,14 +675,12 @@ fn login_native_keyring_url() {
// URL with embedded credentials works
uv_snapshot!(context.auth_login()
.arg("https://test:password@example.com/simple")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Stored credentials for test@https://example.com/
");
@@ -839,14 +689,12 @@ fn login_native_keyring_url() {
.arg("https://test@example.com/simple")
.arg("--password")
.arg("password")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Stored credentials for test@https://example.com/
");
@@ -857,14 +705,12 @@ fn login_native_keyring_url() {
.arg("different")
.arg("--password")
.arg("password")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: Cannot specify a username both via the URL and CLI; found `--username different` and `test`
");
@@ -873,14 +719,12 @@ fn login_native_keyring_url() {
.arg("https://test:password@example.com/simple")
.arg("--password")
.arg("different")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: Cannot specify a password both via the URL and CLI
");
@@ -889,14 +733,12 @@ fn login_native_keyring_url() {
.arg("https://test:password@example.com/simple")
.arg("--token")
.arg("some-token")
- .arg("--keyring-provider")
- .arg("native"), @r"
+ .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
- warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: When using `--token`, a username cannot not be provided; found: test
");
}
diff --git a/crates/uv/tests/it/show_settings.rs b/crates/uv/tests/it/show_settings.rs
index cc2a1ea50..fb0d9afd0 100644
--- a/crates/uv/tests/it/show_settings.rs
+++ b/crates/uv/tests/it/show_settings.rs
@@ -7684,7 +7684,7 @@ fn preview_features() {
show_settings: true,
preview: Preview {
flags: PreviewFeatures(
- PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_KEYRING,
+ PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH,
),
},
python_preference: Managed,
@@ -7908,7 +7908,7 @@ fn preview_features() {
show_settings: true,
preview: Preview {
flags: PreviewFeatures(
- PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_KEYRING,
+ PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH,
),
},
python_preference: Managed,
diff --git a/docs/concepts/authentication/cli.md b/docs/concepts/authentication/cli.md
index 6178b231f..5c8890f64 100644
--- a/docs/concepts/authentication/cli.md
+++ b/docs/concepts/authentication/cli.md
@@ -52,9 +52,7 @@ $ uv auth token --username foo example.com
## Configuring the storage backend
-By default, credentials are persisted in plain text to the uv
-[credentials file](./http.md#the-uv-credentials-file).
+Credentials are persisted to the uv [credentials store](./http.md#the-uv-credentials-store).
-If the [native keyring provider](./http.md#the-native-keyring-provider) is enabled, it will be used
-instead, and the credentials will be stored in a secure system store. The native keyring is
-currently experimental, but will become the default in the future.
+By default, credentials are written to a plaintext file. An encrypted system-native storage backend
+can be enabled with `UV_PREVIEW_FEATURES=native-auth`.
diff --git a/docs/concepts/authentication/http.md b/docs/concepts/authentication/http.md
index d9f1835a1..cb60f120f 100644
--- a/docs/concepts/authentication/http.md
+++ b/docs/concepts/authentication/http.md
@@ -6,7 +6,7 @@ Authentication can come from the following sources, in order of precedence:
- The URL, e.g., `https://:@/...`
- A [netrc](#netrc-files) configuration file
-- The uv credentials file
+- The uv credentials store
- A [keyring provider](#keyring-providers) (off by default)
Authentication may be used for hosts specified in the following contexts:
@@ -25,45 +25,39 @@ for storing credentials on a system.
Reading credentials from `.netrc` files is always enabled. The target file path will be loaded from
the `NETRC` environment variable if defined, falling back to `~/.netrc` if not.
-## The uv credentials file
+## The uv credentials store
-uv will read credentials from `~/.local/share/uv/credentials/credentials.toml`. This file is
-currently not intended to be edited manually.
+uv can read and write credentials from a store using the [`uv auth` commands](./cli.md).
-To add or remove credentials, use the [`uv auth` commands](./cli.md).
-
-## Keyring providers
-
-A keyring provider typically fetches credentials from an operating system store.
-
-The keyring providers are not used by default.
-
-### The 'subprocess' keyring provider
-
-The 'subprocess' keyring provider invokes the `keyring` command to fetch credentials.
-
-The expected interface for this is based on the popular [keyring](https://github.com/jaraco/keyring)
-Python package. Similar support is built-in to pip.
-
-Set `--keyring-provider subprocess`, `UV_KEYRING_PROVIDER=subprocess`, or
-`tool.uv.keyring-provider = "subprocess"` to use the provider.
-
-### The 'native' keyring provider
+Credentials are stored in a plaintext file in uv's state directory, e.g.,
+`~/.local/share/uv/credentials/credentials.toml` on Unix. This file is currently not intended to be
+edited manually.
!!! note
- The native keyring provider is in [preview](../preview.md) — it is still experimental and being
- actively developed.
+ A secure, system native storage mechanism is in [preview](../preview.md) — it is still
+ experimental and being actively developed. In the future, this will become the default storage
+ mechanism.
-The native keyring provider uses the secret storage mechanism native to your operating system. On
-macOS, it uses the Keychain Services. On Windows, it uses the Windows Credential Manager. On Linux,
-it uses the DBus-based Secret Service API.
+ When enabled, uv will use the secret storage mechanism native to your operating system. On
+ macOS, it uses the Keychain Services. On Windows, it uses the Windows Credential Manager. On
+ Linux, it uses the DBus-based Secret Service API.
-Currently, uv only searches the native keyring provider for credentials it has added to the secret
-store. To add or remove credentials, use the [`uv auth` commands](./cli.md).
+ Currently, uv only searches the native store for credentials it has added to the secret store —
+ it will not retrieve credentials persisted by other applications.
-Set `--keyring-provider native`, `UV_KEYRING_PROVIDER=native`, or
-`tool.uv.keyring-provider = "native"` to use the provider.
+ Set `UV_PREVIEW_FEATURES=native-auth` to use this storage mechanism.
+
+## Keyring providers
+
+A keyring provider is a concept from `pip` allowing retrieval of credentials from an interface
+matching the popular [keyring](https://github.com/jaraco/keyring) Python package.
+
+The "subprocess" keyring provider invokes the `keyring` command to fetch credentials. uv does not
+support additional keyring provider types at this time.
+
+Set `--keyring-provider subprocess`, `UV_KEYRING_PROVIDER=subprocess`, or
+`tool.uv.keyring-provider = "subprocess"` to use the provider.
## Persistence of credentials
diff --git a/docs/concepts/preview.md b/docs/concepts/preview.md
index 986f7674b..effb389a8 100644
--- a/docs/concepts/preview.md
+++ b/docs/concepts/preview.md
@@ -70,6 +70,8 @@ The following preview features are available:
- `python-upgrade`: Allows
[transparent Python version upgrades](./python-versions.md#upgrading-python-versions).
- `format`: Allows using `uv format`.
+- `native-auth`: Enables storage of credentials in a
+ [system-native location](../concepts/authentication/http.md#the-uv-credentials-store).
## Disabling preview features
diff --git a/docs/reference/cli.md b/docs/reference/cli.md
index 3ba8eb2c9..3f945e73c 100644
--- a/docs/reference/cli.md
+++ b/docs/reference/cli.md
@@ -94,7 +94,6 @@ uv auth login [OPTIONS]
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--managed-pythonRequire use of uv-managed Python versions.
By default, uv prefers using Python versions it manages. However, it will use system Python versions if a uv-managed Python is not installed. This option disables use of system Python versions.
@@ -168,7 +167,6 @@ uv auth logout [OPTIONS]
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--managed-pythonRequire use of uv-managed Python versions.
By default, uv prefers using Python versions it manages. However, it will use system Python versions if a uv-managed Python is not installed. This option disables use of system Python versions.
@@ -238,7 +236,6 @@ uv auth token [OPTIONS]
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--managed-pythonRequire use of uv-managed Python versions.
By default, uv prefers using Python versions it manages. However, it will use system Python versions if a uv-managed Python is not installed. This option disables use of system Python versions.
@@ -447,7 +444,6 @@ uv run [OPTIONS] [COMMAND]
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--link-mode link-modeThe method to use when installing packages from the global cache.
Defaults to clone (also known as Copy-on-Write) on macOS, and hardlink on Linux and Windows.
@@ -853,7 +849,6 @@ uv add [OPTIONS] >
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--link-mode link-modeThe method to use when installing packages from the global cache.
Defaults to clone (also known as Copy-on-Write) on macOS, and hardlink on Linux and Windows.
@@ -1054,7 +1049,6 @@ uv remove [OPTIONS] ...
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--link-mode link-modeThe method to use when installing packages from the global cache.
Defaults to clone (also known as Copy-on-Write) on macOS, and hardlink on Linux and Windows.
@@ -1237,7 +1231,6 @@ uv version [OPTIONS] [VALUE]
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--link-mode link-modeThe method to use when installing packages from the global cache.
Defaults to clone (also known as Copy-on-Write) on macOS, and hardlink on Linux and Windows.
@@ -1436,7 +1429,6 @@ uv sync [OPTIONS]
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--link-mode link-modeThe method to use when installing packages from the global cache.
Defaults to clone (also known as Copy-on-Write) on macOS, and hardlink on Linux and Windows.
@@ -1682,7 +1674,6 @@ uv lock [OPTIONS]
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--link-mode link-modeThe method to use when installing packages from the global cache.
This option is only used when building source distributions.
@@ -1862,7 +1853,6 @@ uv export [OPTIONS]
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--link-mode link-modeThe method to use when installing packages from the global cache.
This option is only used when building source distributions.
@@ -2057,7 +2047,6 @@ uv tree [OPTIONS]
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--link-mode link-modeThe method to use when installing packages from the global cache.
This option is only used when building source distributions.
@@ -2401,7 +2390,6 @@ uv tool run [OPTIONS] [COMMAND]
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--link-mode link-modeThe method to use when installing packages from the global cache.
Defaults to clone (also known as Copy-on-Write) on macOS, and hardlink on Linux and Windows.
@@ -2623,7 +2611,6 @@ uv tool install [OPTIONS]
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--link-mode link-modeThe method to use when installing packages from the global cache.
Defaults to clone (also known as Copy-on-Write) on macOS, and hardlink on Linux and Windows.
@@ -2837,7 +2824,6 @@ uv tool upgrade [OPTIONS] ...
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--link-mode link-modeThe method to use when installing packages from the global cache.
Defaults to clone (also known as Copy-on-Write) on macOS, and hardlink on Linux and Windows.
@@ -4058,7 +4044,6 @@ uv pip compile [OPTIONS] >
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--link-mode link-modeThe method to use when installing packages from the global cache.
This option is only used when building source distributions.
@@ -4354,7 +4339,6 @@ uv pip sync [OPTIONS] ...
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--link-mode link-modeThe method to use when installing packages from the global cache.
Defaults to clone (also known as Copy-on-Write) on macOS, and hardlink on Linux and Windows.
@@ -4628,7 +4612,6 @@ uv pip install [OPTIONS] |--editable May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--link-mode link-modeThe method to use when installing packages from the global cache.
Defaults to clone (also known as Copy-on-Write) on macOS, and hardlink on Linux and Windows.
@@ -4875,7 +4858,6 @@ uv pip uninstall [OPTIONS] >
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--managed-pythonRequire use of uv-managed Python versions.
By default, uv prefers using Python versions it manages. However, it will use system Python versions if a uv-managed Python is not installed. This option disables use of system Python versions.
@@ -5054,7 +5036,6 @@ uv pip list [OPTIONS]
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--managed-pythonRequire use of uv-managed Python versions.
By default, uv prefers using Python versions it manages. However, it will use system Python versions if a uv-managed Python is not installed. This option disables use of system Python versions.
@@ -5230,7 +5211,6 @@ uv pip tree [OPTIONS]
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--managed-pythonRequire use of uv-managed Python versions.
By default, uv prefers using Python versions it manages. However, it will use system Python versions if a uv-managed Python is not installed. This option disables use of system Python versions.
@@ -5472,7 +5452,6 @@ uv venv [OPTIONS] [PATH]
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--link-mode link-modeThe method to use when installing packages from the global cache.
This option is only used for installing seed packages.
@@ -5629,7 +5608,6 @@ uv build [OPTIONS] [SRC]
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--link-mode link-modeThe method to use when installing packages from the global cache.
This option is only used when building source distributions.
@@ -5782,7 +5760,6 @@ uv publish --publish-url https://upload.pypi.org/legacy/ --check-url https://pyp
May also be set with the UV_KEYRING_PROVIDER environment variable.
Possible values:
disabled: Do not use keyring for credential lookup
-native: Use a native integration with the system keychain for credential lookup
subprocess: Use the keyring command for credential lookup
--managed-pythonRequire use of uv-managed Python versions.
By default, uv prefers using Python versions it manages. However, it will use system Python versions if a uv-managed Python is not installed. This option disables use of system Python versions.
diff --git a/uv.schema.json b/uv.schema.json
index e062fca5b..655ce6a84 100644
--- a/uv.schema.json
+++ b/uv.schema.json
@@ -1144,11 +1144,6 @@
"type": "string",
"const": "disabled"
},
- {
- "description": "Use a native integration with the system keychain for credential lookup.",
- "type": "string",
- "const": "native"
- },
{
"description": "Use the `keyring` command for credential lookup.",
"type": "string",