rsiprtp
An audio-focused SIP user-agent stack for Rust, built around Sans-IO state machines (pure logic that emits actions instead of doing network I/O) for transactions and dialogs, with batteries-included transports, media, and high-level call management for VoIP, telephony, and AI voice agents.
Targets traditional SIP / VoIP. Not a WebRTC stack — see Scope.
Features
Signaling
- SIP message parsing and building — in-tree implementation, no third-party SIP parser dependency. Differential-tested against rsip 0.4 (kept as a dev-dep oracle) on a corpus of mdsiprtp3 fixtures, hand-curated SIP shapes, and RFC 4475 torture tests.
- SIP message parsing and building (RFC 3261)
- Digest authentication, including SHA-256 (RFC 7616)
- Sans-IO transaction state machines — all four of RFC 3261 §17 (INVITE client / server, non-INVITE client / server) with the full timer set
- INVITE dialog management with route sets, early dialogs, and target refresh
- SDP offer/answer negotiation (RFC 3264) and SDP construction
- Call hold / resume via re-INVITE with direction changes (
sendrecv↔sendonly/inactive) - Blind and attended call transfer (REFER, RFC 3515; Replaces, RFC 3891)
- PRACK / 100rel reliable provisional responses (RFC 3262)
- UPDATE method (RFC 3311) for in-dialog refresh
- Session timers (RFC 4028) — both refresher (UPDATE / re-INVITE) and non-refresher (BYE on peer silence) paths
- Registration with digest challenge handling and periodic refresh
Media
- RTP send/receive with sequence and timestamp handling
- RTCP SR / RR / SDES / BYE / APP, with
rtcp-muxsupport (RFC 5761) - DTMF (RFC 4733) telephone-event
- G.711 (PCMU/PCMA), G.722, and Opus codecs (Opus inband FEC supported)
- Adaptive jitter buffer with reorder, duplicate, and late-arrival detection
- N-way mixer with active-speaker tracking for conference scenarios
Transport and security
- UDP, TCP, and TLS transports built on Tokio
- RFC 3263 SIP URI resolution (NAPTR / SRV / A) via
SipResolver - SRTP encryption with SDES key exchange (RFC 3711 + RFC 4568)
- ICE / STUN / TURN (RFC 8445 / 5389 / 5766), with an
IceSessionhelper that gathers candidates and runs connectivity checks alongsideCallManager. Host and server-reflexive candidates are supported; TURN relay candidates (theTurnClientexists inice::turnbut is not wired intoIceSession), trickle ICE, ICE restart, IPv6 dual-stack interop, symmetric-NAT peer-reflexive (prflx) discovery, and RFC 7675 consent-freshness keepalives are not yet implemented.
Architecture
- Single crate organized into focused modules with a flat
preludeimport surface - Sans-IO core: deterministic, runtime-agnostic, and easy to test
Installation
Or directly in Cargo.toml:
[]
= "0.4"
= { = "1", = ["full"] }
MSRV: Rust 1.88.
Examples
Worked end-to-end programs live in
crates/rsiprtp/examples/:
basic_call.rs— REGISTER + INVITE + BYE against a live Asterisk server, including digest auth and RTP media.voicemail.rs— answer an inbound call and record the caller's audio to a WAV file.ai_bridge.rs— bridge a SIP call into an external audio pipeline (the same shapegabbyuses).ice_call.rs— twoCallManagers on loopback driving anIceSessionthrough a full gather / offer / answer / connectivity-check / probe flow.session_timers.rs— the PRACK / UPDATE / session-timer choreography (RFC 3262 / 3311 / 4028) showing howtick/next_deadline/drain_outbound_requeststhread into atokio::select!event loop.
Run one with environment configuration, for example:
SIP_SERVER=192.168.1.10 SIP_USER=1001 SIP_PASS=secret SIP_DEST='*43' \
A minimal API sketch — the manager is constructed with a ManagerConfig
and driven from your transport, emitting ManagerEvents you react to:
use *;
let config = ManagerConfig ;
let mut manager = new;
let call_id = manager.create_call;
// `call_id` identifies the call in subsequent `ManagerEvent`s.
// Pump SIP messages into the manager and react to emitted events from your transport loop.
See the examples above for the surrounding transport, event loop, and SDP/RTP plumbing.
Architecture
rsiprtp is a single crate organized into modules layered from foundations
up through transport, media, transactions, dialogs, and finally a session
layer. The pieces most consumers want are re-exported flat via
rsiprtp::prelude::*, but every module is also reachable directly.
Session │ session, dialog (CallManager, RegistrationManager, INVITE dialogs)
Transaction │ transaction (RFC 3261 state machines, Sans-IO)
Signaling │ sip, sdp (message parsing & digest auth; offer/answer)
Media │ rtp, srtp, media (RTP/RTCP/DTMF, SRTP-SDES, codecs, jitter buffer)
Network │ transport, ice (UDP/TCP/TLS + DNS; ICE/STUN/TURN building blocks)
Foundation │ core (shared types, errors, configuration)
For the full module graph, the Sans-IO event/action loop, and a typical INVITE call flow, see ARCHITECTURE.md.
Scope
rsiprtp is a user-agent (UA) stack focused on placing and answering
audio calls over traditional SIP. The following are deliberately out of
scope or not yet implemented; if you need any of these, please open an
issue first:
- WebRTC interop. No DTLS-SRTP. SRTP is reachable via SDES key exchange (RFC 4568), which works against SIP carriers but not against browsers.
- Server roles: REGISTER server / location service, B2BUA, proxy, registrar.
- Event packages: SUBSCRIBE / NOTIFY / PUBLISH carry method-level message support only — no event-state machines, presence, BLF, or MWI.
- MESSAGE / SIMPLE / MSRP messaging.
- SIP over WebSocket (RFC 7118).
- Video codecs and FEC (RED / ULPFEC / RTX).
Status
rsiprtp is pre-1.0: the public API may change between minor releases
until 1.0. It is suitable for prototyping and serious internal use today —
pin an exact version before depending on it from production code.
See CHANGELOG.md for release notes.
Companion: gabby
gabby is a Voice AI agent built on top of rsiprtp. It
accepts SIP calls and converses using Vosk (speech-to-text), a local Ollama
LLM, and Piper (text-to-speech). It lives in the same workspace as a
demonstration of what rsiprtp can do, but it is not published to
crates.io because it depends on native libraries (libvosk). Treat it as
a worked example rather than part of the public API.
Acknowledgments
rsiprtp is built on excellent work in the Rust ecosystem, including:
rsip (dev-dependency, used as the
differential-test oracle for the in-tree SIP parser),
tokio and
rustls for async transport and TLS,
hickory-resolver for DNS,
ropus,
ezk-g722, and
audio-codec-algorithms
for codecs.
Testing
Run the standard cargo bar:
For a one-shot full check (fmt, clippy, cargo-deny, build, tests, doc,
coverage) with an HTML report under crates/rsiprtp/tests/results/,
use the full_test runner:
See wrk_docs/2026.05.02 - HLD - full_test runner - V2.md for design
notes.
Contributing
rsiprtp is a personal project — shared in the hope it's a useful base
to fork, vendor, or learn from. Issues and pull requests are welcome,
but I make no commitment to triage, review, or merge them; if you need
support, please fork. See CONTRIBUTING.md for the
maintenance posture and PR conventions, and SECURITY.md
for vulnerability reports.
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.