Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions Cargo.lock

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

11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@ tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
# Includes errors, traces, AND Sentry Logs (structured log forwarding).
sentry = { version = "0.47", features = ["tracing", "logs", "reqwest", "rustls", "panic", "backtrace", "contexts", "debug-images", "test"], default-features = false, optional = true }
sentry-tracing = { version = "0.47", optional = true }
# rustls 0.23 explicit CryptoProvider install at boot. We use rustls
# transitively via reqwest (`ring` backend) AND sentry's reqwest (which
# pulls rustls-platform-verifier → `aws-lc-rs` backend). When both
# backends are linked, rustls 0.23 refuses to auto-pick and panics on
# the first TLS connection with the "Could not automatically determine
# the process-level CryptoProvider" message. Direct dep with the `ring`
# feature lets us call `install_default()` at boot to disambiguate
# (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!


# Optional OpenTelemetry integration (off by default, enable with --features otel).
# HTTP/protobuf transport at runtime, reqwest-based, rustls — same rationale
Expand Down
35 changes: 35 additions & 0 deletions src/core/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,24 @@ pub fn init(mode: LogMode, verbose: bool) -> InitGuards {
_ => EnvFilter::new("info"),
};

// rc.8 panic fix: rustls 0.23 ships with multiple crypto backends
// (we transitively link both `ring` via reqwest 0.12's rustls-tls
// feature and `aws-lc-rs` via sentry's reqwest 0.13 →
// rustls-platform-verifier chain). When both are present, rustls
// refuses to auto-pick and panics with "Could not automatically
// determine the process-level CryptoProvider" on the FIRST TLS
// connection. The canonical fix is to install one explicitly at
// boot, before any TLS use. We pick `ring` because it matches the
// backend the rest of the binary uses (jsonwebtoken, reqwest's
// rustls-tls feature).
//
// `install_default()` returns `Err` if a provider was already
// installed — safe to ignore because the first installer wins and
// we don't care who got there first. Both `ring` and `aws-lc-rs`
// implement the same TLS standards; consistency at the process
// level is what matters.
install_crypto_provider();

// Init Sentry first (before subscriber) so sentry-tracing layer can be wired in.
let sentry_guard = init_sentry();

Expand Down Expand Up @@ -166,6 +184,23 @@ fn before_send(
Some(event)
}

/// Install the rustls 0.23 process-level `CryptoProvider`.
///
/// Pinned to `ring` because that's the backend the rest of the binary
/// already uses (jsonwebtoken signs/verifies with ring; reqwest's
/// rustls-tls feature pulls ring transitively). The `--features sentry`
/// build additionally links `aws-lc-rs` via sentry's reqwest 0.13 →
/// rustls-platform-verifier dependency chain, which is what triggered
/// the rc.8 panic. Calling install_default() here disambiguates before
/// any TLS connection.
///
/// Safe to call multiple times across the process — `install_default()`
/// returns `Err` after the first successful install and we discard
/// that result. No `Once` wrapper needed.
fn install_crypto_provider() {
let _ = rustls::crypto::ring::default_provider().install_default();
}

/// Initialize Sentry if a DSN is configured. Returns `None` when Sentry is
/// disabled (no DSN, or feature not compiled in).
fn init_sentry() -> Option<SentryGuard> {
Expand Down