Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,41 @@ jobs:
- name: Run Tests
shell: bash
run: |
# Test all features and no features for each package.
# Test all features for each package.
# ref-tests is excluded here and run separately with --test-threads=1
# to avoid exhausting OS thread limits when pocket-ic spawns many threads.
#
# The previous `--no-default-features` pass was dropped: only ic-agent
# and ic-utils have features, ic-utils has no default features (so
# `--no-default-features` was a no-op), and ic-agent's lib tests panic
# without a TLS provider compiled in. Minimal-feature coverage for
# ic-agent is provided by the two dedicated steps below.
for p in $(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name != "ref-tests") | .manifest_path'); do
pushd $(dirname $p)
cargo test --all-features --no-fail-fast
cargo test --no-default-features --no-fail-fast
popd
done
env:
RUST_BACKTRACE: 1

- name: Run Tests (ic-agent tls-ring)
# Exercises tls-ring alone (the minimal-feature path dfinity/ic relies
# on to avoid rustls provider conflicts). The default test pass above
# covers aws-lc-rs alone and both providers together (--all-features).
Comment thread
lwshang marked this conversation as resolved.
Outdated
shell: bash
run: |
cargo test -p ic-agent --no-default-features --features pem,tls-ring --no-fail-fast

- name: Run Tests (ic-agent no-tls panic path)
# When no TLS feature is enabled, building the default reqwest client
# must panic with "No provider set". The integration test asserts this
# via #[should_panic]; run only that test, since the rest of ic-agent's
# lib tests would also panic for the same (expected) reason.
shell: bash
run: |
cargo test -p ic-agent --no-default-features --features pem \
--test crypto_provider_neither --no-fail-fast

- name: Run Tests (WASM)
if: ${{ matrix.os == 'ubuntu-latest' }}
run: |
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

## [0.48.0] - 2026-05-21

* `ic-agent`: Added cargo features `tls-aws-lc-rs` (default) and `tls-ring` to select the rustls crypto provider used by the default `reqwest::Client`. Features are additive: when both are enabled, aws-lc-rs is installed as the process-wide rustls default. Reqwest's `rustls` feature (which hardcoded aws-lc-rs) has been swapped for `rustls-no-provider`; ic-agent now installs the chosen provider via `CryptoProvider::install_default()` on the default-client path, idempotently (an application-installed provider is not overwritten). When the user supplies a client via `AgentBuilder::with_http_client`, ic-agent installs no provider.

### Breaking Changes

* `ic-agent`:
* Default feature set changed from `["pem"]` to `["pem", "tls-aws-lc-rs"]`. Stock-default users are unaffected (aws-lc-rs has been the only crypto provider available since 0.46.0).
* Consumers using `default-features = false` must now opt into a TLS feature, otherwise `Agent::new` panics with "No provider set" when constructing the default reqwest client.
* Migration: add `tls-aws-lc-rs` (matches previous behavior) or `tls-ring` (matches reqwest 0.12 behavior) to the feature list, or supply your own `reqwest::Client` via `AgentBuilder::with_http_client`.
* Example: `ic-agent = { version = "0.48", default-features = false, features = ["pem", "tls-ring"] }`.
* Removed the deprecated `http_transport` module (`ReqwestTransport`, `AgentBuilder::with_transport`, `AgentBuilder::with_arc_transport`), deprecated since 0.38.0.
* Migration: use the dedicated `AgentBuilder` methods (`with_url`, `with_http_client`, `with_arc_route_provider`, `with_max_response_body_size`, `with_max_tcp_error_retries`).

## [0.47.3] - 2026-05-15

* `ic-agent`: Added the `EffectiveId` enum (`Canister(Principal)` | `Subnet(Principal)`) and widened `Agent::update_signed`, `query_signed`, `request_status_signed`, `request_status_raw`, `wait`, `wait_signed`, `read_state_raw`, `verify`, and `sign_request_status` to accept `impl Into<EffectiveId>`. Passing a bare `Principal` is unchanged (treated as `EffectiveId::Canister(_)`); passing `EffectiveId::Subnet(_)` routes to the subnet-scoped HTTP endpoints (`/api/v4/subnet/<id>/call`, `/api/v3/subnet/<id>/read_state`, `/api/v3/subnet/<id>/query`) introduced in IC interface spec 0.60.0.
Expand Down
15 changes: 8 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 6 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ members = [
]

[workspace.package]
version = "0.47.3"
version = "0.48.0"
authors = ["DFINITY Stiftung <sdk@dfinity.org>"]
edition = "2021"
repository = "https://github.com/dfinity/agent-rs"
Expand All @@ -30,10 +30,10 @@ license = "Apache-2.0"
# a comment listing those crates). Otherwise, features are declared in the individual crate Cargo.toml.
#
# The path dependencies below ensure all workspace members use the same version of internal crates.
ic-agent = { path = "ic-agent", version = "0.47.3", default-features = false }
ic-identity-hsm = { path = "ic-identity-hsm", version = "0.47.3" }
ic-transport-types = { path = "ic-transport-types", version = "0.47.3" }
ic-utils = { path = "ic-utils", version = "0.47.3" }
ic-agent = { path = "ic-agent", version = "0.48.0", default-features = false }
ic-identity-hsm = { path = "ic-identity-hsm", version = "0.48.0" }
ic-transport-types = { path = "ic-transport-types", version = "0.48.0" }
ic-utils = { path = "ic-utils", version = "0.48.0" }
ic-utils-bindgen = { path = "ic-utils-bindgen" }
ref-tests = { path = "ref-tests" }

