From 01c7b7e8c5e31d8f56051ee463e97b92d05608ad Mon Sep 17 00:00:00 2001 From: dittops Date: Sun, 27 Sep 2026 22:50:30 +0530 Subject: [PATCH 1/8] fix(ci): single-quote the string literal in the merge gate's expression GitHub expressions only take single-quoted strings. `join(needs.*.result, ",")` made the whole workflow invalid, so every CI run since e03826e failed in 0 s with a workflow file issue and no job ever ran. The value now arrives through env instead of being spliced into the script. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b40303e4..3fead716 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -383,8 +383,10 @@ jobs: - cross-compile-musl steps: - name: assert all required jobs succeeded + env: + RESULTS: ${{ join(needs.*.result, ',') }} run: | - results='${{ join(needs.*.result, ",") }}' + results="$RESULTS" echo "required job results: $results" IFS=',' read -ra arr <<< "$results" for r in "${arr[@]}"; do From f52f06dac5113143107a5e7e46103b2c2139ab3e Mon Sep 17 00:00:00 2001 From: dittops Date: Sun, 27 Sep 2026 22:52:11 +0530 Subject: [PATCH 2/8] style(plugin-api): rustfmt The rustfmt job never ran while ci.yml was invalid, so these 8 hunks landed unformatted. No code change. Co-Authored-By: Claude Opus 5.5 --- plugin-api/src/lib.rs | 45 ++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/plugin-api/src/lib.rs b/plugin-api/src/lib.rs index 7d7e5d01..0b007ee4 100644 --- a/plugin-api/src/lib.rs +++ b/plugin-api/src/lib.rs @@ -123,7 +123,11 @@ pub struct PluginManifest { impl PluginManifest { /// Create a new plugin manifest with required fields. - pub fn new(id: impl Into, name: impl Into, version: impl Into) -> Self { + pub fn new( + id: impl Into, + name: impl Into, + version: impl Into, + ) -> Self { Self { id: id.into(), name: name.into(), @@ -460,7 +464,11 @@ pub struct STTVTable { /// Send audio data for transcription. /// `audio_data` points to audio bytes, `audio_len` is the byte count. - pub send_audio: extern "C" fn(handle: *mut ProviderHandle, audio_data: *const u8, audio_len: usize) -> FFIResult, + pub send_audio: extern "C" fn( + handle: *mut ProviderHandle, + audio_data: *const u8, + audio_len: usize, + ) -> FFIResult, /// Set the result callback. /// `callback` is called with STT results; `user_data` is passed through. @@ -472,11 +480,8 @@ pub struct STTVTable { /// Set the error callback. /// `callback` is called with error code and message. - pub set_error_callback: extern "C" fn( - handle: *mut ProviderHandle, - callback: ErrorCallbackFn, - user_data: *mut (), - ), + pub set_error_callback: + extern "C" fn(handle: *mut ProviderHandle, callback: ErrorCallbackFn, user_data: *mut ()), /// Get provider info as JSON string. pub get_provider_info: extern "C" fn(handle: *const ProviderHandle) -> RString, @@ -538,7 +543,8 @@ pub struct TTSVTable { /// Send text for synthesis. /// `text` points to the text string; `flush` indicates whether to flush immediately. - pub speak: extern "C" fn(handle: *mut ProviderHandle, text: *const RString, flush: bool) -> FFIResult, + pub speak: + extern "C" fn(handle: *mut ProviderHandle, text: *const RString, flush: bool) -> FFIResult, /// Clear queued text. pub clear: extern "C" fn(handle: *mut ProviderHandle) -> FFIResult, @@ -554,11 +560,8 @@ pub struct TTSVTable { ), /// Set the error callback. - pub set_error_callback: extern "C" fn( - handle: *mut ProviderHandle, - callback: ErrorCallbackFn, - user_data: *mut (), - ), + pub set_error_callback: + extern "C" fn(handle: *mut ProviderHandle, callback: ErrorCallbackFn, user_data: *mut ()), /// Set the completion callback. pub set_complete_callback: extern "C" fn( @@ -636,7 +639,11 @@ pub struct RealtimeVTable { pub is_ready: extern "C" fn(handle: *const ProviderHandle) -> bool, /// Send audio data. - pub send_audio: extern "C" fn(handle: *mut ProviderHandle, audio_data: *const u8, audio_len: usize) -> FFIResult, + pub send_audio: extern "C" fn( + handle: *mut ProviderHandle, + audio_data: *const u8, + audio_len: usize, + ) -> FFIResult, /// Send text message. pub send_text: extern "C" fn(handle: *mut ProviderHandle, text: *const RString) -> FFIResult, @@ -662,11 +669,8 @@ pub struct RealtimeVTable { ), /// Set the error callback. - pub set_error_callback: extern "C" fn( - handle: *mut ProviderHandle, - callback: ErrorCallbackFn, - user_data: *mut (), - ), + pub set_error_callback: + extern "C" fn(handle: *mut ProviderHandle, callback: ErrorCallbackFn, user_data: *mut ()), /// Get provider info as JSON string. pub get_provider_info: extern "C" fn(handle: *const ProviderHandle) -> RString, @@ -764,7 +768,8 @@ pub struct PluginModule { /// Factory function for creating Realtime providers. /// /// Set to `ROption::RNone` if this plugin doesn't provide Realtime. - pub create_realtime: ROption RResult>, + pub create_realtime: + ROption RResult>, } impl RootModule for PluginModule_Ref { From 3842e13c169ed12362394c462b4f66198f588e8d Mon Sep 17 00:00:00 2001 From: dittops Date: Sun, 27 Sep 2026 23:04:33 +0530 Subject: [PATCH 3/8] fix(ci): install the gateway's native build deps; check OpenSSL on the runtime graph Every compiling job failed in webrtc-sys's build script: libva headers not found. A composite action now installs the builder stage's packages from gateway/Dockerfile and sets the same CC/CXX/ZSTD env, in all 9 jobs that compile the gateway. The musl cross-compile job could never pass: livekit links libwebrtc, a glibc C++ prebuilt, and the runners have no musl C++ compiler. It is replaced by the claim it stood for, that no openssl/native-tls crate is in the runtime dependency graph under any feature. ort-sys uses native-tls only as a build dependency, to download ONNX Runtime. Also: rust-cache keys may not contain commas, and one build matrix entry had three. Co-Authored-By: Claude Opus 5.5 --- .github/actions/native-deps/action.yml | 24 +++++++++++ .github/workflows/ci.yml | 60 +++++++++++++++----------- 2 files changed, 58 insertions(+), 26 deletions(-) create mode 100644 .github/actions/native-deps/action.yml diff --git a/.github/actions/native-deps/action.yml b/.github/actions/native-deps/action.yml new file mode 100644 index 00000000..88ca53bd --- /dev/null +++ b/.github/actions/native-deps/action.yml @@ -0,0 +1,24 @@ +name: native build deps +description: >- + The system libraries and compiler the builder stage of gateway/Dockerfile installs. livekit's + webrtc-sys panics in its build script without the libva headers, so every job that compiles + the gateway needs this. Keep the package list and env in step with gateway/Dockerfile. +runs: + using: composite + steps: + - name: install native build deps + shell: bash + run: | + sudo apt-get update -o Acquire::Retries=5 + sudo apt-get install -y --no-install-recommends -o Acquire::Retries=5 \ + clang cmake pkg-config libssl-dev libzstd-dev \ + libva-dev libdrm-dev libglib2.0-dev libgbm-dev \ + libx11-dev libxext-dev libxrandr-dev libxcomposite-dev libxdamage-dev libxfixes-dev + # webrtc-sys enables the NVIDIA codec when /include/cuda.h exists; the + # workflow points CUDA_HOME here, at a directory with no cuda.h. + mkdir -p /tmp/nocuda + { + echo "CC=clang" + echo "CXX=clang++" + echo "ZSTD_SYS_USE_PKG_CONFIG=1" + } >> "$GITHUB_ENV" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3fead716..51687346 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,8 +64,7 @@ jobs: with: { components: clippy } - uses: Swatinem/rust-cache@v2 with: { workspaces: gateway } - - name: prepare nocuda dir - run: mkdir -p /tmp/nocuda + - uses: ./.github/actions/native-deps - run: cargo clippy --all-targets --features ${{ env.PROD_FEATURES }} -- -D warnings build-and-unit: @@ -84,9 +83,9 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - with: { workspaces: gateway, key: "${{ matrix.features }}" } - - name: prepare nocuda dir - run: mkdir -p /tmp/nocuda + # rust-cache keys may not contain commas, and one matrix entry has three. + with: { workspaces: gateway, key: "features-${{ strategy.job-index }}" } + - uses: ./.github/actions/native-deps - name: check run: cargo check ${{ matrix.features && format('--features {0}', matrix.features) || '' }} - name: unit tests @@ -184,6 +183,7 @@ jobs: with: { components: llvm-tools-preview } - uses: Swatinem/rust-cache@v2 with: { workspaces: gateway, key: coverage } + - uses: ./.github/actions/native-deps - name: install cargo-llvm-cov uses: taiki-e/install-action@cargo-llvm-cov - name: provision ONNX Runtime (load-dynamic; BUILD.md Gotcha 6) @@ -231,8 +231,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 with: { workspaces: gateway, key: openapi } - - name: prepare nocuda dir - run: mkdir -p /tmp/nocuda + - uses: ./.github/actions/native-deps - name: drift test (in-memory regen == committed docs/openapi.yaml) run: cargo test --features openapi --test openapi_drift -- --nocapture - name: re-export via CLI and assert the committed artifact is unchanged @@ -258,6 +257,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 with: { workspaces: gateway, key: accuracy } + - uses: ./.github/actions/native-deps - name: cache ONNX models uses: actions/cache@v4 with: @@ -286,8 +286,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 with: { workspaces: gateway } - - name: prepare nocuda dir - run: mkdir -p /tmp/nocuda + - uses: ./.github/actions/native-deps # These exercise the gateway end-to-end against the in-repo mock providers # (tests/mock_providers) — protocol-level coverage for all providers without paid keys. - run: cargo test --features dag-routing --test e2e_mock_tests --test load_test_with_mocks --test server_startup --test ws_tests --test keystone_wire --test provider_keystone_completeness @@ -300,8 +299,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 with: { workspaces: gateway } - - name: prepare nocuda dir - run: mkdir -p /tmp/nocuda + - uses: ./.github/actions/native-deps - run: cargo build --release - name: boot and probe liveness/readiness/metrics run: | @@ -321,21 +319,32 @@ jobs: curl -fsS http://localhost:3001/metrics | grep -q 'waav_provider' || { echo "metrics missing waav_provider series"; kill $PID 2>/dev/null; exit 1; } echo "healthy"; kill $PID 2>/dev/null; exit 0 - cross-compile-musl: - name: cross-compile (musl, rustls) + # --------------------------------------------------------------------------------------- + # no-openssl: the README's "rustls, no OpenSSL dependency". This was a musl cross-compile + # check, but the gateway links livekit's libwebrtc (a glibc C++ prebuilt), so it has never + # built for musl. Asserts on the RUNTIME graph (-e normal), for every feature: ort-sys pulls + # native-tls in as a BUILD dependency to download ONNX Runtime, which ships in no binary. + # --------------------------------------------------------------------------------------- + no-openssl: + name: no OpenSSL at runtime (rustls only) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - with: { targets: x86_64-unknown-linux-musl } - - run: sudo apt-get update && sudo apt-get install -y musl-tools - - uses: Swatinem/rust-cache@v2 - with: { workspaces: gateway } - - name: prepare nocuda dir - run: mkdir -p /tmp/nocuda - # Validates the rustls/no-OpenSSL cross-compilation claim (README). turn/noise features - # are excluded here because their native ONNX/tract deps complicate musl static linking. - - run: cargo check --target x86_64-unknown-linux-musl --features dag-routing + - name: no openssl / native-tls crate in the runtime dependency graph + run: | + set -euo pipefail + for features in "--all-features" "--no-default-features"; do + found=$(cargo tree --locked $features -e normal --prefix none --format '{p}' \ + | grep -E '^(openssl|openssl-sys|native-tls) ' | sort -u || true) + if [ -n "$found" ]; then + echo "::error::OpenSSL reached the runtime graph ($features):" + echo "$found" + cargo tree --locked $features -e normal -i openssl-sys || true + exit 1 + fi + done + echo "runtime graph is rustls-only" real-provider-e2e: name: real-provider e2e (SECRET-GATED) @@ -354,8 +363,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 with: { workspaces: gateway } - - name: prepare nocuda dir - run: mkdir -p /tmp/nocuda + - uses: ./.github/actions/native-deps - name: run #[ignore]d real-provider tests for providers whose secret is present run: cargo test --release --test real_provider_tests -- --ignored --test-threads=1 @@ -363,7 +371,7 @@ jobs: # required: single aggregate context for branch protection. It fails if ANY gating job # failed or was skipped (e.g. a cancelled supply-chain), so requiring this one context in # branch protection makes fmt/clippy/build-matrix/supply-chain/coverage/openapi-drift/ - # accuracy-enforced/integration/server-smoke/cross-compile all effectively required. + # accuracy-enforced/integration/server-smoke/no-openssl all effectively required. # real-provider-e2e is intentionally NOT required (secret-gated, skipped on PRs). # --------------------------------------------------------------------------------------- required: @@ -380,7 +388,7 @@ jobs: - accuracy-enforced - integration-mock - server-smoke - - cross-compile-musl + - no-openssl steps: - name: assert all required jobs succeeded env: From ce11e4b50ad8d2f5ea12cd56df7122d1f58a0886 Mon Sep 17 00:00:00 2001 From: dittops Date: Sun, 27 Sep 2026 23:37:11 +0530 Subject: [PATCH 4/8] fix(ci): provision ONNX Runtime for the unit tests too ort is built with load-dynamic, so core::onnx's unit tests panic without libonnxruntime.so, and build+unit never provisioned it. The download moves into .github/actions/onnxruntime, which build+unit, coverage and accuracy share. Co-Authored-By: Claude Opus 5.5 --- .github/actions/onnxruntime/action.yml | 16 ++++++++++++++++ .github/workflows/ci.yml | 18 ++++-------------- 2 files changed, 20 insertions(+), 14 deletions(-) create mode 100644 .github/actions/onnxruntime/action.yml diff --git a/.github/actions/onnxruntime/action.yml b/.github/actions/onnxruntime/action.yml new file mode 100644 index 00000000..66e756cb --- /dev/null +++ b/.github/actions/onnxruntime/action.yml @@ -0,0 +1,16 @@ +name: ONNX Runtime +description: >- + Downloads ONNX Runtime and points ORT_DYLIB_PATH at it. `ort` is built with load-dynamic + (BUILD.md Gotcha 6), so anything that LOADS a model, core::onnx's unit tests included, + panics without the shared library at run time. The version comes from the workflow's + ORT_VERSION. +runs: + using: composite + steps: + - name: provision ONNX Runtime + shell: bash + run: | + curl -fsSL --retry 5 -o /tmp/ort.tgz \ + "https://github.com/microsoft/onnxruntime/releases/download/v${ORT_VERSION}/onnxruntime-linux-x64-${ORT_VERSION}.tgz" + tar -xzf /tmp/ort.tgz -C /tmp + echo "ORT_DYLIB_PATH=/tmp/onnxruntime-linux-x64-${ORT_VERSION}/lib/libonnxruntime.so" >> "$GITHUB_ENV" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 51687346..ceb224b8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,6 +86,8 @@ jobs: # rust-cache keys may not contain commas, and one matrix entry has three. with: { workspaces: gateway, key: "features-${{ strategy.job-index }}" } - uses: ./.github/actions/native-deps + # core::onnx's unit tests load the runtime whatever the feature set. + - uses: ./.github/actions/onnxruntime - name: check run: cargo check ${{ matrix.features && format('--features {0}', matrix.features) || '' }} - name: unit tests @@ -186,13 +188,7 @@ jobs: - uses: ./.github/actions/native-deps - name: install cargo-llvm-cov uses: taiki-e/install-action@cargo-llvm-cov - - name: provision ONNX Runtime (load-dynamic; BUILD.md Gotcha 6) - run: | - mkdir -p /tmp/nocuda - curl -fsSL -o /tmp/ort.tgz \ - "https://github.com/microsoft/onnxruntime/releases/download/v${ORT_VERSION}/onnxruntime-linux-x64-${ORT_VERSION}.tgz" - tar -xzf /tmp/ort.tgz -C /tmp - echo "ORT_DYLIB_PATH=/tmp/onnxruntime-linux-x64-${ORT_VERSION}/lib/libonnxruntime.so" >> "$GITHUB_ENV" + - uses: ./.github/actions/onnxruntime - name: cargo llvm-cov (lib, production features) # Excludes #[ignore]/live_* by construction (no --ignored, lib targets only). run: | @@ -263,13 +259,7 @@ jobs: with: path: ~/.cache/waav key: waav-models-v1 - - name: provision ONNX Runtime (load-dynamic; BUILD.md Gotcha 6) - run: | - mkdir -p /tmp/nocuda - curl -fsSL -o /tmp/ort.tgz \ - "https://github.com/microsoft/onnxruntime/releases/download/v${ORT_VERSION}/onnxruntime-linux-x64-${ORT_VERSION}.tgz" - tar -xzf /tmp/ort.tgz -C /tmp - echo "ORT_DYLIB_PATH=/tmp/onnxruntime-linux-x64-${ORT_VERSION}/lib/libonnxruntime.so" >> "$GITHUB_ENV" + - uses: ./.github/actions/onnxruntime - name: provision turn-detect model run: CACHE_PATH="$HOME/.cache/waav" cargo run --features turn-detect -- init || true # The accuracy tests now assert precision/recall/F1 + latency thresholds internally From bb36521bd7430d09e678a53bc8dd37b05280f90f Mon Sep 17 00:00:00 2001 From: dittops Date: Sun, 27 Sep 2026 23:37:24 +0530 Subject: [PATCH 5/8] fix(gateway): clear clippy -D warnings on rust 1.98 The clippy job never ran while ci.yml was invalid. All 85 findings across lib, tests, benches and examples: - await_holding_lock (53): every one is a test env or serialisation lock, deliberately held across the test body's awaits. Each #[tokio::test] owns its runtime and thread, so a blocked lock() cannot deadlock the guard's holder. Allowed for test builds only, with the reason recorded; production code is still linted. - chunks_exact(2) over PCM16 -> as_chunks::<2>(); % -> is_multiple_of; (n + d - 1) / d -> div_ceil; a Default for TTSProvider; the 320-byte STTResult is boxed in the observer queue event; orphaned doc comments, a needless lifetime, an explicit loop counter, redundant matches. - The aligned_copy bench ignored copy_from_slice's Result; a failed copy would have benchmarked nothing, so it now fails loudly. The release image builds with rust 1.96; every API used predates it. Co-Authored-By: Claude Opus 5.5 --- gateway/benches/simd_benchmarks.rs | 7 ++++--- gateway/examples/simd_perf_comparison.rs | 4 ++-- gateway/src/core/audio/resampler.rs | 8 +++++--- gateway/src/core/llm/adapter.rs | 6 +++--- .../src/core/observability/async_observer.rs | 7 +++++-- gateway/src/core/stt/batch.rs | 4 ++-- gateway/src/core/stt/groq/client.rs | 4 ++-- gateway/src/core/stt/openai/client.rs | 4 ++-- gateway/src/core/stt/wav.rs | 2 +- gateway/src/core/tts/alibaba_cloud/provider.rs | 2 -- gateway/src/core/tts/iflytek/provider.rs | 2 -- gateway/src/core/tts/provider.rs | 6 ++++++ gateway/src/core/turn_detect/detector.rs | 4 ++-- gateway/src/handlers/openai_audio.rs | 6 ++++-- gateway/src/handlers/voices.rs | 2 +- gateway/src/lib.rs | 13 +++++++++++++ gateway/src/livekit/client/audio.rs | 6 ++++-- gateway/src/utils/simd_ops.rs | 8 ++++---- gateway/tests/dag_realtime_frontend_audio.rs | 6 ++++-- gateway/tests/fixtures/audio_fixtures.rs | 6 ++++-- gateway/tests/livekit_audio_latency_tests.rs | 6 ++++-- gateway/tests/llm_adapter_live_e2e.rs | 17 ++++++++++++----- gateway/tests/openai_stt_integration.rs | 8 ++++++++ gateway/tests/openai_tts_integration.rs | 8 ++++++++ gateway/tests/smart_turn_accuracy_test.rs | 12 ++++++++---- gateway/tests/turn_detect_latency.rs | 4 +--- gateway/tests/unsafe_safety_tests.rs | 12 ++++++++---- 27 files changed, 117 insertions(+), 57 deletions(-) diff --git a/gateway/benches/simd_benchmarks.rs b/gateway/benches/simd_benchmarks.rs index edf60168..3a750f9e 100644 --- a/gateway/benches/simd_benchmarks.rs +++ b/gateway/benches/simd_benchmarks.rs @@ -80,8 +80,8 @@ mod scalar { const PCM_TO_FLOAT_SCALE: f32 = 1.0 / 32768.0; let sample_count = pcm.len() / 2; let mut output = Vec::with_capacity(sample_count); - for chunk in pcm.chunks_exact(2) { - let sample = i16::from_le_bytes([chunk[0], chunk[1]]) as f32; + for chunk in pcm.as_chunks::<2>().0 { + let sample = i16::from_le_bytes(*chunk) as f32; output.push((sample * PCM_TO_FLOAT_SCALE).clamp(-1.0, 1.0)); } output @@ -486,7 +486,8 @@ fn bench_aligned_buffer(c: &mut Criterion) { group.bench_with_input(BenchmarkId::new("aligned_copy", size), &data, |b, data| { let mut dst = simd_ops::AlignedBuffer::::new(size); b.iter(|| { - dst.copy_from_slice(black_box(data)); + dst.copy_from_slice(black_box(data)) + .expect("dst and data are both `size` long"); }); }); } diff --git a/gateway/examples/simd_perf_comparison.rs b/gateway/examples/simd_perf_comparison.rs index 7db76b1d..19e58ee9 100644 --- a/gateway/examples/simd_perf_comparison.rs +++ b/gateway/examples/simd_perf_comparison.rs @@ -624,8 +624,8 @@ fn compare_accuracy(a: &[f32], b: &[f32]) -> (bool, f32, f64) { fn pcm_to_float_scalar(pcm: &[u8]) -> Vec { let sample_count = pcm.len() / 2; let mut output = Vec::with_capacity(sample_count); - for chunk in pcm.chunks_exact(2) { - let sample = i16::from_le_bytes([chunk[0], chunk[1]]) as f32; + for chunk in pcm.as_chunks::<2>().0 { + let sample = i16::from_le_bytes(*chunk) as f32; output.push((sample * PCM_TO_FLOAT_SCALE).clamp(-1.0, 1.0)); } output diff --git a/gateway/src/core/audio/resampler.rs b/gateway/src/core/audio/resampler.rs index 59a4aba7..a929dd52 100644 --- a/gateway/src/core/audio/resampler.rs +++ b/gateway/src/core/audio/resampler.rs @@ -213,7 +213,7 @@ pub fn resample_pcm16( in_rate: u32, out_rate: u32, ) -> Option> { - if pcm.len() % 2 != 0 { + if !pcm.len().is_multiple_of(2) { warn!( bytes = pcm.len(), "malformed PCM16 egress chunk length; dropping chunk instead of truncating a partial sample" @@ -225,8 +225,10 @@ pub fn resample_pcm16( return None; } let samples: Vec = pcm - .chunks_exact(2) - .map(|b| i16::from_le_bytes([b[0], b[1]]) as f32 / 32768.0) + .as_chunks::<2>() + .0 + .iter() + .map(|b| i16::from_le_bytes(*b) as f32 / 32768.0) .collect(); let out = r.resample(&samples, in_rate, out_rate)?; let mut bytes = Vec::with_capacity(out.len() * 2); diff --git a/gateway/src/core/llm/adapter.rs b/gateway/src/core/llm/adapter.rs index b0585d25..f2858b97 100644 --- a/gateway/src/core/llm/adapter.rs +++ b/gateway/src/core/llm/adapter.rs @@ -414,10 +414,10 @@ fn parse_args_object(arguments: &str) -> Value { }) } -fn request_body_object_mut<'a>( - body: &'a mut Value, +fn request_body_object_mut( + body: &mut Value, adapter: AdapterKind, -) -> Option<&'a mut serde_json::Map> { +) -> Option<&mut serde_json::Map> { let obj = body.as_object_mut(); if obj.is_none() { tracing::error!( diff --git a/gateway/src/core/observability/async_observer.rs b/gateway/src/core/observability/async_observer.rs index 8e2130d4..b6d8c1b1 100644 --- a/gateway/src/core/observability/async_observer.rs +++ b/gateway/src/core/observability/async_observer.rs @@ -34,7 +34,7 @@ pub const DEFAULT_ASYNC_OBSERVER_QUEUE: usize = 256; /// One observable event, owned (queueable). enum ObserverEvent { - SttResult(STTResult, u64), + SttResult(Box, u64), TtsChunk(AudioData, Option), TtsComplete(u64), ConnectionStateChange { @@ -150,7 +150,10 @@ fn dispatch(observer: &dyn VoiceObserver, event: ObserverEvent) { impl VoiceObserver for AsyncObserver { fn on_stt_result(&self, result: &STTResult, latency_ns: u64) { - self.enqueue(ObserverEvent::SttResult(result.clone(), latency_ns)); + self.enqueue(ObserverEvent::SttResult( + Box::new(result.clone()), + latency_ns, + )); } fn on_tts_chunk(&self, chunk: &AudioData, ttfb_ns: Option) { self.enqueue(ObserverEvent::TtsChunk(chunk.clone(), ttfb_ns)); diff --git a/gateway/src/core/stt/batch.rs b/gateway/src/core/stt/batch.rs index 440d5b1e..17cf25e4 100644 --- a/gateway/src/core/stt/batch.rs +++ b/gateway/src/core/stt/batch.rs @@ -40,7 +40,7 @@ const BATCH_CALLBACK_URL_SCHEMES: &[&str] = &["http", "https"]; pub const MAX_BATCH_INLINE_AUDIO_BYTES: usize = 25 * 1024 * 1024; /// JSON body budget for base64 inline audio plus envelope overhead. pub const BATCH_JSON_BODY_LIMIT_BYTES: usize = - ((MAX_BATCH_INLINE_AUDIO_BYTES + 2) / 3) * 4 + (1024 * 1024); + MAX_BATCH_INLINE_AUDIO_BYTES.div_ceil(3) * 4 + (1024 * 1024); // ============================================================================= // Envelope @@ -911,7 +911,7 @@ pub(crate) fn decode_inline_batch_audio_with_limit( .take_while(|&&b| b == b'=') .count() .min(2); - let decoded_upper_bound = ((payload.len() + 3) / 4) * 3 - padding; + let decoded_upper_bound = payload.len().div_ceil(4) * 3 - padding; if decoded_upper_bound > max_decoded_bytes { return Err(format!( "inline batch audio exceeds decoded size limit of {max_decoded_bytes} bytes" diff --git a/gateway/src/core/stt/groq/client.rs b/gateway/src/core/stt/groq/client.rs index 82abf0de..71352f57 100644 --- a/gateway/src/core/stt/groq/client.rs +++ b/gateway/src/core/stt/groq/client.rs @@ -400,8 +400,8 @@ impl GroqSTT { let sample_count = audio_data.len() / 2; // Process PCM 16-bit little-endian samples - for chunk in audio_data.chunks_exact(2) { - let sample = i16::from_le_bytes([chunk[0], chunk[1]]) as f32 * PCM_TO_FLOAT_SCALE; + for chunk in audio_data.as_chunks::<2>().0 { + let sample = i16::from_le_bytes(*chunk) as f32 * PCM_TO_FLOAT_SCALE; sum_squares += sample * sample; } diff --git a/gateway/src/core/stt/openai/client.rs b/gateway/src/core/stt/openai/client.rs index e109aa85..f2e0f2cb 100644 --- a/gateway/src/core/stt/openai/client.rs +++ b/gateway/src/core/stt/openai/client.rs @@ -282,8 +282,8 @@ impl OpenAISTT { let sample_count = audio_data.len() / 2; // Process PCM 16-bit little-endian samples - for chunk in audio_data.chunks_exact(2) { - let sample = i16::from_le_bytes([chunk[0], chunk[1]]) as f32 * PCM_TO_FLOAT_SCALE; + for chunk in audio_data.as_chunks::<2>().0 { + let sample = i16::from_le_bytes(*chunk) as f32 * PCM_TO_FLOAT_SCALE; sum_squares += sample * sample; } diff --git a/gateway/src/core/stt/wav.rs b/gateway/src/core/stt/wav.rs index ced20e03..8fe8cc4f 100644 --- a/gateway/src/core/stt/wav.rs +++ b/gateway/src/core/stt/wav.rs @@ -48,7 +48,7 @@ pub(crate) fn create_pcm_wav_header( if bits_per_sample == 0 { return Err(WavBuildError::ZeroBitsPerSample); } - if bits_per_sample % 8 != 0 { + if !bits_per_sample.is_multiple_of(8) { return Err(WavBuildError::InvalidBitsPerSample(bits_per_sample)); } diff --git a/gateway/src/core/tts/alibaba_cloud/provider.rs b/gateway/src/core/tts/alibaba_cloud/provider.rs index 4e5d8ef3..b36e6598 100644 --- a/gateway/src/core/tts/alibaba_cloud/provider.rs +++ b/gateway/src/core/tts/alibaba_cloud/provider.rs @@ -63,8 +63,6 @@ use crate::core::tts::base::{ #[allow(dead_code)] const PROVIDER_INFO: &str = "Alibaba Cloud DashScope TTS (阿里云)"; -/// WebSocket connection timeout. - /// Channel buffer size for text messages. const TEXT_CHANNEL_BUFFER: usize = 32; diff --git a/gateway/src/core/tts/iflytek/provider.rs b/gateway/src/core/tts/iflytek/provider.rs index 364c61c1..f95fbe39 100644 --- a/gateway/src/core/tts/iflytek/provider.rs +++ b/gateway/src/core/tts/iflytek/provider.rs @@ -51,8 +51,6 @@ use crate::core::tts::base::{ /// Provider information string. const PROVIDER_INFO: &str = "iFlytek TTS WebSocket v2.0 (科大讯飞)"; -/// WebSocket connection timeout. - /// WebSocket message timeout. const WS_MESSAGE_TIMEOUT: Duration = Duration::from_secs(60); diff --git a/gateway/src/core/tts/provider.rs b/gateway/src/core/tts/provider.rs index 72adedec..0c047cb4 100644 --- a/gateway/src/core/tts/provider.rs +++ b/gateway/src/core/tts/provider.rs @@ -259,6 +259,12 @@ pub struct TTSProvider { previous_text: Arc>>, } +impl Default for TTSProvider { + fn default() -> Self { + Self::new() + } +} + impl TTSProvider { /// Create a new HTTP-based TTS provider instance pub fn new() -> Self { diff --git a/gateway/src/core/turn_detect/detector.rs b/gateway/src/core/turn_detect/detector.rs index d6ced1cc..8f0f8041 100644 --- a/gateway/src/core/turn_detect/detector.rs +++ b/gateway/src/core/turn_detect/detector.rs @@ -271,8 +271,8 @@ mod tests { #[test] fn test_normalization_regexes_compile_without_unwrap() { - assert!(matches!(&*PUNCT_REGEX, Ok(_))); - assert!(matches!(&*WHITESPACE_REGEX, Ok(_))); + assert!(PUNCT_REGEX.is_ok()); + assert!(WHITESPACE_REGEX.is_ok()); } #[test] diff --git a/gateway/src/handlers/openai_audio.rs b/gateway/src/handlers/openai_audio.rs index 71938604..c9d43d76 100644 --- a/gateway/src/handlers/openai_audio.rs +++ b/gateway/src/handlers/openai_audio.rs @@ -1418,8 +1418,10 @@ async fn apply_noise_suppression( // An odd length would mean a truncated final sample, so the chunk size is // asserted by construction rather than assumed. let samples: Vec = processed - .chunks_exact(2) - .map(|c| i16::from_le_bytes([c[0], c[1]])) + .as_chunks::<2>() + .0 + .iter() + .map(|c| i16::from_le_bytes(*c)) .collect(); turn_span.record(NOISE_SUPPRESSION_ATTR, true); info!( diff --git a/gateway/src/handlers/voices.rs b/gateway/src/handlers/voices.rs index 044f4e48..d45140a8 100644 --- a/gateway/src/handlers/voices.rs +++ b/gateway/src/handlers/voices.rs @@ -15,7 +15,7 @@ const MAX_VOICE_CLONE_SAMPLE_BYTES: usize = 25 * 1024 * 1024; const MAX_VOICE_CLONE_TOTAL_AUDIO_BYTES: usize = 250 * 1024 * 1024; /// JSON body budget for base64 clone samples plus request metadata. pub const VOICE_CLONE_JSON_BODY_LIMIT_BYTES: usize = - ((MAX_VOICE_CLONE_TOTAL_AUDIO_BYTES + 2) / 3) * 4 + (1024 * 1024); + MAX_VOICE_CLONE_TOTAL_AUDIO_BYTES.div_ceil(3) * 4 + (1024 * 1024); fn voice_handler_http_client() -> Result { crate::core::net::ssrf_protected_client_builder(crate::core::net::HTTP_URL_SCHEMES).build() diff --git a/gateway/src/lib.rs b/gateway/src/lib.rs index 63e7eeb4..ee5ba53c 100644 --- a/gateway/src/lib.rs +++ b/gateway/src/lib.rs @@ -1,3 +1,16 @@ +// Tests that mutate process env vars serialise on `core::net::test_env_lock`, a process-wide +// std Mutex, and hold it across the whole test body, awaits included: releasing it at an await +// would let another test observe a half-set env. Each #[tokio::test] runs its own runtime on its +// own thread, so a blocked lock() stalls only that thread and cannot deadlock the task holding +// the guard, which is the hazard this lint guards against. Test builds only. +#![cfg_attr( + test, + allow( + clippy::await_holding_lock, + reason = "test env lock is deliberately held across awaits; see comment" + ) +)] + pub mod auth; pub mod config; pub mod core; diff --git a/gateway/src/livekit/client/audio.rs b/gateway/src/livekit/client/audio.rs index 88ad4e43..add21f78 100644 --- a/gateway/src/livekit/client/audio.rs +++ b/gateway/src/livekit/client/audio.rs @@ -400,8 +400,10 @@ impl LiveKitClient { } else { // Unaligned buffer: fall back to the endian-explicit decode. audio_data - .chunks_exact(2) - .map(|chunk| i16::from_le_bytes([chunk[0], chunk[1]])) + .as_chunks::<2>() + .0 + .iter() + .map(|chunk| i16::from_le_bytes(*chunk)) .collect() } } diff --git a/gateway/src/utils/simd_ops.rs b/gateway/src/utils/simd_ops.rs index ccf4e192..530fb09c 100644 --- a/gateway/src/utils/simd_ops.rs +++ b/gateway/src/utils/simd_ops.rs @@ -504,8 +504,8 @@ pub fn pcm_to_float_simd(pcm: &[u8]) -> Vec { // First convert bytes to i16 then to f32 (scalar - SIMD int conversion is complex) let mut samples: Vec = Vec::with_capacity(sample_count); - for chunk in pcm.chunks_exact(2) { - let sample = i16::from_le_bytes([chunk[0], chunk[1]]) as f32; + for chunk in pcm.as_chunks::<2>().0 { + let sample = i16::from_le_bytes(*chunk) as f32; samples.push(sample); } @@ -1076,8 +1076,8 @@ pub fn copy_to_tensor_simd(src: &[f32], dst: &mut [f32]) -> Result<(), TensorCop #[allow(dead_code)] pub fn pcm_to_float_scalar(pcm: &[u8]) -> Vec { let mut output = Vec::with_capacity(pcm.len() / 2); - for chunk in pcm.chunks_exact(2) { - let sample = i16::from_le_bytes([chunk[0], chunk[1]]) as f32; + for chunk in pcm.as_chunks::<2>().0 { + let sample = i16::from_le_bytes(*chunk) as f32; output.push((sample * PCM_TO_FLOAT_SCALE).clamp(-1.0, 1.0)); } output diff --git a/gateway/tests/dag_realtime_frontend_audio.rs b/gateway/tests/dag_realtime_frontend_audio.rs index 81e76ad0..e75b9d69 100644 --- a/gateway/tests/dag_realtime_frontend_audio.rs +++ b/gateway/tests/dag_realtime_frontend_audio.rs @@ -659,8 +659,10 @@ async fn real_audio_drives_frontend_into_realtime_dag_node() { // Count how many 16-bit samples the model changed — full DeepFilterNet processing // rewrites essentially the whole buffer; a stub / pass-through changes nothing. let changed_samples = raw_utterance - .chunks_exact(2) - .zip(denoised_utterance.chunks_exact(2)) + .as_chunks::<2>() + .0 + .iter() + .zip(denoised_utterance.as_chunks::<2>().0) .filter(|(a, b)| a != b) .count(); let total_samples = raw_utterance.len() / 2; diff --git a/gateway/tests/fixtures/audio_fixtures.rs b/gateway/tests/fixtures/audio_fixtures.rs index ba3942b4..365c0b9f 100644 --- a/gateway/tests/fixtures/audio_fixtures.rs +++ b/gateway/tests/fixtures/audio_fixtures.rs @@ -232,8 +232,10 @@ pub fn samples_to_bytes(samples: &[i16]) -> Vec { /// Convert bytes to i16 samples pub fn bytes_to_samples(bytes: &[u8]) -> Vec { bytes - .chunks_exact(2) - .map(|chunk| i16::from_le_bytes([chunk[0], chunk[1]])) + .as_chunks::<2>() + .0 + .iter() + .map(|chunk| i16::from_le_bytes(*chunk)) .collect() } diff --git a/gateway/tests/livekit_audio_latency_tests.rs b/gateway/tests/livekit_audio_latency_tests.rs index 2e6e2141..691d931a 100644 --- a/gateway/tests/livekit_audio_latency_tests.rs +++ b/gateway/tests/livekit_audio_latency_tests.rs @@ -250,8 +250,10 @@ async fn test_audio_conversion_latency() { } } else { audio_data - .chunks_exact(2) - .map(|c| i16::from_le_bytes([c[0], c[1]])) + .as_chunks::<2>() + .0 + .iter() + .map(|c| i16::from_le_bytes(*c)) .collect() }; diff --git a/gateway/tests/llm_adapter_live_e2e.rs b/gateway/tests/llm_adapter_live_e2e.rs index 6691e7c3..3b11942a 100644 --- a/gateway/tests/llm_adapter_live_e2e.rs +++ b/gateway/tests/llm_adapter_live_e2e.rs @@ -11,6 +11,14 @@ //! second turn proving history threading — the full B-G1 live gate from //! PIPECAT_FIX_PLAN §2. +// `ollama_serial_lock` is held across each test's awaits on purpose: each +// #[tokio::test] owns its runtime and thread, so a blocked lock() stalls only that +// thread, never the task holding the guard. +#![allow( + clippy::await_holding_lock, + reason = "test serialisation lock deliberately held across awaits; see comment" +)] + use std::sync::{Arc, Mutex}; use tokio_util::sync::CancellationToken; @@ -42,11 +50,6 @@ fn openai_key() -> Option { } } -/// LIVE (OpenAI, key-gated): a REASONING model (gpt-5-mini) must round-trip -/// through WaaV's OpenAI adapter — this is the regression for the -/// `max_tokens`→`max_completion_tokens` shape (reasoning models 400 on -/// `max_tokens`; live-caught). Validates the fast tier + the two-tier shape too. - /// Serializes the live-ollama tests: two reasoning-LLM generations racing each other (and the /// build load on this box) starve the shared local ollama past the adapter timeout — a pure /// resource-contention flake, not a product bug. One at a time is deterministic. Poison-tolerant. @@ -56,6 +59,10 @@ fn ollama_serial_lock() -> std::sync::MutexGuard<'static, ()> { .unwrap_or_else(std::sync::PoisonError::into_inner) } +/// LIVE (OpenAI, key-gated): a REASONING model (gpt-5-mini) must round-trip +/// through WaaV's OpenAI adapter — this is the regression for the +/// `max_tokens`→`max_completion_tokens` shape (reasoning models 400 on +/// `max_tokens`; live-caught). Validates the fast tier + the two-tier shape too. #[tokio::test] async fn openai_reasoning_model_live_round_trip() { let _serial = ollama_serial_lock(); diff --git a/gateway/tests/openai_stt_integration.rs b/gateway/tests/openai_stt_integration.rs index 2f9dd4ee..0c327fb0 100644 --- a/gateway/tests/openai_stt_integration.rs +++ b/gateway/tests/openai_stt_integration.rs @@ -9,6 +9,14 @@ //! Note: Tests requiring actual API calls are marked with #[ignore] //! and require OPENAI_API_KEY environment variable. +// `openai_base_url_env_lock` serialises every test that sets OPENAI_BASE_URL and is held across +// the test's awaits on purpose: each #[tokio::test] owns its runtime and thread, so a blocked +// lock() stalls only that thread. The same reasoning as the lib's test env lock. +#![allow( + clippy::await_holding_lock, + reason = "env lock deliberately held across awaits; see comment" +)] + use std::future::Future; use std::panic::AssertUnwindSafe; use std::sync::Arc; diff --git a/gateway/tests/openai_tts_integration.rs b/gateway/tests/openai_tts_integration.rs index 9c546b15..216f7aab 100644 --- a/gateway/tests/openai_tts_integration.rs +++ b/gateway/tests/openai_tts_integration.rs @@ -9,6 +9,14 @@ //! Note: Tests requiring actual API calls are marked with #[ignore] //! and require OPENAI_API_KEY environment variable. +// `openai_base_url_env_lock` is held across each test's awaits on purpose: each +// #[tokio::test] owns its runtime and thread, so a blocked lock() stalls only that +// thread, never the task holding the guard. +#![allow( + clippy::await_holding_lock, + reason = "test serialisation lock deliberately held across awaits; see comment" +)] + use std::future::Future; use std::panic::AssertUnwindSafe; use std::sync::Arc; diff --git a/gateway/tests/smart_turn_accuracy_test.rs b/gateway/tests/smart_turn_accuracy_test.rs index 839511fd..fd818c16 100644 --- a/gateway/tests/smart_turn_accuracy_test.rs +++ b/gateway/tests/smart_turn_accuracy_test.rs @@ -110,9 +110,11 @@ fn read_wav_16k_mono(path: &Path) -> Result> { // Convert to f32 samples let samples: Vec = if bits_per_sample == 16 { audio_data - .chunks_exact(2) + .as_chunks::<2>() + .0 + .iter() .map(|chunk| { - let sample = i16::from_le_bytes([chunk[0], chunk[1]]); + let sample = i16::from_le_bytes(*chunk); sample as f32 / 32768.0 }) .collect() @@ -128,8 +130,10 @@ fn read_wav_16k_mono(path: &Path) -> Result> { // Convert to mono if stereo let mono_samples: Vec = if num_channels == 2 { samples - .chunks_exact(2) - .map(|c| (c[0] + c[1]) / 2.0) + .as_chunks::<2>() + .0 + .iter() + .map(|[l, r]| (l + r) / 2.0) .collect() } else { samples diff --git a/gateway/tests/turn_detect_latency.rs b/gateway/tests/turn_detect_latency.rs index 356a9b90..899ab819 100644 --- a/gateway/tests/turn_detect_latency.rs +++ b/gateway/tests/turn_detect_latency.rs @@ -102,8 +102,7 @@ async fn silero_vad_inference_latency() -> Result<()> { // Silero processes 512-sample (32 ms) frames at 16 kHz. let audio = synth(SR); // 1 s → ~31 frames let mut per_frame = Vec::new(); - let mut i = 0usize; - for frame in audio.chunks(512) { + for (i, frame) in audio.chunks(512).enumerate() { if frame.len() < 512 { break; } @@ -113,7 +112,6 @@ async fn silero_vad_inference_latency() -> Result<()> { if i >= 5 { per_frame.push(us); } - i += 1; } let n = per_frame.len(); println!("\n=== SILERO-VAD per-frame (512-sample / 32ms) inference latency (n={n}) ==="); diff --git a/gateway/tests/unsafe_safety_tests.rs b/gateway/tests/unsafe_safety_tests.rs index 65ad49a4..73ae49ac 100644 --- a/gateway/tests/unsafe_safety_tests.rs +++ b/gateway/tests/unsafe_safety_tests.rs @@ -96,8 +96,10 @@ fn convert_bytes_to_i16_safe(audio_data: &[u8]) -> Vec { // Unaligned, or big-endian host: endian-explicit decode. audio_data - .chunks_exact(2) - .map(|chunk| i16::from_le_bytes([chunk[0], chunk[1]])) + .as_chunks::<2>() + .0 + .iter() + .map(|chunk| i16::from_le_bytes(*chunk)) .collect() } @@ -105,8 +107,10 @@ fn convert_bytes_to_i16_safe(audio_data: &[u8]) -> Vec { /// Used as the oracle the production fast path must match bit-for-bit. fn convert_bytes_to_i16_le_reference(audio_data: &[u8]) -> Vec { audio_data - .chunks_exact(2) - .map(|chunk| i16::from_le_bytes([chunk[0], chunk[1]])) + .as_chunks::<2>() + .0 + .iter() + .map(|chunk| i16::from_le_bytes(*chunk)) .collect() } From 082b7727a6a6b0910dcf6ecc7fc83a40d2119cec Mon Sep 17 00:00:00 2001 From: dittops Date: Sun, 27 Sep 2026 23:37:11 +0530 Subject: [PATCH 6/8] fix(deps): clear the supply-chain gate's advisories deny.toml used `wildcard-dependencies`, which cargo-deny no longer accepts (`wildcards`), so the config failed to load before any check. Fixed by upgrading: anyhow 1.0.104 (unsound), crossbeam-epoch 0.9.21, h2 0.4.19, rtrb 0.3.5, rustls 0.23.45, quinn-proto 0.11.18, cxx 1.0.202, event-listener 5.4.2, memmap2 0.9.11, and the yanked chacha20, der and spin. object_store 0.12 -> 0.14.2 for quick-xml 0.41 (get() moved to ObjectStoreExt). The AWS SDK crates drop their default `rustls` feature: it only adds the legacy hyper-0.14 connector (rustls 0.21, h2 0.3, rustls-webpki 0.101), and with BehaviorVersion::latest(), which every call site passes, the SDK already uses default-https-client. Recorded as ignores with reasons (deny.toml; mirrored in gateway/.cargo/audit.toml for cargo audit): tonic 0.11's h2 0.3 and rustls-webpki 0.102, pinned by google-api-proto for the Google Speech client; tract-nnef's NNEF parser, which WaaV never feeds (the fixed tract caps time < 0.3.42 and would bring back RUSTSEC-2026-0009); five unmaintained crates with no upgrade. CDLA-Permissive-2.0 (the webpki-roots CA bundle) is an allowed licence. Co-Authored-By: Claude Opus 5.5 --- deny.toml | 20 +- gateway/.cargo/audit.toml | 17 ++ gateway/Cargo.lock | 351 ++++++++++++++++-------------- gateway/Cargo.toml | 12 +- gateway/src/handlers/recording.rs | 2 +- 5 files changed, 229 insertions(+), 173 deletions(-) create mode 100644 gateway/.cargo/audit.toml diff --git a/deny.toml b/deny.toml index ef006b1e..804c0189 100644 --- a/deny.toml +++ b/deny.toml @@ -35,12 +35,29 @@ ignore = [ # perform RSA private-key decryption on attacker-controlled ciphertext on the hot path). # Remove once `rsa` ships a patched release. { id = "RUSTSEC-2023-0071", reason = "rsa Marvin timing sidechannel; no patched release; not on an exploitable path" }, + # tonic 0.11 is pinned by google-api-proto (Google Speech v2 gRPC). WaaV is the CLIENT on that + # channel and its only peer is Google's endpoint. Leaves with the move to googleapis-tonic-*. + { id = "RUSTSEC-2026-0258", reason = "h2 0.3 only via tonic 0.11 (google-api-proto); client to Google Speech only" }, + { id = "RUSTSEC-2026-0049", reason = "rustls-webpki 0.102 only via tonic 0.11; CRLs are not configured" }, + { id = "RUSTSEC-2026-0098", reason = "rustls-webpki 0.102 only via tonic 0.11; validates Google's public chain" }, + { id = "RUSTSEC-2026-0099", reason = "rustls-webpki 0.102 only via tonic 0.11; validates Google's public chain" }, + { id = "RUSTSEC-2026-0104", reason = "rustls-webpki 0.102 only via tonic 0.11; CRLs are not configured" }, + # tract_nnef::tensors::read_tensor reads NNEF archives. WaaV never loads one: noise-filter runs the + # DeepFilterNet ONNX model compiled into the binary. The fixed 0.21.16+ caps `time` < 0.3.42, + # which would reintroduce RUSTSEC-2026-0009 (fixed in time 0.3.47). + { id = "RUSTSEC-2026-0217", reason = "tract NNEF tensor parser; WaaV loads only the compiled-in DeepFilterNet ONNX model" }, + # Unmaintained, no safe upgrade exists. Not vulnerabilities. + { id = "RUSTSEC-2026-0150", reason = "unmaintained audiopus_sys (opus-codec feature); no maintained replacement yet" }, + { id = "RUSTSEC-2024-0014", reason = "unmaintained generational-arena via abi_stable (plugin ABI); no upgrade" }, + { id = "RUSTSEC-2024-0436", reason = "unmaintained paste (proc-macro) via abi_stable/tokenizers; no upgrade" }, + { id = "RUSTSEC-2025-0134", reason = "unmaintained rustls-pemfile via axum-server 0.7 and tonic 0.11" }, + { id = "RUSTSEC-2026-0249", reason = "unmaintained smartstring via rhai (dag-routing); no upgrade" }, ] # --------------------------------------------------------------------------------------- [bans] multiple-versions = "warn" # duplicate-version churn is noise, not a merge blocker -wildcard-dependencies = "deny" # forbid `*` version requirements (reproducibility, W11) +wildcards = "deny" # forbid `*` version requirements (reproducibility, W11) allow-wildcard-paths = true # ...except path deps (waav-plugin-api), which are intra-repo # --------------------------------------------------------------------------------------- @@ -62,6 +79,7 @@ allow = [ "0BSD", "BSL-1.0", "OpenSSL", + "CDLA-Permissive-2.0", # webpki-roots / webpki-root-certs: the Mozilla CA bundle as data ] confidence-threshold = 0.9 diff --git a/gateway/.cargo/audit.toml b/gateway/.cargo/audit.toml new file mode 100644 index 00000000..f675622f --- /dev/null +++ b/gateway/.cargo/audit.toml @@ -0,0 +1,17 @@ +# cargo audit's ignore list. It must match [advisories].ignore in ../../deny.toml, where +# every entry carries its justification. +[advisories] +ignore = [ + "RUSTSEC-2023-0071", + "RUSTSEC-2026-0258", + "RUSTSEC-2026-0049", + "RUSTSEC-2026-0098", + "RUSTSEC-2026-0099", + "RUSTSEC-2026-0104", + "RUSTSEC-2026-0217", + "RUSTSEC-2026-0150", + "RUSTSEC-2024-0014", + "RUSTSEC-2024-0436", + "RUSTSEC-2025-0134", + "RUSTSEC-2026-0249", +] diff --git a/gateway/Cargo.lock b/gateway/Cargo.lock index 918e3e5c..7734c65b 100644 --- a/gateway/Cargo.lock +++ b/gateway/Cargo.lock @@ -148,7 +148,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -159,14 +159,14 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "anymap2" @@ -338,9 +338,9 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.17.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" +checksum = "b281d307588d634de920874890732659e2e7672f72b5e10e81badc1a8a83621e" dependencies = [ "aws-lc-sys", "untrusted 0.7.1", @@ -349,14 +349,15 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.41.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4" +checksum = "9bff6c3b54fad79a2e60b8102caf565819711497c1f5f092f49508e2f5c31b27" dependencies = [ "cc", "cmake", "dunce", "fs_extra", + "pkg-config", ] [[package]] @@ -619,19 +620,13 @@ dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", "aws-smithy-types", - "h2 0.3.27", - "h2 0.4.14", - "http 0.2.12", + "h2 0.4.19", "http 1.4.1", - "http-body 0.4.6", - "hyper 0.14.32", "hyper 1.10.1", - "hyper-rustls 0.24.2", - "hyper-rustls 0.27.9", + "hyper-rustls", "hyper-util", "pin-project-lite", - "rustls 0.21.12", - "rustls 0.23.40", + "rustls 0.23.45", "rustls-native-certs 0.8.3", "rustls-pki-types", "tokio", @@ -901,7 +896,7 @@ dependencies = [ "hyper 1.10.1", "hyper-util", "pin-project-lite", - "rustls 0.23.40", + "rustls 0.23.45", "rustls-pemfile", "rustls-pki-types", "tokio", @@ -942,6 +937,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + [[package]] name = "base64-simd" version = "0.8.0" @@ -1173,9 +1174,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chacha20" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" dependencies = [ "cfg-if", "cpufeatures 0.3.0", @@ -1323,7 +1324,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] @@ -1355,15 +1356,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "const-oid" version = "0.9.6" @@ -1470,6 +1462,16 @@ dependencies = [ "libc", ] +[[package]] +name = "crc-fast" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e75b2483e97a5a7da73ac68a05b629f9c53cff58d8ed1c77866079e18b00dba5" +dependencies = [ + "digest 0.10.7", + "spin 0.10.1", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -1538,9 +1540,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d" dependencies = [ "crossbeam-utils", ] @@ -1626,9 +1628,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.194" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747d8437319e3a2f43d93b341c137927ca70c0f5dabeea7a005a73665e247c7e" +checksum = "13f6de320895f42e6e081abb5c7983bedcf0b6d0ff9323de0d33f620c8ac1199" dependencies = [ "cc", "cxx-build", @@ -1641,9 +1643,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.194" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0f4697d190a142477b16aef7da8a99bfdc41e7e8b1687583c0d23a79c7afc1e" +checksum = "4fde53ca86b9704a943fef0f1e1d836239a6aedca5de3c65fa9f97ec0bd46d39" dependencies = [ "cc", "codespan-reporting", @@ -1651,39 +1653,39 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.117", + "syn 3.0.6", ] [[package]] name = "cxxbridge-cmd" -version = "1.0.194" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0956799fa8678d4c50eed028f2de1c0552ae183c76e976cf7ca8c4e36a7c328" +checksum = "07bae89236c811fd4d08ed3441759ac4ac98d752cbfd3de341315ba16ad20ec3" dependencies = [ "clap", "codespan-reporting", "indexmap 2.14.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.6", ] [[package]] name = "cxxbridge-flags" -version = "1.0.194" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23384a836ab4f0ad98ace7e3955ad2de39de42378ab487dc28d3990392cb283a" +checksum = "49045042e5fced01b80742aba5508de82aa4f13677ed3fa2b4cda709c40c5918" [[package]] name = "cxxbridge-macro" -version = "1.0.194" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6acc6b5822b9526adfb4fc377b67128fdd60aac757cc4a741a6278603f763cf" +checksum = "b181252e2e3d3b5d183afbdc033a3958b445eb3e1a0ae65fc3d4f5259f5da6fd" dependencies = [ "indexmap 2.14.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.6", ] [[package]] @@ -1842,9 +1844,9 @@ dependencies = [ [[package]] name = "der" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" +checksum = "a878c850e9e421b20262e9b41f9c860e4785fa07541c266b62ff9d1ef998a80a" dependencies = [ "pem-rfc7468 1.0.0", "zeroize", @@ -2128,7 +2130,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -2139,11 +2141,10 @@ checksum = "d817e038c30374a4bcb22f94d0a8a0e216958d4c3dcde369b1439fec4bdda6e6" [[package]] name = "event-listener" -version = "5.4.1" +version = "5.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" dependencies = [ - "concurrent-queue", "parking", "pin-project-lite", ] @@ -2462,11 +2463,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", "rand_core 0.10.1", "wasip2", "wasip3", + "wasm-bindgen", ] [[package]] @@ -2514,7 +2517,7 @@ dependencies = [ "jsonwebtoken 10.4.0", "reqwest 0.13.4", "rustc_version", - "rustls 0.23.40", + "rustls 0.23.45", "rustls-pki-types", "serde", "serde_json", @@ -2652,9 +2655,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.14" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" +checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16" dependencies = [ "atomic-waker", "bytes", @@ -2895,7 +2898,7 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2 0.4.14", + "h2 0.4.19", "http 1.4.1", "http-body 1.0.1", "httparse", @@ -2907,21 +2910,6 @@ dependencies = [ "want", ] -[[package]] -name = "hyper-rustls" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" -dependencies = [ - "futures-util", - "http 0.2.12", - "hyper 0.14.32", - "log", - "rustls 0.21.12", - "tokio", - "tokio-rustls 0.24.1", -] - [[package]] name = "hyper-rustls" version = "0.27.9" @@ -2931,7 +2919,7 @@ dependencies = [ "http 1.4.1", "hyper 1.10.1", "hyper-util", - "rustls 0.23.40", + "rustls 0.23.45", "rustls-native-certs 0.8.3", "tokio", "tokio-rustls 0.26.4", @@ -3200,7 +3188,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -3254,6 +3242,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" @@ -3419,7 +3416,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin 0.9.8", + "spin 0.9.9", ] [[package]] @@ -3744,12 +3741,12 @@ dependencies = [ [[package]] name = "md-5" -version = "0.10.6" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +checksum = "69b6441f590336821bb897fb28fc622898ccceb1d6cea3fde5ea86b090c4de98" dependencies = [ "cfg-if", - "digest 0.10.7", + "digest 0.11.3", ] [[package]] @@ -3773,9 +3770,9 @@ checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" [[package]] name = "memmap2" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" dependencies = [ "libc", ] @@ -3943,7 +3940,7 @@ dependencies = [ "httparse", "memchr", "mime", - "spin 0.9.8", + "spin 0.9.9", "version_check", ] @@ -4013,6 +4010,18 @@ dependencies = [ "zip 2.4.2", ] +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags 2.11.1", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "no-std-compat" version = "0.4.1" @@ -4050,7 +4059,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -4163,28 +4172,33 @@ dependencies = [ [[package]] name = "object_store" -version = "0.12.5" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbfbfff40aeccab00ec8a910b57ca8ecf4319b335c542f2edcd19dd25a1e2a00" +checksum = "f1796bc93603f78c5760a69f2d58badc9618d22adade0a95385bb2adbae4eb94" dependencies = [ "async-trait", - "base64 0.22.1", + "aws-lc-rs", + "base64 0.23.1", "bytes", "chrono", + "crc-fast", "form_urlencoded", - "futures", + "futures-channel", + "futures-core", + "futures-util", "http 1.4.1", "http-body-util", "humantime", "hyper 1.10.1", - "itertools 0.14.0", + "itertools 0.15.0", "md-5", + "nix", "parking_lot", "percent-encoding", "quick-xml", - "rand 0.9.4", - "reqwest 0.12.28", - "ring", + "rand 0.10.1", + "reqwest 0.13.4", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", @@ -4195,6 +4209,7 @@ dependencies = [ "walkdir", "wasm-bindgen-futures", "web-time", + "windows-sys 0.61.2", ] [[package]] @@ -4923,7 +4938,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" dependencies = [ "bytes", - "heck 0.5.0", + "heck 0.4.1", "itertools 0.12.1", "log", "multimap", @@ -5036,9 +5051,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.38.4" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" dependencies = [ "memchr", "serde", @@ -5056,7 +5071,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.23.40", + "rustls 0.23.45", "socket2 0.6.4", "thiserror 2.0.18", "tokio", @@ -5066,18 +5081,19 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.14" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +checksum = "a9746dbde176634f4f2f1faf2404e30a31b2bc1e9cafb5329c95d8177a18c9fc" dependencies = [ "aws-lc-rs", "bytes", - "getrandom 0.3.4", + "getrandom 0.4.2", "lru-slab", - "rand 0.9.4", + "rand 0.10.1", + "rand_pcg", "ring", "rustc-hash", - "rustls 0.23.40", + "rustls 0.23.45", "rustls-pki-types", "slab", "thiserror 2.0.18", @@ -5207,6 +5223,15 @@ dependencies = [ "rand 0.8.6", ] +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", +] + [[package]] name = "rand_xoshiro" version = "0.7.0" @@ -5405,12 +5430,12 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.4.14", + "h2 0.4.19", "http 1.4.1", "http-body 1.0.1", "http-body-util", "hyper 1.10.1", - "hyper-rustls 0.27.9", + "hyper-rustls", "hyper-util", "js-sys", "log", @@ -5418,7 +5443,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.40", + "rustls 0.23.45", "rustls-native-certs 0.8.3", "rustls-pki-types", "serde", @@ -5434,7 +5459,7 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-streams", + "wasm-streams 0.4.2", "web-sys", "webpki-roots 1.0.7", ] @@ -5448,18 +5473,20 @@ dependencies = [ "base64 0.22.1", "bytes", "futures-core", + "futures-util", + "h2 0.4.19", "http 1.4.1", "http-body 1.0.1", "http-body-util", "hyper 1.10.1", - "hyper-rustls 0.27.9", + "hyper-rustls", "hyper-util", "js-sys", "log", "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.40", + "rustls 0.23.45", "rustls-pki-types", "rustls-platform-verifier", "serde", @@ -5468,12 +5495,14 @@ dependencies = [ "sync_wrapper 1.0.2", "tokio", "tokio-rustls 0.26.4", + "tokio-util", "tower 0.5.3", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams 0.5.0", "web-sys", ] @@ -5553,9 +5582,9 @@ dependencies = [ [[package]] name = "rtrb" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ade083ccbb4bf536df69d1f6432cc23deb7acccff86b183f3923a6fd56a1153" +checksum = "fae8ee26b0371a29a77d2b2d6b3ae13aa81def6f9bf1b1b92a32d279a5e709b7" [[package]] name = "rubato" @@ -5630,19 +5659,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls" -version = "0.21.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" -dependencies = [ - "log", - "ring", - "rustls-webpki 0.101.7", - "sct", + "windows-sys 0.60.2", ] [[package]] @@ -5661,16 +5678,16 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.40" +version = "0.23.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" dependencies = [ "aws-lc-rs", "log", "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.103.13", + "rustls-webpki 0.103.15", "subtle", "zeroize", ] @@ -5730,14 +5747,14 @@ dependencies = [ "jni 0.22.4", "log", "once_cell", - "rustls 0.23.40", + "rustls 0.23.45", "rustls-native-certs 0.8.3", "rustls-platform-verifier-android", - "rustls-webpki 0.103.13", + "rustls-webpki 0.103.15", "security-framework 3.7.0", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -5746,16 +5763,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring", - "untrusted 0.9.0", -] - [[package]] name = "rustls-webpki" version = "0.102.8" @@ -5769,9 +5776,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.13" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "aws-lc-rs", "ring", @@ -5874,16 +5881,6 @@ dependencies = [ "sha2 0.10.9", ] -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted 0.9.0", -] - [[package]] name = "sec1" version = "0.7.3" @@ -6246,7 +6243,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -6268,9 +6265,15 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spin" -version = "0.9.8" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" + +[[package]] +name = "spin" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "023a211cb3138dbc438680b32560ad89f699977624c9f8dbb95a47d5b4c07dd3" [[package]] name = "spinning_top" @@ -6503,6 +6506,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "sync_wrapper" version = "0.1.2" @@ -6556,7 +6570,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -6812,16 +6826,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls 0.21.12", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.25.0" @@ -6839,7 +6843,7 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ - "rustls 0.23.40", + "rustls 0.23.45", "tokio", ] @@ -6885,7 +6889,7 @@ checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" dependencies = [ "futures-util", "log", - "rustls 0.23.40", + "rustls 0.23.45", "rustls-pki-types", "tokio", "tokio-rustls 0.26.4", @@ -6986,7 +6990,7 @@ dependencies = [ "axum 0.8.9", "base64 0.22.1", "bytes", - "h2 0.4.14", + "h2 0.4.19", "http 1.4.1", "http-body 1.0.1", "http-body-util", @@ -7388,7 +7392,7 @@ dependencies = [ "httparse", "log", "rand 0.9.4", - "rustls 0.23.40", + "rustls 0.23.45", "rustls-pki-types", "sha1", "thiserror 2.0.18", @@ -7514,7 +7518,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" dependencies = [ "base64 0.22.1", - "der 0.8.0", + "der 0.8.2", "log", "native-tls", "percent-encoding", @@ -7714,7 +7718,7 @@ dependencies = [ "rhai", "rtrb", "rubato 0.16.2", - "rustls 0.23.40", + "rustls 0.23.45", "semver", "serde", "serde_json", @@ -7907,6 +7911,19 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasmparser" version = "0.244.0" @@ -8024,7 +8041,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] diff --git a/gateway/Cargo.toml b/gateway/Cargo.toml index 0cbcaf8c..79a9c730 100644 --- a/gateway/Cargo.toml +++ b/gateway/Cargo.toml @@ -212,7 +212,8 @@ regex = "1.10" once_cell = "1.19" uuid = { version = "1.10", features = ["v4"] } -object_store = { version = "0.12", features = ["aws"] } +# 0.14.1+: earlier releases pull quick-xml < 0.41 (RUSTSEC-2026-0194, RUSTSEC-2026-0195). +object_store = { version = "0.14.2", features = ["aws"] } # OpenAPI documentation (feature-gated) utoipa = { version = "5.3", optional = true, features = ["axum_extras"] } @@ -276,11 +277,14 @@ async-stream = "0.3" # AWS SDKs for Amazon Transcribe and Polly aws-config = { version = "1.5", features = ["behavior-version-latest"] } -aws-sdk-transcribestreaming = "1.62" -aws-sdk-polly = "1.61" +# No `rustls` feature: it only adds the legacy hyper-0.14 connector (rustls 0.21, h2 0.3, +# rustls-webpki 0.101, all under RUSTSEC advisories). With BehaviorVersion::latest(), which +# every call site passes, the SDK uses `default-https-client` anyway. +aws-sdk-transcribestreaming = { version = "1.62", default-features = false, features = ["default-https-client", "rt-tokio"] } +aws-sdk-polly = { version = "1.61", default-features = false, features = ["http-1x", "default-https-client", "rt-tokio"] } # AWS Bedrock Runtime — drives Nova Sonic's `InvokeModelWithBidirectionalStream` # (HTTP/2 bidi event stream; the same SDK family as transcribestreaming above). -aws-sdk-bedrockruntime = "1.62" +aws-sdk-bedrockruntime = { version = "1.62", default-features = false, features = ["default-https-client", "rt-tokio"] } aws-credential-types = "1.2" aws-types = "1.3" aws-smithy-types = "1.3" diff --git a/gateway/src/handlers/recording.rs b/gateway/src/handlers/recording.rs index 6b3d1d53..fb4c99dd 100644 --- a/gateway/src/handlers/recording.rs +++ b/gateway/src/handlers/recording.rs @@ -4,7 +4,7 @@ use axum::{ http::{HeaderMap, HeaderValue, StatusCode, header}, response::{IntoResponse, Response}, }; -use object_store::{Error as ObjectStoreError, ObjectStore, path::Path as ObjectPath}; +use object_store::{Error as ObjectStoreError, ObjectStoreExt, path::Path as ObjectPath}; use serde_json::json; use std::sync::Arc; use tracing::{debug, error, info, warn}; From e1264d523671909d9287624c3f6edd19915da558 Mon Sep 17 00:00:00 2001 From: dittops Date: Sun, 27 Sep 2026 23:37:11 +0530 Subject: [PATCH 7/8] fix(ci): tune typos to the codebase instead of 1845 false positives The codebase writes British English, but locale was en-us. Domain terms, language codes, deliberate misspellings the tests feed to the code, and opaque ids are allow-listed in labelled groups. Agent-run transcripts under inferv2/REVIEW/*.json are excluded. The gate still catches recieve, adress and seperate. Co-Authored-By: Claude Opus 5.5 --- _typos.toml | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/_typos.toml b/_typos.toml index 744af0cc..ed137aac 100644 --- a/_typos.toml +++ b/_typos.toml @@ -13,11 +13,17 @@ extend-exclude = [ "**/node_modules/**", "**/target/**", "**/*.min.js", + "inferv2/REVIEW/*.json", # agent-run transcripts (truncated tool output), not prose ] [default] -# Don't try to correct inside base64/hex blobs and long identifiers. -locale = "en-us" +locale = "en" # the codebase writes British English; accept both +# Don't try to correct inside base64/hex blobs: PEM bodies and keys in tests, commit shas and +# agent/workflow ids in notes. +extend-ignore-re = [ + "[A-Za-z0-9+/]{40,}={0,2}", + "\\b[0-9a-f]*[0-9][0-9a-f]*[a-f][0-9a-f]*\\b", +] [default.extend-words] # Domain terms that look like typos but are intentional. @@ -28,6 +34,61 @@ mut = "mut" crate = "crate" WaaV = "WaaV" waav = "waav" +# Acronyms and domain abbreviations. +fpt = "fpt" # FPT.AI, a Vietnamese speech provider +ane = "ane" # Apple Neural Engine +cann = "cann" # Huawei CANN (Ascend NPU runtime) +dbe = "dbe" # GPU double-bit ECC error +rto = "rto" # retransmission timeout +nin = "nin" # network-in-network +iit = "iit" # IIT Madras (AI4Bharat) +ist = "ist" # iFlytek real-time (IST) mode +onn = "onn" # ONNX split by a line break in tables +ake = "ake" # redis notify-keyspace-events flags +arange = "arange" # numpy / torch +strat = "strat" # local variable for a strategy +thr = "thr" # threshold in formulas +mis = "mis" # mis- prefix (mis-routed, mis-detected) +formant = "formant" # acoustic resonance +lasr = "lasr" # Google lasr_ctc architecture +criticals = "criticals" +datas = "datas" +# Language codes (ISO 639), phonemes and non-English voice names. +ba = "ba" +fo = "fo" +tha = "tha" +fre = "fre" +iy = "iy" +nam = "nam" +giong = "giong" +tung = "tung" +# Correct spellings typos does not know. +unparseable = "unparseable" +uncatalogued = "uncatalogued" +empted = "empted" # pre-empted +ded = "ded" # ANDed +alls = "alls" # "fail-alls" +correc = "correc" # CORREC**T**ness +# Deliberate misspellings the tests feed to the code (a typo'd key or value must be rejected), +# and partial words a streaming test sends one delta at a time ("Hel" + "lo", ""). +stabilty = "stabilty" +minimial = "minimial" +provder = "provder" +helo = "helo" +tru = "tru" +abd = "abd" +hel = "hel" +thi = "thi" +# Short variable names, ids and UI labels. +pn = "pn" # promptName +ue = "ue" # inside an ElevenLabs voice id +whth = "whth" # a workflow id +rovider = "rovider" # the "[P]rovider" hotkey label +propert = "propert" # propert{y,ies} pluralised in an f-string +symbl = "symbl" # Symbl.ai, in prose +# Table cells cut off in the source notes. +ful = "ful" +transfor = "transfor" [default.extend-identifiers] # Provider / vendor / library identifiers that are not English words. @@ -54,3 +115,7 @@ realfft = "realfft" rubato = "rubato" silero = "silero" Silero = "Silero" +Speaches = "Speaches" # speaches-ai/speaches, an OpenAI-compatible speech server +symbl = "symbl" # Symbl.ai +signall = "signall" # signall.us +cristal = "cristal" # a gradient-string preset From 1ac7a01f359bfca6a646a4bd1925e7057aa562f1 Mon Sep 17 00:00:00 2001 From: dittops Date: Sun, 27 Sep 2026 23:58:40 +0530 Subject: [PATCH 8/8] fix(ci): run the merge gate from the workspace root It has no checkout, so the workflow-wide working-directory (gateway) does not exist and bash could not start. All ten required results were success. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ceb224b8..e6c31e74 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -368,6 +368,10 @@ jobs: name: required (merge gate) runs-on: ubuntu-latest if: always() + # No checkout here, so the workflow-wide `working-directory: gateway` does not exist. + defaults: + run: + working-directory: . needs: - fmt - clippy