## Summary Related: [block/buzz-app#1424](https://github.com/block/buzz-app/pull/1424) (native federated sessions; attaches the assertion on Huddle audio once this lands) `buzz_audio_client::connection::connect` took the audio URL as `&str`, so a host had no way to add headers to the WebSocket upgrade. buzz-app needs to send its NIP-FI user assertion (`Nostr-Federated-Identity: Bearer <jwt>`) on the Huddle audio socket. The relay checks NIP-FI when the audio connection upgrades (`audio/handler.rs` → `check_nip_fi_at_upgrade`). This PR widens the parameter to `impl IntoClientRequest + Unpin`, the same bound `connect_async_tls_with_config` already takes. Nothing else changes: TLS, timeouts and the NIP-42 handshake work as before. Existing `&str`/`&String` callers, including `desktop/src-tauri/src/huddle/relay_api.rs`, compile unchanged. --------- Signed-off-by: Will Pfleger <wpfleger@block.xyz> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
Shared Huddle audio client
buzz-audio-client contains the Huddle v2 audio handshake, wire format, Opus
sender, and per-peer NetEQ jitter buffer used by Buzz desktop clients. It does
not depend on Tauri, the v0 desktop application, a relay server, identity stores,
or speech models. A consumer can select this package from a pinned block/buzz
Git revision without building the rest of the workspace.
The host validates the destination and supplies an asynchronous NIP-42 signer
to connection::connect. The crate bounds connection and admission, validates
the initial roster, and returns a socket owned by the host. Dropping the pending
future or socket releases it; the crate creates no detached tasks. Hosts retain
room creation, membership checks, mute, device routing, connection supervision,
and application teardown. Audio admission alone does not grant chat access.
AudioEncoder accepts up to 960 finite mono samples at 48 kHz and produces a
20 ms v2 packet. PeerJitterBuffer consumes the sequence/timestamp/Opus fields
from wire::parse_relay_frame and produces 480 samples per 10 ms playout tick.
Hosts maintain one receiver per peer, discard its state on leave or roster
reassignment, bound playback queues, and stop idle peers after buffered audio
drains. V2 has no occupancy epoch in media, so a delayed packet from a previous
occupant cannot be distinguished after index reuse.
The default native-roots feature selects system TLS trust. Hosts that use
WebPKI roots select default-features = false, features = ["webpki-roots"];
hosts can also pass their configured TLS connector. Signing and trust policy
remain host-owned.
Opus 0.4 uses the maintained opusic-sys backend. NetEQ is pinned to 0.8.3:
its published source does not use its declared ringbuf dependency, which has
advisory RUSTSEC-2026-0293. The root security policy documents that unreachable
path and forbids other consumers of vulnerable ringbuf versions. Re-audit or
remove that exception when updating NetEQ; 0.9.1 still declares the old ringbuf.
Run cargo test -p buzz-audio-client and
cargo clippy -p buzz-audio-client --all-targets -- -D warnings from the root
with Hermit active. Tests use real loopback WebSockets and Opus audio, without a
live community or microphone. Client integration still requires a native
two-person call, mute, leave, cancellation, and device-switching tryout.
STT/TTS providers, model installation, and speech HTTP endpoints are intentionally outside this transport crate.