Expand Down Expand Up @@ -88,6 +88,7 @@ rand = "0.10.1"
rangemap = "1.7"
reqwest = { version = "0.13.2", default-features = false }
ring = "0.17"
rustls = { version = "0.23", default-features = false }
sec1 = "0.7.2"
semver = "1.0.7"
serde = "1.0.215"
Expand Down
12 changes: 10 additions & 2 deletions ic-agent/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,15 @@ targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"]
features = ["wasm-bindgen"]

[features]
default = ["pem"]
default = ["pem", "tls-aws-lc-rs"]
pem = ["dep:pem", "pkcs8/pem"]
ring = ["dep:ring"]
# rustls crypto provider selection. Features are additive: if both are enabled,
# aws-lc-rs is installed as the process-wide rustls default (the ring code is
# compiled in but unused). To use ring, set `default-features = false` and
# enable `tls-ring` explicitly.
tls-aws-lc-rs = ["dep:rustls", "rustls/aws-lc-rs"]
tls-ring = ["dep:rustls", "rustls/ring"]
ic_ref_tests = ["default"] # Used to separate integration tests for ic-ref which need a server running.
wasm-bindgen = [
"dep:js-sys",
Expand Down Expand Up @@ -69,8 +75,9 @@ pem = { workspace = true, optional = true }
pkcs8 = { workspace = true, features = ["std"] }
rand = { workspace = true }
rangemap = { workspace = true }
reqwest = { workspace = true, default-features = false, features = ["blocking", "json", "rustls", "stream"] }
reqwest = { workspace = true, default-features = false, features = ["blocking", "json", "rustls-no-provider", "stream"] }
ring = { workspace = true, optional = true }
rustls = { workspace = true, default-features = false, features = ["std", "tls12"], optional = true }
sec1 = { workspace = true, features = ["pem"] }
serde = { workspace = true, features = ["derive"] }
serde_bytes = { workspace = true }
Expand Down Expand Up @@ -103,6 +110,7 @@ tracing-subscriber = { workspace = true }

[target.'cfg(not(target_family = "wasm"))'.dev-dependencies]
mockito = { workspace = true }
rustls = { workspace = true, features = ["aws-lc-rs", "ring"] }
tokio = { workspace = true, features = ["full"] }

[target.'cfg(target_family = "wasm")'.dev-dependencies]
Expand Down
8 changes: 0 additions & 8 deletions ic-agent/src/agent/http_transport/mod.rs

This file was deleted.

116 changes: 0 additions & 116 deletions ic-agent/src/agent/http_transport/reqwest_transport.rs

This file was deleted.

42 changes: 38 additions & 4 deletions ic-agent/src/agent/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
pub(crate) mod agent_config;
pub mod agent_error;
pub(crate) mod builder;
// delete this module after 0.40
#[doc(hidden)]
#[deprecated(since = "0.38.0", note = "use the AgentBuilder methods")]
pub mod http_transport;
pub(crate) mod nonce;
pub(crate) mod response_authentication;
pub mod route_provider;
Expand Down Expand Up @@ -211,6 +207,43 @@ impl fmt::Debug for Agent {
}
}

/// Install a process-wide rustls [`CryptoProvider`] if none is already installed.
///
/// Called when [`Agent::new`] builds its default [`reqwest::Client`]. Reqwest's
/// `rustls-no-provider` feature defers the provider choice to whichever
/// `CryptoProvider` is registered as the process default; this function makes
/// the choice based on the active cargo features so users don't have to install
/// one themselves.
///
/// The call is idempotent: `install_default` returns `Err` if a default was
/// already set, which we ignore. That means an application that installs its
/// own provider before constructing an `Agent` wins.
///
/// Feature precedence: `tls-aws-lc-rs` wins over `tls-ring` when both are
/// enabled, preserving additivity (enabling `tls-ring` on top of the default
/// never silently flips the installed provider).
#[cfg(not(target_family = "wasm"))]
pub(crate) fn install_default_crypto_provider() {
#[cfg(any(feature = "tls-aws-lc-rs", feature = "tls-ring"))]
{
// Cheap fast-path: if a default is already installed (by us on a prior
// `Agent::new`, or by the application), skip constructing a provider.
// The check has a benign TOCTOU race — `install_default()` is itself
// atomic, so concurrent installers still produce a single winner and
// the others' `Err` is discarded.
if rustls::crypto::CryptoProvider::get_default().is_some() {
return;
}
#[cfg(feature = "tls-aws-lc-rs")]
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
#[cfg(all(feature = "tls-ring", not(feature = "tls-aws-lc-rs")))]
let _ = rustls::crypto::ring::default_provider().install_default();
}
// If neither feature is enabled, do nothing. The user is expected to either
// call `with_http_client` or install a provider themselves before building
// the agent; otherwise reqwest will panic when constructing its TLS config.
}

impl Agent {
/// Create an instance of an [`AgentBuilder`] for building an [`Agent`]. This is simpler than
/// using the [`AgentConfig`] and [`Agent::new()`].
Expand All @@ -225,6 +258,7 @@ impl Agent {
client: config.client.unwrap_or_else(|| {
#[cfg(not(target_family = "wasm"))]
{
install_default_crypto_provider();
Client::builder()
.use_rustls_tls()
.timeout(Duration::from_secs(360))
Expand Down
Loading
Loading