Skip to content

fix(boot): install rustls CryptoProvider to prevent rc.8 panic on first TLS call#143

Merged
miguelrios merged 1 commit into
mainfrom
fix/rustls-crypto-provider-panic
Jun 5, 2026
Merged

fix(boot): install rustls CryptoProvider to prevent rc.8 panic on first TLS call#143
miguelrios merged 1 commit into
mainfrom
fix/rustls-crypto-provider-panic

Conversation

@claudio-michel

@claudio-michel claudio-michel Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

The bug

rc.8 ships with the `--features sentry` binary panicking at first TLS connection. Live event in Sentry: BACKEND-TEST-2RH, level fatal, 2 events under release 0.8.0-rc.8.

```
panic: Could not automatically determine the process-level
CryptoProvider from Rustls crate features. Call
CryptoProvider::install_default() before this point to select a
provider manually, or make sure exactly one of the 'aws-lc-rs' and
'ring' features is enabled.
```

Root cause

Dual rustls crypto backend in the dep graph that the `--features sentry` build triggers:

```
rustls 0.23.37 enables 'ring' AND 'aws-lc-rs' features
├── ring ← jsonwebtoken, reqwest 0.12 (our direct dep, rustls-tls)
└── aws-lc-rs ← rustls-platform-verifier 0.6.2
└── reqwest 0.13.2
└── sentry 0.47 (via 'rustls' feature)
```

The `rustls` feature on the sentry crate (which we enable in #137's expanded feature list) routes to reqwest 0.13 internally, which pulls rustls-platform-verifier → aws-lc-rs. Our own reqwest 0.12 pulls ring. rustls 0.23 sees both backends linked, refuses to auto-pick, and panics on the first TLS handshake.

The default-features binary doesn't hit this because the sentry dep is gated and reqwest 0.13 + rustls-platform-verifier never get pulled.

Fix

```rust
rustls::crypto::ring::default_provider().install_default()
```

Called once at the very start of `logging::init()`, before any TLS client is built (sentry transport, reqwest pools, auth_generator, MCP HTTP transport). Pinned to `ring` because that's the backend the rest of the binary already uses (jsonwebtoken signs with ring; reqwest's rustls-tls pulls ring). `install_default()` returns Err if already installed — we discard that result; first installer wins, both backends implement the same TLS standards, what matters is consistency at the process level.

Verification

End-to-end smoke against a live proxy built with `--features sentry`, pointed at the same Sentry DSN that produced the rc.8 panic:

