# Sipral: Session Initiation Protocol Rust Audio Layer, the full description > Sipral (Session Initiation Protocol Rust Audio Layer) is a SIP client stack in Rust: signalling, media, encryption and NAT traversal behind one C ABI. It replaces PJSIP without the GPL, and puts a phone line inside a voice agent without a media server in between. Website: https://sipral.org ## What it is Sipral (Session Initiation Protocol Rust Audio Layer) is a SIP client stack in Rust. It covers signalling, media, encryption and NAT traversal, and exposes all of it through one C ABI with generated bindings for Swift, Kotlin, .NET, Python and Dart, plus a React Native package over the Swift and Kotlin layers. It is written to replace PJSIP in softphones without GPL exposure, and to give a voice agent a phone line without a media server in between. Who it is for: teams building softphones, mobile calling apps, contact-centre clients and voice agents that need SIP in a closed-source product or an app store build. ## Pipeline and architecture 1. A sans-I/O protocol core (parser, transactions, dialogs, SDP, authentication) that opens no socket, starts no thread, reads no clock and draws no random number. The caller feeds received bytes and the current time in, and takes bytes to send, events and the next timeout out. 2. A user agent layer for registration, calls, hold, transfer, subscriptions and messaging, kept apart from media: the two exchange only a description of the negotiated session. 3. A media layer: RTP and RTCP, SRTP and DTLS-SRTP, a jitter buffer with concealment and drift correction, codecs, conferencing. 4. A NAT layer: STUN, TURN and ICE. 5. A facade that joins signalling and media, and a C ABI over it, from which every binding is generated. 6. Optional edges: audio device backends (CoreAudio, WASAPI, AAudio, PipeWire) for device mode, or a headless build that hands PCM to the application or to a separate agent process over a socket. ## Data it touches - SIP messages and SDP exchanged with the PBX, proxy or carrier the application configures. - Account credentials the application passes in, used for digest authentication; the diagnostic trace strips credentials and keys. - Audio: from the device in device mode, or as 16-bit PCM frames in the application's hands in application and headless modes. - Diagnostics the application asks for: a per-call decision record as JSON, pcapng captures with GDPR redaction, health counters. - There is no licence check inside the library: the commercial licence is a contract, not a runtime lock. ## Measured figures - 10 000 concurrent calls in one process: Audio both ways on every call, set up at about 500 a second. - 72 KB of memory per call: A softphone barely notices it; a server holds thousands of calls. - 61 billion fuzzing executions: Hostile input thrown at the parsers and protocol engines. Zero crashes. - 7 languages, one interface: Swift, Kotlin, Java, .NET, Python, Dart and React Native, over one C ABI. Security testing: 61 billion fuzzing executions against the parsers and protocol engines, zero crashes and zero hangs; every message of the RFC 4475 SIP torture suite asserted. Memory-safe Rust throughout; unsafe code is confined to the C boundary and the audio device layer. Ten thousand concurrent calls, held in one process: - Calls up: 5 000 at 5 000 calls, 10 000 at 10 000 - Setup rate: 500 a second at 5 000 calls, 499 a second at 10 000 - Setup, INVITE to 2xx: p50, p90: 2.6 ms, 6.3 ms at 5 000 calls, 7.5 ms, 95.6 ms at 10 000 - Processor: 5.32 cores at 5 000 calls, 8.48 cores at 10 000 - Resident memory, peak: 363 MB at 5 000 calls, 718 MB at 10 000 ## Features ### Signalling that survives real deployments RFC 3261 in a core that owns no socket and no clock, with the extensions that PBXs, proxies and carriers actually send. - RFC 3261: SIP itself: transactions, dialogs, forking, the §17 timers as unit tests - RFC 3263 · 2782: NAPTR, SRV and A/AAAA location, every target kept for failover on a timeout, a lost transport or a 503, the name asked again when its TTL runs out - RFC 3261 §18.1.1: a request over 1300 bytes is not put in a datagram: promotion to TCP, or a trimmed retry - RFC 3262 · 3311: reliable provisional responses with PRACK, and UPDATE - RFC 4028: session timers - RFC 7616 · 8760: digest authentication, with SHA-256 and SHA-512/256 - RFC 3515 · 3891 · 4488: REFER, Replaces, and REFER without an implicit subscription: blind and attended transfer - RFC 5626 · 3327: Outbound connection management and keep-alives, and the Path header - RFC 5627: GRUU, asked for on REGISTER and used as the Contact of what opens a dialog - RFC 3608: Service-Route, learned from the 200 OK to REGISTER and preloaded on requests to the registrar - RFC 8599: push notification bindings, for calls announced while the app sleeps - RFC 3323 · 3325: privacy and asserted identity, P-Asserted-Identity read behind a per-account trust gate - RFC 3326 · 6432: the Reason header and Q.850 causes, read and written - RFC 4475: the SIP torture test messages, asserted message by message ### More than a call An audio pipeline from the RTP packet to the earpiece, with the codecs deployments ask for and the services that ride on a call. - Opus: RFC 6716 and RFC 7587; a build without it exists for products that cannot carry the Opus patent pool exposure - G.722 · G.711 · L16: wideband at 64 kbit/s, A-law and mu-law, linear PCM - G.729 A and B: written from the ITU Recommendation, bit-exact against every Annex A and Annex B conformance stream; VAD, DTX and comfort noise - RFC 3550 · 3551: RTP and RTCP, with an adaptive jitter buffer, loss concealment and clock drift correction - RFC 4733 · 6086: DTMF three ways: RTP events, SIP INFO and in-band - Conference: N-way local conferences, each call on its own codec - RFC 7866 · 7865: SIPREC, as the recorded party and as the recording server - RFC 3428 · 3842 · 3856 · 4235: MESSAGE, message waiting, presence and the dialog package a busy lamp field is built on - RFC 4103: real-time text, T.140 beside the audio with redundancy - RFC 3611 · 6035: RTCP-XR VoIP metrics with an R factor and MOS, published to a collector at call end ### Encrypted the ways real PBXs and carriers ask for Primitives are taken from permissively licensed crates; the protocols around them are written in-tree and proven against the RFC test vectors. - RFC 3711 · 4568: SRTP keyed by SDES, AES_CM_128 and the 256-bit suites of RFC 6188 - RFC 7714: AES-GCM for SRTP and SRTCP, both key widths, proved against every test vector byte for byte - RFC 5764 · 5763 · 6347: DTLS-SRTP with DTLS 1.2 written in-tree, both roles, no renegotiation, extended master secret required - Best effort: SDES offered on RTP/AVP for PBXs that answer RTP/SAVP with 488 - TLS: with the platform trust store, a private CA, or one certificate pinned by its fingerprint - RFC 8224 · 8588 · 8225 · 8226: STIR/SHAKEN: every INVITE an account places signed, every caller verified before the phone rings ### NAT handled, not hoped for From a softphone behind a home router to an agent on a server with a public address. - RFC 8489 · 5389: STUN, with failover when the first server is dead - RFC 8656: TURN over UDP, TCP and TLS, taken over by ICE as the relayed candidate - RFC 8445: ICE in the full and lite roles, with restarts and role conflicts - RFC 7675 · 8863: consent freshness, and a checklist waited on rather than failed - RFC 5761 · 7983: RTP and RTCP on one port; DTLS, STUN and RTP told apart on one socket - Network change: a call that follows its own address when the network moves: re-registered and re-offered from the new one ### Device mode or headless, per stack The same stack is a softphone with no audio code, or a line inside a voice agent with no media server. - Device mode: the library opens the microphone and speaker itself: CoreAudio voice processing, WASAPI communications streams, AAudio, with the platform echo cancellation - Application mode: each call hands its audio to the application as 16-bit PCM, a frame at a time, and takes back what the far end hears; on every platform, in every language - Headless: PCM carried over a socket to a separate agent process; a headless build links no audio device at all - Desktop Linux: a PipeWire backend for device mode ### Diagnosable in the field When a call fails on a customer's carrier, the record of why travels with the bug report. - Decision record: every decision a call took, with its reason code, as JSON - pcapng: capture export with GDPR redaction - Counters: health counters: retransmissions, refusals at a limit and more - Trace: a diagnostic trace with credentials and keys stripped - Replay: a recorded field session replays deterministically, and becomes a regression test ## Interoperability Every flow runs through the native API and again through the C interface, on clean and impaired links. Passed flows per peer: - Asterisk 22.10.1 (the configuration most integrators run): Register, Call, Hold, resume, Blind transfer, Attended transfer, DTMF, RFC 4733, DTMF, SIP INFO, SRTP, SDES, DTLS-SRTP, Compact form, G.729, MESSAGE, MWI - FreeSWITCH via Kamailio 1.10.12 / 6.1.4 (registration and routing through a proxy): Register, Call, Hold, resume, Blind transfer, Attended transfer, SRTP, SDES, DTLS-SRTP, Compact form - FreeSWITCH via OpenSIPS 1.10.12 / 4.0.2 (a second, independent proxy): Register, Call, Hold, resume, Blind transfer, Attended transfer, DTLS-SRTP, Compact form - baresip via Kamailio 4.11.0 / 6.1.4 (phone to phone, end to end): Call, Hold, resume, SRTP, SDES, DTLS-SRTP - FreePBX, live 16 (Asterisk 18.9-cert13) (a live system, on its own extensions): Register, Call, Hold, resume, Blind transfer, Attended transfer, DTMF, RFC 4733, SRTP, SDES - Asterisk 22.10.1, from behind a NAT: calling and called through STUN, and still reachable minutes after registering: keep-alives hold the NAT open - Sipral to Sipral, each behind its own NAT: full ICE through two NATs, relayed through TURN where the NATs block a direct path, forked calls relayed to two phones, STUN failover past a dead server - Headless agent, ICE-lite: ICE required by the far end, through the native API and the C interface - Two Sipral stacks, C ABI to C ABI: STIR/SHAKEN: signed calls verified and carried, unsigned calls and untrusted signatures refused ## Compared with PJSIP Sipral's headless agent and pjsua 2.17, PJSIP's own client, against the same Asterisk, timed from packet captures, memory and CPU measured the same way for both. Private memory: 2.8 MB against 12.7 MB idle, 3.1 MB against 16.8 MB with four calls up. CPU with four calls up: 2.3 % of a core against 20.15 %. First INVITE: 869 bytes against 1373. - Registration, first REGISTER to its 200: Sipral 7.7 ms, pjsua 10.3 ms - Outgoing call to Asterisk echo, first INVITE to its 200: Sipral 10.8 ms, pjsua 4.6 ms - Incoming call from Asterisk, INVITE to the client 200: Sipral 9.5 ms, pjsua 4.2 ms - First INVITE, no ICE, size on the wire: Sipral 869 bytes, pjsua 1373 bytes - idle calls: Sipral 4.6 MB / 2.8 MB RSS/private, 0.65 % CPU; pjsua 13.3 MB / 12.7 MB, 0.65 % - 1 calls: Sipral 4.7 MB / 2.9 MB RSS/private, 1.40 % CPU; pjsua 14.2 MB / 13.7 MB, 6.55 % - 4 calls: Sipral 4.9 MB / 3.1 MB RSS/private, 2.30 % CPU; pjsua 17.3 MB / 16.8 MB, 20.15 % - 10 calls: Sipral 5.4 MB / 3.5 MB RSS/private, 4.35 % CPU; pjsua 4 up of 10: 17.4 MB / 16.9 MB, 22.70 % - 100 calls: Sipral 12.2 MB / 10.3 MB RSS/private, 28.65 % CPU; pjsua 4 up of 100: 17.3 MB / 16.7 MB, 20.95 % Under the same impaired links (netem lossy, mobile and satellite profiles) Asterisk rated what the two clients sent alike. After a change of address Sipral re-registered and re-offered on its own (first SIP after 394 ms, audio back after 417 ms). Sipral sends a request over 1300 bytes over TCP, as RFC 3261 section 18.1.1 requires, so its INVITE is never fragmented. ## Status and roadmap - 1.0 is on its way: the interface it freezes is defined, and every version since only adds to it. Packages are published with 1.0; commercial licences are available now. - 1.0: the interface frozen; XCFramework, AAR, NuGet packages, Python wheels and the JVM jar published. - Mobile: a push waking the app from the background, Bluetooth hand-off during a call, builds ready for store review. - After 1.0: video with VP8, VP9 and AV1. ## What it deliberately does not do - Proxy, registrar, B2BUA: Sipral is an endpoint. - Trickle ICE: rare between SIP endpoints, which exchange candidates in the offer and the answer; excluded until a peer needs it. - Video: not before 1.0, so the audio path stays deep; VP8, VP9 and AV1 come after it. - MSRP: session-mode messaging is out of scope; pager-mode MESSAGE is in. - SIP-T, SIP-I, IMS: carrier interconnect and 3GPP registration are a different product. - iLBC: Opus covers the same ground, and better. - AMR, AMR-WB: patented mobile radio codecs; on the IP leg operators offer G.711 or Opus. ## Methodology The conditions behind the figures above. - PJSIP comparison: Debian 13 x86-64, 32 cores; Asterisk 22.10.1; Sipral headless agent, release build, Rust 1.95.0; pjsua 2.17, Alpine package; 29 September 2026. Host: Debian 13, Linux 6.12.95, 32 cores, x86-64, a virtual machine doing other work at the same time. PBX: Asterisk 22.10.1, one account, G.711 only, one contact at a time. Times read off packet captures taken in each client's own network namespace; resident and private memory and user plus system CPU time read from the same /proc files for both; impairments applied with tc netem in both directions, every row rated by one reduced ITU-T G.107 E-model. pjsua ran with the four-call limit its packaged build was compiled with. - Ten thousand calls: Intel Xeon E5-2698 v4 at 2.2 GHz, 32 vCPUs, Debian 13, kernel 6.12; release build, Rust 1.95.0; 29 September 2026. Two processes on one machine, G.711 on a UDP socket per call, a tone both ways, placed at 500 a second and held sixty seconds. - Full method: https://sipral.org/compare/#methodology ## Licensing Dual-licensed: AGPL-3.0-only at no cost, or a commercial licence for closed-source distribution, app stores, unpublished modified services and OEM resale. The commercial licence is one price per company, perpetual for the versions delivered during the maintenance term, with no royalties and no per-seat or per-channel counting. Quotes through https://sipral.org/contact/. ## Questions and answers Q: What is Sipral? A: Sipral (Session Initiation Protocol Rust Audio Layer) is a SIP client stack written in Rust: signalling, media, encryption and NAT traversal behind one C ABI. It is meant to replace PJSIP in softphones and to put a phone line inside a voice agent without a media server in between. Q: Can Sipral replace PJSIP in an existing softphone? A: That is what it is built for. It covers registration, calls, hold, transfers, DTMF, SRTP, DTLS-SRTP, ICE and TURN, and its documentation includes a migration guide that maps each pjsua concept to its equivalent in Sipral. It contains no GPL or LGPL code, so a closed-source product does not inherit GPL obligations from it. Q: Does Sipral contain any GPL or LGPL code? A: No. It was written clean-room from the RFCs and ITU Recommendations, and it links only permissively licensed dependencies (MIT, BSD, Apache-2.0, ISC, Zlib and equivalents). The build fails if a dependency under any other licence is added. Q: Which platforms and languages does Sipral support? A: macOS, iOS, Android, Windows and Linux. Besides Rust and C, it has bindings for Swift, Kotlin and Java, .NET, Python, Dart and Flutter, and React Native. The C header and the Swift, Kotlin, .NET, Python and Dart bindings are generated from one declaration of the C ABI. Q: Can Sipral run without a microphone or speaker, for a voice agent? A: Yes. Next to device mode, where the library opens the microphone and speaker itself, application mode hands every call's audio to the application as 16-bit PCM, one frame at a time, and takes what the far end should hear back the same way. A headless build can also carry that audio over a socket to a separate agent process. Q: Which encryption does Sipral support? A: SRTP keyed by SDES (AES-CM 128 and 256, AES-GCM), DTLS-SRTP written in-tree, an SRTP best-effort mode for PBXs that refuse encrypted offers, TLS with the platform trust store, a private CA or one pinned certificate, and STIR/SHAKEN signing and verification (RFC 8224, RFC 8588). Q: How does Sipral get through NAT? A: STUN with failover, TURN over UDP, TCP and TLS, ICE in the full and lite roles with restarts and consent freshness, RFC 5626 keep-alives, and a call that follows its own address when the network changes. Q: When is 1.0? A: 1.0 is on its way. The interface it freezes is already defined, and every version since only adds to it, so an integration started now carries over. Packages are published with 1.0, and commercial licences are available now. Q: Do I need a commercial licence to ship Sipral in an App Store app? A: Yes. Under the AGPL-3.0 the whole application must reach its users with its source, which the terms of the App Store and Google Play conflict with. The commercial licence allows closed-source distribution and app stores explicitly, with no royalties and no per-seat or per-channel counting. Q: How is Sipral tested? A: With 61 billion fuzzing executions against its parsers and protocol engines and zero crashes, with every message of the RFC 4475 SIP torture suite asserted, and with a lab that runs calls, transfers, DTMF, SRTP, DTLS-SRTP, NAT, ICE and TURN against Asterisk, FreeSWITCH, Kamailio, OpenSIPS, baresip and a live FreePBX, through the native API and again through the C interface. Q: Which RFCs does Sipral implement? A: RFC 3261 and RFC 3263 for signalling and server location, with PRACK (RFC 3262), UPDATE (RFC 3311), session timers (RFC 4028), REFER and Replaces, digest with SHA-256 (RFC 8760), Outbound (RFC 5626), GRUU (RFC 5627) and push notifications (RFC 8599). Media follows RFC 3550, 3711, 5764 and 7714; NAT traversal follows RFC 8489, 8656 and 8445. The full list, by area, is on the features page. Q: Which audio codecs are included? A: Opus, G.722, G.711 (A-law and mu-law), L16 and G.729 with Annexes A and B. The G.729 implementation is bit-exact against the ITU conformance streams. A build without Opus is available for products that cannot carry the Opus patent pool exposure. Q: Why are iLBC and AMR not supported? A: Deliberately. Opus covers the same ground as iLBC and covers it better, and nothing in the field asks for iLBC while refusing Opus. AMR and AMR-WB are patented mobile radio codecs; on the IP leg operators offer G.711 or Opus. Q: Does Sipral support video? A: Not before 1.0, so the audio path stays deep. VP8, VP9 and AV1, RTCP feedback for pictures and per-stream hold are planned after 1.0. Q: What does a sans-I/O core mean in practice? A: The protocol core opens no socket, starts no thread, reads no clock and draws no random number. Bytes go in, bytes to send and events come out, and the caller says what time it is. The RFC 3261 transaction timer diagrams are ordinary unit tests, and a recorded field session replays deterministically. Q: Is the DTLS-SRTP implementation Sipral's own? A: Yes. DTLS 1.2 is implemented in Sipral itself, in both roles, over permissively licensed cryptographic primitives, and interoperates with FreeSWITCH, Asterisk and baresip. Q: Is Sipral a SIP proxy, registrar or B2BUA? A: No. Sipral is an endpoint. Proxy, registrar and B2BUA roles are deliberately out of scope, as are SIP-T and SIP-I carrier interconnect, IMS registration and session-mode messaging (MSRP). Q: Does Sipral support trickle ICE? A: Not yet, deliberately: trickle ICE is rare between SIP endpoints, which exchange candidates in the offer and the answer. It stays excluded until a peer needs it. Q: How do I diagnose a call in the field? A: Each call keeps a record of every decision with its reason code, exported as JSON. Calls can be exported as pcapng with GDPR redaction, health counters are exposed, and the diagnostic trace strips credentials and keys. Q: How much memory does a call take? A: About 72 KB. Ten thousand concurrent calls with audio both ways have been held in one process at that cost each. Of what a live call holds, about 16 KB is signalling state and about 35 KB its media session.