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
24 changes: 24 additions & 0 deletions .github/actions/native-deps/action.yml
Original file line number Diff line number Diff line change
@@ -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 <CUDA_HOME>/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"
16 changes: 16 additions & 0 deletions .github/actions/onnxruntime/action.yml
Original file line number Diff line number Diff line change
@@ -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"
86 changes: 45 additions & 41 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -84,9 +83,11 @@ 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
# 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
Expand Down Expand Up @@ -184,15 +185,10 @@ 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)
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: |
Expand Down Expand Up @@ -231,8 +227,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
Expand All @@ -258,18 +253,13 @@ 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:
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
Expand All @@ -286,8 +276,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
Expand All @@ -300,8 +289,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: |
Expand All @@ -321,21 +309,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)
Expand All @@ -354,22 +353,25 @@ 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

# ---------------------------------------------------------------------------------------
# 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:
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
Expand All @@ -380,11 +382,13 @@ jobs:
- accuracy-enforced
- integration-mock
- server-smoke
- cross-compile-musl
- no-openssl
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
Expand Down
69 changes: 67 additions & 2 deletions _typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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", "<thi" + "nk>").
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.
Expand All @@ -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
20 changes: 19 additions & 1 deletion deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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

# ---------------------------------------------------------------------------------------
Expand All @@ -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

Expand Down
17 changes: 17 additions & 0 deletions gateway/.cargo/audit.toml
Original file line number Diff line number Diff line change
@@ -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",
]
Loading
Loading