Probe Result
Proxy boot with sentry DSN configured ✓ Sentry client initialized, no panic
`/health` ✓ 200 OK with rc.8 version
`/tools` ✓ 200 OK, manifest loaded
`/call → https://httpbin.org/get\` (real TLS) ✓ 200 OK with httpbin echo body
Sentry batcher flushes (real TLS to ingest.us.sentry.io) ✓ 8 log items + 3 events, all returned event IDs
Proxy uptime after 4 TLS roundtrips + Sentry flushes ✓ Stayed alive
`grep panic|CryptoProvider|aws-lc|FATAL` in proxy log ✓ Zero matches

Test suite under `--features sentry`:

```
cargo test --features sentry → 1256 passed / 0 failed
cargo clippy --features sentry --lib --tests -- -D warnings → clean
cargo fmt --all --check → clean
```

After this lands

  • Bump PR for v0.8.0-rc.9 against main, cut the tag.
  • This bug is latent on v0.7.14 / v0.7.15 too (same sentry feature set, same dep graph). If those binaries are still serving traffic, backport. Otherwise let the 0.7 line stay sunset and require those environments to move to rc.9.

Sister files

The race fix from PR #141's Greptile review is still orphaned on the now-merged `fix/lazy-schema-discovery` branch as commit `f1f7809`. Not included here — that's a separate P2 worth its own small PR.

🤖 Generated with Claude Code

…anic

rc.8 ships with the --features sentry binary panicking at first TLS
connection:

  panic: Could not automatically determine the process-level
  CryptoProvider from Rustls crate features. Call
  CryptoProvider::install_default() before this point to select a
  provider manually, or make sure exactly one of the 'aws-lc-rs' and
  'ring' features is enabled.

  Sentry issue BACKEND-TEST-2RH, 2 events, release 0.8.0-rc.8.

Root cause is a dual-crypto-backend link in the dep graph that the
--features sentry build triggers:

  rustls 0.23.37 enables 'ring' AND 'aws-lc-rs' features
  ├── ring ← jsonwebtoken, reqwest 0.12 (our direct dep, rustls-tls)
  └── aws-lc-rs ← rustls-platform-verifier 0.6.2
                  └── reqwest 0.13.2
                      └── sentry 0.47 (via 'rustls' feature)

The 'rustls' feature on the sentry crate (which we enable in #137's
expanded feature list) routes to reqwest 0.13 internally, which pulls
rustls-platform-verifier → aws-lc-rs. Our own reqwest 0.12 pulls ring.
rustls 0.23 sees both, refuses to auto-pick, and panics on the first
handshake.

Fix:

  rustls::crypto::ring::default_provider().install_default()

Called once at the very start of `logging::init()`, before any TLS
client is built (sentry's transport, reqwest pools, the proxy's
auth_generator, MCP HTTP transport). Pinned to `ring` because that's
the backend the rest of the binary already uses (jsonwebtoken signs
with ring; reqwest's rustls-tls feature pulls ring). install_default()
returns Err if a provider was already installed — we discard that
result; first installer wins, both backends implement the same TLS
standards, what matters is consistency at the process level.

Verification:

End-to-end smoke against a live proxy built with --features sentry,
pointed at the same Sentry DSN that produced the rc.8 panic:

  ✓ Proxy boots clean (no panic, no CryptoProvider error in log)
  ✓ /health → 200 OK with rc.8 version
  ✓ /tools → 200 OK, manifest loaded
  ✓ /call → real TLS handshake to https://httpbin.org/get, 200 OK
  ✓ Sentry batcher flushed 8 log items + 3 events to
    o4506617108561920.ingest.us.sentry.io, all returned event IDs
  ✓ Proxy stayed alive across 4 TLS roundtrips + Sentry flushes
  ✓ Zero matches for panic|CryptoProvider|aws-lc|FATAL in proxy log

Full test suite under --features sentry: 1256 passed / 0 failed.
cargo clippy --features sentry --lib --tests -- -D warnings clean.
cargo fmt --all --check clean.

This bug was latent in v0.7.14 / v0.7.15 too (same sentry feature
set). Will be backported to release/0.7 in a separate PR if those
binaries are still in service.
@greptile-apps

greptile-apps Bot commented Jun 5, 2026

Copy link
Copy Markdown

Greptile Summary

This PR installs an explicit rustls CryptoProvider (ring) at process boot to prevent the panic introduced in rc.8 when the --features sentry binary encounters both ring and aws-lc-rs backends in the dep graph. The root cause (sentry's reqwest 0.13 pulling rustls-platform-verifieraws-lc-rs, while our reqwest 0.12 pulls ring) is well-documented and the canonical fix is applied correctly.

  • Adds rustls = { version = \"0.23\", features = [\"ring\", \"std\"] } as a non-optional direct dep and calls install_crypto_provider() unconditionally from logging::init(), even for non-sentry builds where there is only one backend and no panic risk.
  • install_default() returns Err if already installed and the result is intentionally discarded; the comment and doc-string both explain the rationale correctly.

Confidence Score: 4/5

Safe to merge — the fix correctly installs the ring CryptoProvider before any TLS connection and directly resolves the rc.8 boot panic.

The fix is minimal and targeted: one function call at the top of logging::init(), well ahead of the first TLS handshake. install_default() is idempotent (returns Err on repeat calls, result discarded), and ring is the correct choice given the rest of the binary already uses it via jsonwebtoken and reqwest's rustls-tls feature. The only design question is whether the rustls dep and the provider-install call should be gated on the sentry feature — since the panic only occurs in that build — but the unconditional approach is harmless given rustls was already transitively present through reqwest.

No files require special attention. The Cargo.toml choice to make rustls non-optional is a mild over-reach but introduces no correctness issue.

Important Files Changed

Filename Overview
src/core/logging.rs Adds install_crypto_provider() call at the top of init() and the helper function; fix is correct and well-commented. The call is unconditional (not feature-gated on sentry), which is harmless but slightly over-broad.
Cargo.toml Adds rustls 0.23 with ring + std features as a non-optional direct dep; correct version and features for the fix, but the dep is unconditional rather than gated on the sentry feature where the panic actually occurs.
Cargo.lock Lock file updated to record rustls as a direct dep of ati; no new rustls version introduced — the same 0.23.x that was already transitively present is now explicitly listed.

Sequence Diagram

sequenceDiagram
    participant main
    participant loginit as logging::init
    participant icp as install_crypto_provider
    participant rustls
    participant initsentry as init_sentry
    participant tlssentry as sentry transport TLS
    participant tlsreqwest as reqwest pool TLS

    main->>loginit: init(mode, verbose)
    loginit->>icp: install_crypto_provider()
    icp->>rustls: ring::default_provider().install_default()
    rustls-->>icp: Ok or Err ignored
    icp-->>loginit: return

    loginit->>initsentry: init_sentry()
    initsentry->>tlssentry: sentry::init first TLS handshake
    tlssentry->>rustls: pick CryptoProvider
    rustls-->>tlssentry: ring installed no panic

    loginit-->>main: InitGuards

    main->>tlsreqwest: first HTTP request
    tlsreqwest->>rustls: pick CryptoProvider
    rustls-->>tlsreqwest: ring consistent
Loading

Reviews (1): Last reviewed commit: "fix(boot): install rustls CryptoProvider..." | Re-trigger Greptile

Comment thread Cargo.toml
# (matches the `ring` backend the rest of the binary uses via
# jsonwebtoken + reqwest's rustls-tls feature). Single-binary fix —
# no Cargo.lock surgery needed. rc.8 panic root cause.
rustls = { version = "0.23", default-features = false, features = ["ring", "std"] }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 rustls dep is unconditional — not gated on the sentry feature

The panic only manifests when --features sentry is active (that's the build that pulls both ring via reqwest 0.12 and aws-lc-rs via sentry's reqwest 0.13 → rustls-platform-verifier). Without sentry, the dep graph contains only ring, and rustls 0.23 auto-selects it without panicking. Adding rustls as an always-on direct dep (and always calling install_default() from init()) is harmless — ring was already transitively present — but it permanently widens the explicit dep surface for all build profiles. Consider gating it alongside the sentry feature: rustls = { ..., optional = true } and adding "dep:rustls" to the sentry feature entry in [features].

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@miguelrios miguelrios merged commit 85a2ecc into main Jun 5, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant