# rsiprtp
[](https://crates.io/crates/rsiprtp)
[](https://docs.rs/rsiprtp)
[](https://github.com/0x4D44/rsiprtp/actions/workflows/ci.yml)
[](#license)
[](#installation)
> 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](#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-mux` support (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 `IceSession` helper
that gathers candidates and runs connectivity checks alongside
`CallManager`. Host and server-reflexive candidates are supported;
TURN relay candidates (the `TurnClient` exists in `ice::turn` but is
not wired into `IceSession`), trickle ICE, ICE restart, IPv6
dual-stack interop, symmetric-NAT peer-reflexive (prflx) discovery,
and RFC 7675 consent-freshness keepalives are not yet implemented.
[`SipResolver`]: https://docs.rs/rsiprtp/latest/rsiprtp/transport/struct.SipResolver.html
**Architecture**
- Single crate organized into focused modules with a flat `prelude`
import surface
- Sans-IO core: deterministic, runtime-agnostic, and easy to test
## Installation
```sh
cargo add rsiprtp
cargo add tokio --features full
```
Or directly in `Cargo.toml`:
```toml
[dependencies]
rsiprtp = "0.4"
tokio = { version = "1", features = ["full"] }
```
MSRV: **Rust 1.88**.
## Examples
Worked end-to-end programs live in
[`crates/rsiprtp/examples/`](crates/rsiprtp/examples):
- [`basic_call.rs`](crates/rsiprtp/examples/basic_call.rs) — REGISTER + INVITE +
BYE against a live Asterisk server, including digest auth and RTP media.
- [`voicemail.rs`](crates/rsiprtp/examples/voicemail.rs) — answer an inbound
call and record the caller's audio to a WAV file.
- [`ai_bridge.rs`](crates/rsiprtp/examples/ai_bridge.rs) — bridge a SIP call
into an external audio pipeline (the same shape `gabby` uses).
- [`ice_call.rs`](crates/rsiprtp/examples/ice_call.rs) — two `CallManager`s
on loopback driving an `IceSession` through a full gather / offer /
answer / connectivity-check / probe flow.
- [`session_timers.rs`](crates/rsiprtp/examples/session_timers.rs) — the
PRACK / UPDATE / session-timer choreography (RFC 3262 / 3311 / 4028)
showing how `tick` / `next_deadline` / `drain_outbound_requests`
thread into a `tokio::select!` event loop.
Run one with environment configuration, for example:
```sh
SIP_SERVER=192.168.1.10 SIP_USER=1001 SIP_PASS=secret SIP_DEST='*43' \
cargo run --example basic_call
```
A minimal API sketch — the manager is constructed with a `ManagerConfig`
and driven from your transport, emitting `ManagerEvent`s you react to:
```rust,ignore
use rsiprtp::prelude::*;
let config = ManagerConfig {
local_sip_addr: "0.0.0.0:5060".to_string(), // IP:port
local_rtp_addr: "0.0.0.0".to_string(), // IP only
rtp_port_range: (10_000, 20_000),
call_config: CallConfig::default(),
};
let mut manager = CallManager::new(config);
let call_id = manager.create_call("sip:bob@example.com".to_string());
// `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.
```text
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](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](CHANGELOG.md) for release notes.
## Companion: gabby
[`gabby`](crates/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`](https://crates.io/crates/rsip) (dev-dependency, used as the
differential-test oracle for the in-tree SIP parser),
[`tokio`](https://crates.io/crates/tokio) and
[`rustls`](https://crates.io/crates/rustls) for async transport and TLS,
[`hickory-resolver`](https://crates.io/crates/hickory-resolver) for DNS,
[`ropus`](https://crates.io/crates/ropus),
[`ezk-g722`](https://crates.io/crates/ezk-g722), and
[`audio-codec-algorithms`](https://crates.io/crates/audio-codec-algorithms)
for codecs.
## Testing
Run the standard cargo bar:
```sh
cargo test --workspace --exclude gabby -- --test-threads=1
```
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:
```sh
cargo run --release -p full_test # everything
cargo run --release -p full_test -- --skip-coverage # ~3 min faster
cargo run --release -p full_test -- --help # all flags
```
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](CONTRIBUTING.md) for the
maintenance posture and PR conventions, and [SECURITY.md](SECURITY.md)
for vulnerability reports.
## License
Licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
<http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT](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.