alloy-rs / alloy-rs/alloy

[Bug] WebSocket transport hardcodes `webpki-roots`, causing `UnknownIssuer` while HTTP works with system trust store

Open
#3,790 2 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Rust
Stars
1.3k
Forks
668
Avg merge
2d 2h
Merged PRs (30d)
29

Description

### Component

transports

### What version of Alloy are you on?

Alloy version: 1.7.4

### Operating System

macOS (Apple Silicon)

### Describe the bug

Title: WebSocket transport hardcodes `webpki-roots`, causing `UnknownIssuer` while HTTP works with system trust store

## Summary

I ran into a TLS trust-store inconsistency between Alloy's HTTP and WebSocket transports.

In my environment, the same RPC provider works over HTTPS once HTTP is configured to use the system/native trust store, but WebSocket still
fails with:

```text
invalid peer certificate: UnknownIssuer

From code inspection, alloy-transport-ws currently uses tokio-tungstenite with rustls-tls-webpki-roots, which appears to hardcode the WS
trust source to WebPKI roots.

This makes ProviderBuilder::connect_ws(...) fail in environments where the certificate chain is trusted by the OS/system trust store but not
by the WebPKI root set.

## Current behavior

WebSocket connection fails with an error chain like:

IO error: invalid peer certificate: UnknownIssuer
Caused by [1]: IO error: invalid peer certificate: UnknownIssuer
Caused by [2]: invalid peer certificate: UnknownIssuer

In contrast, HTTP to the same provider works once using system/native TLS.

## Expected behavior

WebSocket transport should provide a way to use the system trust store as well, or expose TLS customization so users can align WS behavior
with HTTP behavior.

## Relevant implementation

In the current codebase, alloy-transport-ws depends on:

tokio-tungstenite = { workspace = true, features = ["rustls-tls-webpki-roots"] }

and WsConnect::connect() uses:

tokio_tungstenite::connect_async_with_config(...)

without exposing a custom TLS connector or root-store choice.

## Why this matters

This creates behavior differences between HTTP and WS against the same RPC endpoint:

- HTTP can be made to work with system/native trust
- WS remains pinned to WebPKI roots
- users have no hook to inject a custom TLS connector

This is especially problematic in enterprise/proxied environments or with providers whose cert chain is accepted by the system trust store
but not by WebPKI roots.

## Suggested fixes

Any of these would help:

1. Switch WS transport to native/system roots
2. Expose a custom tokio_tungstenite::Connector
3. Add a configurable TLS/root-store strategy for WS transport

## Environment

- Alloy version: 1.7.4
- Commit: 4e6ff5a6
- OS: macOS

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.