How sipx compares
Every other page here describes sipx in its own terms. This one answers the question you actually arrived with: why this and not one of the others?
It is generated from a checked registry, the same way the RFC compliance table is. A claim with no evidence behind it, or one that has aged past its limit, fails the build rather than appearing here with a caveat.
On a narrow screen, scroll the dense evidence tables horizontally; their columns keep a readable width rather than being compressed into vertical strips.
What choosing sipx wins and what it costs, against the stacks a reader is actually weighing it against. Every cell carries the tier of confidence behind it, because this comparison is asymmetric: sipx's own column is computed from this repository, and every other column is somebody reading someone else's code.
scripts/comparison-report.py --check runs in CI. It fails if a claim cites no evidence,
if a measurement carries no command to re-run it, if a judgment carries no reasoning, or
if any observation has aged past its limit. There is no list of claims it agrees to skip:
a row that cannot be evidenced is demoted to a lower tier or removed, and both change what
this page says.
How to read a confidence tier
| Tier | Means | Who may hold it |
|---|---|---|
generated | Computed from this repository at render time | this repository only |
measured | A reproduce command re-derives it from the subject at the version named | any subject whose source can be read |
documented | The subject's own documentation, release notes or advisories state it | any subject |
assessed | Reviewer judgment from indirect evidence | any subject, and kept in the minority |
An observation older than 180 days fails the check rather than being published with a note, and from 30 days out every run says so — the deadline is meant to be met deliberately rather than discovered. A stack that was not evaluated on a question says so in its own row, so an empty cell never has to be interpreted.
The stacks
| Stack | Language | Licence | Source |
|---|---|---|---|
| sipx | Rust | MIT OR Apache-2.0 | repository |
| PJSIP (pjproject) | C | GPL-2.0-or-later, or a commercial licence | repository |
| reSIProcate | C++ | Vovida Software License 1.0 (per file) | repository |
| baresip (with libre) | C | BSD-3-Clause | repository |
| sipgo | Go | BSD-2-Clause | repository |
| SIPSorcery | C# | BSD-3-Clause | repository |
| rsipstack | Rust | MIT | repository |
| JAIN-SIP (NIST reference implementation) | Java | NIST conditions of use (public domain), with Apache-licensed dependencies | repository |
Language and memory safety
What is it written in, and which class of defect is ruled out by construction rather than by review?
A SIP stack parses unauthenticated bytes from anyone who can reach the port. Whether a malformed message can become memory corruption is decided by the language before any code is written, and it is the one property no amount of testing can retrofit.
| Stack | Finding | Confidence | Evidence | Reproduce |
|---|---|---|---|---|
| sipx | Rust, with unsafe_code set to forbid for the whole workspace — the parser that reads unauthenticated bytes cannot contain a memory-safety defect of its own. Unsafe code still exists below it, in the TLS and codec dependencies. | generated · at 1.0.0-rc.2 | the workspace lint table carrying the setting · non-negotiable 3, which also forbids panics on network input | — |
| PJSIP (pjproject) | C, across 503 non-vendored .c files. Memory safety is a property of the code rather than of the language, so the parser that reads unauthenticated bytes is exactly where a defect becomes memory corruption — which is what the project's own advisory history is largely about. The mitigation here is fuzzing and static analysis rather than a type system. | measured · at 2.17 | the SIP stack sources at the pinned tag · the advisory index, most entries buffer-handling defects | C=$(mktemp -d); git clone -q --depth 1 --branch 2.17 https://github.com/pjsip/pjproject "$C"; find "$C" -name '*.c' -not -path '*/third_party/*' | wc -l |
| reSIProcate | C++, across 1191 .cxx and .cpp files — the largest codebase compared here. Memory safety is again a property of the code; RAII and the standard containers remove a large class of defect that plain C leaves exposed, but nothing rules out the rest by construction. | measured · at resiprocate-1.14.0 | the SIP stack sources at the pinned tag | C=$(mktemp -d); git clone -q --depth 1 --branch resiprocate-1.14.0 https://github.com/resiprocate/resiprocate "$C"; find "$C" \( -name '*.cxx' -o -name '*.cpp' \) | wc -l |
| baresip (with libre) | C, and deliberately small: 249 .c files in the application, with the protocol machinery in the companion re library. Memory safety is a property of the code, but the surface a reviewer has to hold in their head is a fraction of the other C and C++ stacks here, which is a real if unquantified safety argument. | measured · at v4.10.0 | the application sources at the pinned tag · the companion library holding the SIP, RTP and TLS machinery | C=$(mktemp -d); git clone -q --depth 1 --branch v4.10.0 https://github.com/baresip/baresip "$C"; find "$C" -name '*.c' | wc -l |
| sipgo | Go, and notably disciplined about it: 73 source files with zero uses of unsafe.Pointer and zero cgo. The runtime rules out memory-corruption defects in the parser, at the cost of a garbage collector in the media path — which matters less here because it does not carry media. | measured · at v1.4.3 | the SIP layer at the pinned tag | `C=$(mktemp -d); git clone -q --depth 1 --branch v1.4.3 https://github.com/emiago/sipgo "$C"; find "$C" -name '*.go' | wc -l; grep -rn 'unsafe.Pointer\ |
| SIPSorcery | C# on .NET, and the split matters: the core SIPSorcery library enables no unsafe blocks at all, while the optional FFmpeg, VP8 and Windows media packages do — 83 unsafe blocks and 8 DllImport declarations across the tree, none of them in the signalling path. A deployment that stays on the managed codecs keeps the parser fully memory-safe. | measured · at v10.0.13 | the core library project, with no AllowUnsafeBlocks · the optional media packages that do enable it | C=$(mktemp -d); git clone -q --depth 1 --branch v10.0.13 https://github.com/sipsorcery-org/sipsorcery "$C"; grep -c AllowUnsafeBlocks "$C"/src/SIPSorcery/SIPSorcery.csproj || echo 'core: none'; grep -rnE '\bunsafe\s*\{' "$C"/src --include='*.cs' | wc -l |
| rsipstack | Rust, with zero occurrences of unsafe anywhere under src/. It does not declare unsafe_code = "forbid", so the property holds by practice rather than by a lint that would fail the build — but as measured at this tag the parser is entirely safe code. | measured · at v0.5.21 | the stack sources at the pinned tag · the manifest, which declares no lint table | C=$(mktemp -d); git clone -q --depth 1 --branch v0.5.21 https://github.com/restsend/rsipstack "$C"; grep -rn 'unsafe' "$C"/src --include='*.rs' | wc -l; grep -rn 'unsafe_code' "$C" || echo 'no forbid declared' |
| JAIN-SIP (NIST reference implementation) | Java, across 945 source files, with no JNI at all — the two matches for native in the tree are both the word inside a comment. The JVM rules out memory-corruption defects in the parser as thoroughly as any option here. | measured · at JAIN-SIP-1-2-166 | the sources at the pinned tag | C=$(mktemp -d); git clone -q --depth 1 --branch JAIN-SIP-1-2-166 https://github.com/usnistgov/jsip "$C"; find "$C"/src -name '*.java' | wc -l; grep -rnE '\bnative\b' "$C"/src --include='*.java' |
Transports
Which transports can carry signalling, and which of them are secure?
Transport coverage decides where a stack can be deployed at all — a browser peer needs WebSocket, a carrier interconnect usually needs TLS, and a stack without the one you need is not a candidate however good the rest of it is.
| Stack | Finding | Confidence | Evidence | Reproduce |
|---|---|---|---|---|
| sipx | UDP, TCP, TLS, WS, WSS, QUIC. Each is a separate cargo feature and is built alone in CI, so a transport that does not compile with the others off is caught rather than shipped. The QUIC mapping is experimental and has no standards-track counterpart. | generated · at 1.0.0-rc.2 | the transport enum and its wire spellings · one feature per transport · the per-feature build matrix | — |
| PJSIP (pjproject) | UDP, TCP and TLS are implemented; the transport-type enum additionally names DTLS, SCTP and a loopback, but only sip_transport_udp.c, _tcp.c, _tls.c and _loop.c exist — SCTP has no transport module behind its enum value. There is no SIP-over-WebSocket transport in the core stack, which rules it out as a browser-facing signalling peer without a gateway. | measured · at 2.17 | the transport-type enum, including the values with no module · the four transport modules that exist | C=$(mktemp -d); git clone -q --depth 1 --branch 2.17 https://github.com/pjsip/pjproject "$C"; ls "$C"/pjsip/src/pjsip/ | grep sip_transport_; sed -n '/typedef enum pjsip_transport_type_e/,/} pjsip_transport_type_e/p' "$C"/pjsip/include/pjsip/sip_types.h | grep -oE 'PJSIP_TRANSPORT_[A-Z0-9_]+' | sort -u |
| reSIProcate | The broadest transport enum here: TLS, TCP, UDP, SCTP, DCCP, DTLS, WS and WSS. WebSocket and secure WebSocket are named in the same enum as the rest, which makes it one of only three stacks on this page that can face a browser without a gateway. | measured · at resiprocate-1.14.0 | the TransportType enum | C=$(mktemp -d); git clone -q --depth 1 --branch resiprocate-1.14.0 https://github.com/resiprocate/resiprocate "$C"; sed -n '/^typedef enum/,/} TransportType;/p' "$C"/rutil/TransportType.hxx |
| baresip (with libre) | UDP, TCP, TLS, WS and WSS, spelled as SIP_TRANSP_* and selected per account. WebSocket support puts it among the stacks that can face a browser directly. | measured · at v4.10.0 | registration switching on the transport · the default transport | C=$(mktemp -d); git clone -q --depth 1 --branch v4.10.0 https://github.com/baresip/baresip "$C"; grep -rhoE 'SIP_TRANSP_[A-Z]+' "$C" | sort -u |
| sipgo | UDP, TCP, TLS, WS and WSS, one source file per transport with a shared connection pool. A clean, complete set for signalling, including the two that let it face a browser. | measured · at v1.4.3 | transport_udp, _tcp, _tls, _ws, _wss and the connection pool | C=$(mktemp -d); git clone -q --depth 1 --branch v1.4.3 https://github.com/emiago/sipgo "$C"; ls "$C"/sip | grep transport_ |
| SIPSorcery | udp, tcp, tls, ws and wss, as a single closed enum. The set is the same as the Go and Rust peers here, and includes both WebSocket forms. | measured · at v10.0.13 | the SIPProtocolsEnum declaration | C=$(mktemp -d); git clone -q --depth 1 --branch v10.0.13 https://github.com/sipsorcery-org/sipsorcery "$C"; f=$(grep -rl 'enum SIPProtocolsEnum' "$C"/src | head -1); sed -n '/enum SIPProtocolsEnum/,/}/p' "$f" |
| rsipstack | UDP, TCP, TLS and WebSocket, one module each, with TLS and WebSocket behind cargo features that are on by default. No QUIC and no SCTP. | measured · at v0.5.21 | udp.rs, tcp.rs, tls.rs, websocket.rs · the feature table gating TLS and WebSocket | C=$(mktemp -d); git clone -q --depth 1 --branch v0.5.21 https://github.com/restsend/rsipstack "$C"; ls "$C"/src/transport/; sed -n '/\[features\]/,/^\[/p' "$C"/Cargo.toml |
| JAIN-SIP (NIST reference implementation) | UDP, TCP, TLS and SCTP — a message processor and channel per transport, with SCTP as a separate module. It is the only stack here that implements SCTP, and the only one with no WebSocket transport at all, so it cannot face a browser without a gateway. | measured · at JAIN-SIP-1-2-166 | UDP, TCP and TLS message processors · the SCTP message processor | C=$(mktemp -d); git clone -q --depth 1 --branch JAIN-SIP-1-2-166 https://github.com/usnistgov/jsip "$C"; ls "$C"/src/gov/nist/javax/sip/stack/ | grep -E 'MessageProcessor.java|MessageChannel.java'; ls "$C"/src/gov/nist/javax/sip/stack/sctp |
Media
What can it encode, carry and protect, and does it terminate media at all?
Signalling-only stacks and media-terminating stacks solve different problems, and the difference is invisible in a feature list that says 'SIP'. This row separates them, and then says which codecs and which media protection each side actually has.
| Stack | Finding | Confidence | Evidence | Reproduce |
|---|---|---|---|---|
| sipx | Terminates media. Codecs claimed and backed by an encoder and a decoder: G.711, L16, Opus. SRTP, DTLS-SRTP and ICE are implemented and reachable from the call layer, which is checked rather than asserted. One fail-closed browser-audio profile composes WSS, ICE, DTLS-SRTP and Opus and has a native-browser proof in both SIP roles; it is not a general browser, TURN or video stack. | generated · at 1.0.0-rc.2 | the codec implementations the claim is checked against · the check that refuses a codec name with no encoder and decoder behind it · the generated RFC table, including the media and security layers · the exact native-browser positives, negatives and evidence contract · the native-browser compatibility job that runs the proof | — |
| PJSIP (pjproject) | Terminates media, and has the widest codec set of any stack here: G.711, G.722, G.722.1, GSM, iLBC, L16, Opus, Speex, SILK, AMR (via opencore), G.729 (via bcg729), plus H.263, H.264 and VP8/VP9 video. SRTP is present with both SDES and DTLS-SRTP keying, alongside an ICE transport and a jitter buffer. This is a full media framework, not a signalling library. | measured · at 2.17 | one source file per codec · transport_srtp, transport_srtp_dtls, transport_srtp_sdes, transport_ice, jbuf | C=$(mktemp -d); git clone -q --depth 1 --branch 2.17 https://github.com/pjsip/pjproject "$C"; ls "$C"/pjmedia/src/pjmedia-codec/*.c; ls "$C"/pjmedia/src/pjmedia/ | grep -iE 'srtp|transport_ice|jbuf' |
| reSIProcate | Media is a separate layer rather than part of the stack: reflow provides an SRTP-capable media flow built on a bundled libsrtp, and media/ carries GStreamer and Kurento integrations plus an SDP container. So it terminates media, but by wiring an external media engine rather than by shipping codecs of its own — which makes the codec question one about the engine you choose. | measured · at resiprocate-1.14.0 | the media flow layer, including Srtp2Helper · GStreamer and Kurento integrations, and the SDP container | C=$(mktemp -d); git clone -q --depth 1 --branch resiprocate-1.14.0 https://github.com/resiprocate/resiprocate "$C"; ls "$C"/reflow "$C"/media; ls "$C"/contrib | grep srtp |
| baresip (with libre) | Terminates media, and everything is a module: separate modules for G.711, G.722, G.722.1, Opus, Opus multistream, AAC, AMR and Codec2, plus srtp, dtls_srtp and ice. The modular build is the distinguishing property — a deployment compiles in the codecs it needs and nothing else, which matters on embedded targets. | measured · at v4.10.0 | one module per codec and per media-security mechanism | C=$(mktemp -d); git clone -q --depth 1 --branch v4.10.0 https://github.com/baresip/baresip "$C"; ls "$C"/modules | grep -iE 'opus|g711|g722|amr|codec2|aac|srtp|dtls|ice' |
| sipgo | Signalling only. Two files mention RTP and there is no codec, SRTP, ICE or jitter-buffer code in the repository; media lives in a separate companion library by the same author. That is a product decision rather than a gap, but it changes what adopting it means: the media half is a second dependency with its own maturity. | measured · at v1.4.3 | the repository root, with no media directory · the README, which points media users at a companion library | C=$(mktemp -d); git clone -q --depth 1 --branch v1.4.3 https://github.com/emiago/sipgo "$C"; grep -rl 'rtp' "$C" --include='*.go' | wc -l; ls "$C" | grep -icE 'media|codec|rtp' || echo 0 |
| SIPSorcery | Terminates media, and is the only stack here whose media half is a full WebRTC implementation in the same language: RTCPeerConnection, an ICE channel, DTLS-SRTP with both client and server roles, SCTP data channels, and managed G.711, G.722 and G.729 codecs. If the target is a browser peer rather than a SIP peer, this is the closest fit on the page. | measured · at v10.0.13 | RTCPeerConnection, RtpIceChannel, DtlsSrtpTransport and the SRTP context · the managed G.711, G.722 and G.729 codecs | C=$(mktemp -d); git clone -q --depth 1 --branch v10.0.13 https://github.com/sipsorcery-org/sipsorcery "$C"; find "$C"/src/SIPSorcery \( -iname '*srtp*' -o -iname '*dtls*' -o -iname '*ice*' \) -name '*.cs' | sed 's|.*/||' | sort; find "$C"/src/SIPSorcery -ipath '*odec*' -name '*.cs' | sed 's|.*/||' |
| rsipstack | Signalling only. The source tree is dialog, transaction, transport, resolver and SIP types; there is an rtp-rs dependency for packet types but no media session, codec, SRTP or ICE code. Terminating media means bringing your own media stack. | measured · at v0.5.21 | the module list, with no media directory · rtp-rs as the only media-adjacent dependency | `C=$(mktemp -d); git clone -q --depth 1 --branch v0.5.21 https://github.com/restsend/rsipstack "$C"; ls "$C"/src; grep -n 'rtp\ |
| JAIN-SIP (NIST reference implementation) | Signalling only, and by design: it is the reference implementation of a signalling API specification, so there is no RTP, codec, SRTP or ICE code anywhere in the tree. Media is the application's problem entirely. | measured · at JAIN-SIP-1-2-166 | the specification's API surface, which is signalling only | C=$(mktemp -d); git clone -q --depth 1 --branch JAIN-SIP-1-2-166 https://github.com/usnistgov/jsip "$C"; find "$C"/src \( -ipath '*rtp*' -o -ipath '*codec*' \) | wc -l |
Security posture
What does the project do to find its own vulnerabilities, and what has it published when it found them?
Every long-lived network stack has had vulnerabilities; the useful question is whether the project looks for them and says so. Published advisories are a sign of maturity, not of weakness — silence is the thing to be wary of.
| Stack | Finding | Confidence | Evidence | Reproduce |
|---|---|---|---|---|
| sipx | No published advisories, because no vulnerability has been reported by anyone outside the project — which is a statement about the project's age, not its safety. A fuzz job runs the parser targets in CI seeded from the RFC torture corpora, and a dependency-advisory job runs on every change. There has been no third-party audit. | documented · at 1.0.0-rc.2 | the fuzz smoke job and the advisory job · the published security surface and reporting route · the generated maturity report, including what is still open | — |
| PJSIP (pjproject) | 53 published advisories — by a wide margin the most of any stack here, and the strongest evidence on this page that a project receives reports and acts on them. It carries in-tree fuzz targets, an OSS-Fuzz workflow, CodeQL analysis, daily Coverity scans and sanitiser builds on Linux and macOS, plus a SECURITY.md reporting route. Read the count as attention, not as weakness. | measured · at 2.17 | the published advisory index · cifuzz, codeql-analysis, coverity-scan-daily, sanitiser builds · the reporting route | gh api '/repos/pjsip/pjproject/security-advisories?per_page=100' --jq 'length'; C=$(mktemp -d); git clone -q --depth 1 --branch 2.17 https://github.com/pjsip/pjproject "$C"; ls "$C"/.github/workflows "$C"/tests/fuzz |
| reSIProcate | No advisories published through the repository's own security tab, and no SECURITY.md at the pinned tag. CI is two build workflows with no sanitiser, fuzzing or static-analysis job; the only fuzzer in the tree belongs to the bundled libsrtp rather than to the SIP parser. For a codebase of this age and size that absence is the notable finding, not a clean bill of health. | measured · at resiprocate-1.14.0 | the advisory index, empty at the time of reading · linux-ci and windows-ci, and nothing else | gh api '/repos/resiprocate/resiprocate/security-advisories?per_page=100' --jq 'length'; C=$(mktemp -d); git clone -q --depth 1 --branch resiprocate-1.14.0 https://github.com/resiprocate/resiprocate "$C"; ls "$C"/.github/workflows; find "$C" -maxdepth 3 -type d -iname 'fuzz*' |
| baresip (with libre) | One published advisory, and no SECURITY.md at the pinned tag — but the CI is the most safety-oriented of any C project here: dedicated sanitizers.yml, valgrind.yml, clang-analyze.yml, coverage.yml and lint.yml workflows alongside the build matrix. There are no in-tree fuzz targets. | measured · at v4.10.0 | sanitizers, valgrind, clang-analyze, coverage, lint · the advisory index | gh api '/repos/baresip/baresip/security-advisories?per_page=100' --jq 'length'; C=$(mktemp -d); git clone -q --depth 1 --branch v4.10.0 https://github.com/baresip/baresip "$C"; ls "$C"/.github/workflows |
| sipgo | Three published advisories and a SECURITY.md reporting route — for a project this young, publishing at all is the signal. It carries a Go fuzzing corpus under sip/testdata/fuzz. There is no sanitiser or race-detector job in the single CI workflow, which for Go means the race detector is available and not wired in. | measured · at v1.4.3 | the published advisories · the fuzzing corpus · the reporting route | gh api '/repos/emiago/sipgo/security-advisories?per_page=100' --jq 'length'; C=$(mktemp -d); git clone -q --depth 1 --branch v1.4.3 https://github.com/emiago/sipgo "$C"; ls "$C"/sip/testdata/fuzz; ls "$C"/.github/workflows |
| SIPSorcery | Two published advisories and a SECURITY.md reporting route. There are no in-tree fuzz targets and no sanitiser or analysis job among the six CI workflows, which are build, test, packaging and smoke-test jobs. The managed runtime carries much of the weight that fuzzing carries for the C stacks here. | measured · at v10.0.13 | the published advisories · the CI workflows, all build and test | gh api '/repos/sipsorcery-org/sipsorcery/security-advisories?per_page=100' --jq 'length'; C=$(mktemp -d); git clone -q --depth 1 --branch v10.0.13 https://github.com/sipsorcery-org/sipsorcery "$C"; ls "$C"/.github/workflows; find "$C" -maxdepth 3 -type d -iname 'fuzz*' |
| rsipstack | No published advisories, no SECURITY.md, no fuzz targets and no sanitiser job — the CI is a single ci.yml. For a project this new that is unsurprising rather than alarming, but it means the memory safety of the language is doing essentially all of the work, and nothing in the build is looking for logic defects in the parser. | measured · at v0.5.21 | the advisory index, empty at the time of reading · a single CI workflow | gh api '/repos/restsend/rsipstack/security-advisories?per_page=100' --jq 'length'; C=$(mktemp -d); git clone -q --depth 1 --branch v0.5.21 https://github.com/restsend/rsipstack "$C"; ls "$C"/.github/workflows; find "$C" -maxdepth 3 -type d -iname 'fuzz*' | wc -l |
| JAIN-SIP (NIST reference implementation) | No published advisories, no SECURITY.md, no fuzz targets, and no CI configuration at all — the repository predates GitHub Actions and was never migrated. Nothing in this repository is currently looking for defects in it, which for a stack still on classpaths is the finding that matters most here. | measured · at JAIN-SIP-1-2-166 | the advisory index, empty at the time of reading · the tree at the pinned tag, with no CI configuration | gh api '/repos/usnistgov/jsip/security-advisories?per_page=100' --jq 'length'; C=$(mktemp -d); git clone -q --depth 1 --branch JAIN-SIP-1-2-166 https://github.com/usnistgov/jsip "$C"; ls "$C"/.github/workflows 2>/dev/null || echo 'no workflows' |
Testing and CI
What has to be green before a change lands, and is conformance measured or asserted?
A capability table tells you what somebody believes; the gate tells you what is checked every time. This row is the closest thing to a measurement of whether the other rows can be trusted.
| Stack | Finding | Confidence | Evidence | Reproduce |
|---|---|---|---|---|
| sipx | 37 gate steps must pass before a change lands, and a self-check refuses to let the local gate omit a CI job. Conformance is a checked registry of 82 RFCs whose published table is generated from it, so a claim cannot drift from the code behind it. Both RFC torture corpora are re-recovered from the RFC editor and diffed against the tree rather than transcribed. | generated · at 1.0.0-rc.2 | the gate, and the check that holds it against CI · the conformance registry the published table is generated from · every way the conformance check fails | — |
| PJSIP (pjproject) | CI builds on Linux, macOS and Windows, with fuzzing, CodeQL and Coverity alongside. Around 40 test translation units, and the SIP message tests draw on the RFC 4475 torture corpus. Conformance to the RFCs is documented in prose rather than checked by anything in the build. | measured · at 2.17 | the message tests, including torture cases · the CI matrix | C=$(mktemp -d); git clone -q --depth 1 --branch 2.17 https://github.com/pjsip/pjproject "$C"; ls "$C"/.github/workflows; find "$C" -path '*/test/*' -name '*.c' | wc -l; grep -li torture "$C"/pjsip/src/test/*.c |
| reSIProcate | 217 test translation units, including a dedicated SipTortureTests.cxx with around 270 assertions — the most thorough parser-torture suite found on this page outside sipx's own corpora. Against that, CI is only two build workflows, so much of that suite's value depends on whoever runs it locally. | measured · at resiprocate-1.14.0 | the torture suite · the stack test directory | `C=$(mktemp -d); git clone -q --depth 1 --branch resiprocate-1.14.0 https://github.com/resiprocate/resiprocate "$C"; find "$C" -path '/test/' ( -name '.cxx' -o -name '.cpp' ) | wc -l; grep -c 'assert\ |
| baresip (with libre) | 34 test translation units against ten CI workflows — the ratio is unusual, and it says where this project puts its trust: not in a large unit-test suite but in building the same code many ways, under sanitisers, valgrind and a static analyser, on Linux, Fedora, Windows and mingw. No RFC torture corpus is present in this repository. | measured · at v4.10.0 | the test directory · ten workflows, several of them analysis rather than build | C=$(mktemp -d); git clone -q --depth 1 --branch v4.10.0 https://github.com/baresip/baresip "$C"; find "$C"/test -name '*.c' | wc -l; ls "$C"/.github/workflows | wc -l |
| sipgo | 83 test functions with 13 skips, and a table-driven TestTorture covering roughly 23 RFC 4475 cases — a real torture suite rather than a mention of one, which puts it ahead of most of this page on parser conformance for its age. CI is a single test workflow. | measured · at v1.4.3 | the table-driven torture suite · the single test workflow | C=$(mktemp -d); git clone -q --depth 1 --branch v1.4.3 https://github.com/emiago/sipgo "$C"; grep -rc 'func Test' "$C" --include='*_test.go' | awk -F: '{s+=$2} END{print s}'; grep -r 't.Skip' "$C" --include='*_test.go' | wc -l; grep -c '^\s*"' "$C"/sip/torture_test.go |
| SIPSorcery | 1172 xunit facts with 13 carrying a skip reason — the largest unit-test suite on this page — and a TortureTests.cs covering 12 RFC 4475 cases of which one is skipped. CI runs the suite on Linux, macOS and Windows. Conformance is asserted in documentation rather than derived from a checked registry. | measured · at v10.0.13 | the torture suite, and the one skipped case · the three-platform test matrix | `C=$(mktemp -d); git clone -q --depth 1 --branch v10.0.13 https://github.com/sipsorcery-org/sipsorcery "$C"; grep -r '[Fact]\ |
| rsipstack | 242 test functions under src/, none ignored — a healthy suite for the size. No RFC torture corpus is present, so parser conformance rests on hand-written cases, and there is no generated conformance record to check a claim against. | measured · at v0.5.21 | tests live in the modules they cover · the transport test directory | `C=$(mktemp -d); git clone -q --depth 1 --branch v0.5.21 https://github.com/restsend/rsipstack "$C"; grep -r '#[tokio::test]\ |
| JAIN-SIP (NIST reference implementation) | Around 200 JUnit tests including a torture suite and a TCK message-flow harness — a serious suite for its era, and the TCK is something no other stack here has. But nothing runs it automatically: with no CI configuration, every one of those tests is only as good as the last person to run it locally. | measured · at JAIN-SIP-1-2-166 | the torture suite · the TCK message-flow harness | C=$(mktemp -d); git clone -q --depth 1 --branch JAIN-SIP-1-2-166 https://github.com/usnistgov/jsip "$C"; grep -r 'public void test' "$C"/src/test --include='*.java' | wc -l; ls "$C"/src/test/torture; ls "$C"/.github/workflows 2>/dev/null || echo 'no CI' |
Maturity and adoption
How long has it existed, who runs it in production, and has anyone outside the project audited it?
This is the row where a new stack loses to an old one, and saying so is what makes the rest of the page worth reading. Years in production against real peers is evidence no test suite substitutes for.
| Stack | Finding | Confidence | Evidence | Reproduce |
|---|---|---|---|---|
| sipx | This is where sipx loses, and not narrowly. Its repository history is measured in weeks where the oldest stacks here are measured in decades; its first public boundary is a beta rather than a stable 1.0; it has no known production deployment outside its own authors; no third party has audited it; and it has never had a vulnerability reported, which says only that nobody has looked. Years of carrying real traffic against real peers is evidence no gate substitutes for, and several stacks on this page have it. | documented · at 1.0.0-rc.2 | predicate counts for the alpha and beta boundaries, generated from the backlog · the full release history, which is short · the two independent peers the suite runs against | — |
| PJSIP (pjproject) | The most mature stack on this page: first tag December 2005, 80 tags since, 6999 commits, and a commit on the day this was measured. Twenty years of carrying real traffic against real peers is evidence no test suite substitutes for, and nothing here matches it. The licence is the trade — GPL-2.0-or-later, or a commercial licence from the vendor. | measured · at 2.17 | the release history from 2005 onward · GPL-2.0; the commercial option is offered separately by the vendor | C=$(mktemp -d); git clone -q --filter=blob:none https://github.com/pjsip/pjproject "$C"; git -C "$C" rev-list --count HEAD; git -C "$C" tag | wc -l; git -C "$C" log -1 --format=%ad --date=short $(git -C "$C" tag --sort=creatordate | head -1) |
| reSIProcate | First tag May 2004 — the oldest lineage here — with 190 tags, 11537 commits and activity within the last fortnight of measurement. It also ships more than a library: repro is a working proxy and registrar, and reTurn a TURN server, so adopting it can mean adopting a stack and its server estate together. | measured · at resiprocate-1.14.0 | the release history from 2004 onward · the proxy and registrar shipped alongside the stack | C=$(mktemp -d); git clone -q --filter=blob:none https://github.com/resiprocate/resiprocate "$C"; git -C "$C" rev-list --count HEAD; git -C "$C" tag | wc -l; git -C "$C" log -1 --format=%ad --date=short $(git -C "$C" tag --sort=creatordate | head -1) |
| baresip (with libre) | First tag June 2014, 88 tags, 4508 commits, and a commit within a fortnight of measurement. Its lineage is older than that date suggests — the re library it is built on predates the split — and it is now maintained by a foundation rather than a single author, which is a succession signal most projects this size do not have. | measured · at v4.10.0 | the release history · BSD-3-Clause, held by the Baresip Foundation | C=$(mktemp -d); git clone -q --filter=blob:none https://github.com/baresip/baresip "$C"; git -C "$C" rev-list --count HEAD; git -C "$C" tag | wc -l; git -C "$C" log -1 --format=%ad --date=short $(git -C "$C" tag --sort=creatordate | head -1) |
| sipgo | First tag May 2022, 74 tags since, 577 commits, active within a fortnight of measurement — young but shipping steadily, and past 1.0. The BSD-2-Clause licence makes it one of the least encumbered options here. | measured · at v1.4.3 | the release history from 2022 onward · BSD-2-Clause | C=$(mktemp -d); git clone -q --filter=blob:none https://github.com/emiago/sipgo "$C"; git -C "$C" rev-list --count HEAD; git -C "$C" tag | wc -l; git -C "$C" log -1 --format=%ad --date=short $(git -C "$C" tag --sort=creatordate | head -1) |
| SIPSorcery | First tag November 2019, 151 tags since, 3077 commits, active within a week of measurement — a fast release cadence and well past 1.0. Distributed through NuGet, which for a .NET consumer is the lowest-friction adoption path on this page. BSD-3-Clause. | measured · at v10.0.13 | the release history from 2019 onward · BSD-3-Clause | C=$(mktemp -d); git clone -q --filter=blob:none https://github.com/sipsorcery-org/sipsorcery "$C"; git -C "$C" rev-list --count HEAD; git -C "$C" tag | wc -l; git -C "$C" log -1 --format=%ad --date=short $(git -C "$C" tag --sort=creatordate | head -1) |
| rsipstack | The youngest stack here by release history: exactly one tag, created three weeks before this was measured, over 442 commits. It is published on crates.io and actively developed, but there is no release track record to read yet and the version number says pre-1.0. MIT licensed. | measured · at v0.5.21 | the release history: one tag · the published crate | C=$(mktemp -d); git clone -q --filter=blob:none https://github.com/restsend/rsipstack "$C"; git -C "$C" rev-list --count HEAD; git -C "$C" tag | wc -l; git -C "$C" log -1 --format=%ad --date=short $(git -C "$C" tag --sort=creatordate | head -1) |
| JAIN-SIP (NIST reference implementation) | The oldest and the most dormant. First tag June 2003 and 60 tags, but the last tag in this repository is from September 2011 and the last commit from July 2024 — fifteen years without a release here. It is still on classpaths through Maven Central and through downstream forks that continued after this repository stopped, so adopting it means adopting a fork's release history rather than this one's. | measured · at JAIN-SIP-1-2-166 | the release history, ending in 2011 · the artefact still published to Maven Central | C=$(mktemp -d); git clone -q --filter=blob:none https://github.com/usnistgov/jsip "$C"; git -C "$C" tag | wc -l; git -C "$C" log -1 --format=%ad --date=short $(git -C "$C" tag --sort=creatordate | head -1); git -C "$C" log -1 --format=%ad --date=short $(git -C "$C" tag --sort=-creatordate | head -1); git -C "$C" log -1 --format=%ad --date=short |
Endpoint capability ledger: rsipstack
This is the leaf-level ownership profile for one immutable subject release. It is a finite discovery gate, not a score: an open sipx row names the story that closes it, and a platform row is excluded or assigned to the cluster repository explicitly. The immutable source revision is retained in the checked data and each row states the subject version it was evaluated against.
| Category | Capability | Subject version | Confidence | Ownership | Status | Evidence | Story or rationale |
|---|---|---|---|---|---|---|---|
| core | Parse, build and serialize SIP requests and responses | v0.5.21 | measured | sipx | implemented | public SIP message, parser and serializer modules | — |
| core | Typed headers with editable unknown-header preservation | v0.5.21 | measured | sipx | implemented | typed and untyped header APIs | — |
| transactions | Client and server INVITE and non-INVITE transactions | v0.5.21 | measured | sipx | implemented | transaction role and state drivers | — |
| lifecycle | Cancelable transaction timers and endpoint shutdown | v0.5.21 | measured | sipx | implemented | timer queue API | — |
| transports | UDP signalling transport | v0.5.21 | measured | sipx | implemented | UDP connection and listener | — |
| transports | TCP signalling transport and connection reuse | v0.5.21 | measured | sipx | implemented | TCP connection implementation | — |
| transports | TLS signalling transport | v0.5.21 | measured | sipx | implemented | TLS listener and client transport | — |
| transports | WebSocket signalling transport | v0.5.21 | measured | sipx | implemented | WebSocket transport implementation | — |
| transports | SIP target resolution with an injectable lookup source | v0.5.21 | measured | sipx | implemented | SIP resolver and lookup-source trait | — |
| operations | Atomic live reload of the TLS server identity | v0.5.21 | measured | sipx | implemented | reloadable certificate resolver | — |
| endpoint | Endpoint message inspection and bounded policy hooks | v0.5.21 | measured | sipx | implemented | MessageInspector and TransportEventInspector traits | — |
| endpoint | Injectable endpoint target selection | v0.5.21 | measured | sipx | implemented | TargetLocator and domain resolver hooks | — |
| operations | Endpoint counters and outstanding-transaction count | v0.5.21 | measured | sipx | implemented | EndpointStats and running transaction access | — |
| operations | Inspect stable identifiers for running transactions | v0.5.21 | measured | sipx | implemented | running transaction key access | — |
| authentication | Digest challenge response and verification | v0.5.21 | measured | sipx | implemented | digest credential, calculation and verification APIs | — |
| dialogs | Authenticated registration with granted expiry | v0.5.21 | measured | sipx | implemented | registration client API | — |
| dialogs | Report the public address learned during registration | v0.5.21 | measured | sipx | implemented | received and rport address extraction | — |
| dialogs | Client and server INVITE dialogs | v0.5.21 | measured | sipx | implemented | invitation, client-dialog and server-dialog modules | — |
| dialogs | Reliable provisional responses and PRACK | v0.5.21 | documented | sipx | implemented | public reliable-provisional capability | — |
| methods | CANCEL, BYE and re-INVITE dialog operations | v0.5.21 | measured | sipx | implemented | cancel, hangup and reinvite methods | — |
| methods | UPDATE requests on a dialog | v0.5.21 | measured | sipx | implemented | UPDATE operation | — |
| methods | OPTIONS requests and endpoint responses | v0.5.21 | measured | sipx | implemented | OPTIONS operation | — |
| methods | REFER and transfer NOTIFY operations | v0.5.21 | measured | sipx | implemented | REFER and NOTIFY operations | — |
| methods | Application-owned INFO in both directions | v0.5.21 | measured | sipx | implemented | INFO dialog operation | — |
| methods | Application-owned MESSAGE in both directions | v0.5.21 | measured | sipx | implemented | generic dialog MESSAGE operation | — |
| methods | Authenticated generic in-dialog request operation | v0.5.21 | measured | sipx | implemented | generic dialog request method | — |
| dialogs | Create, refresh and terminate outbound subscriptions | v0.5.21 | measured | sipx | implemented | client subscription dialog | — |
| dialogs | Accept subscriptions and originate NOTIFY | v0.5.21 | measured | sipx | implemented | server subscription dialog | — |
| dialogs | Create, refresh and remove publications with entity tags | v0.5.21 | measured | sipx | implemented | client publication dialog | — |
| dialogs | Accept inbound publications through the endpoint | v0.5.21 | measured | sipx | implemented | server publication dialog | — |
| lifecycle | Serializable dialog snapshot for external restoration policy | v0.5.21 | measured | sipx | implemented | DialogSnapshot public type | — |
| examples | Stateful proxy with request and response forwarding | v0.5.21 | measured | sipx-clstr | tracked | stateful proxy example | tracking story |
| examples | Registrar and in-memory location service example | v0.5.21 | measured | sipx-clstr | tracked | REGISTER handling and location table | tracking story |
| examples | Registering caller and answerer example | v0.5.21 | measured | sipx | implemented | user-agent client example | — |
| media | RTP file playback | v0.5.21 | measured | sipx | implemented | RTP playback and echo helper | — |
| examples | Runnable RTP echo endpoint example | v0.5.21 | measured | sipx | implemented | RTP echo helper | — |
| endpoint | Dynamically restrict accepted transport sources | v0.5.21 | measured | sipx | implemented | TransportWhitelist and its update API | — |
| operations | Machine-driven call-load responder | v0.5.21 | measured | sipx | implemented | benchmark user agent server mode | — |
| endpoint | WebAssembly endpoint target | v0.5.21 | documented | not-shipped | absent | WASM remains unchecked in the public TODO | — |
| media | Bundled WebRTC media engine | v0.5.21 | documented | not-applicable | excluded | WebRTC is described as an integration use case | The subject describes WebRTC integration as a use case but ships no media engine; sipx likewise keeps browser-native WebRTC outside the Rust stack. |
Comparative signalling load
One neutral workload — UDP dialog signalling without SDP or media — offered by the same pinned driver to each endpoint acting as responder.
What the harness measures
- Fixed load: fixed open-loop offered load, so a slowing responder never causes the caller to quietly offer less work.
- Validity before speed: correctness qualification completes one hundred low-rate five-message dialogs before any capacity number is admitted.
- Instrument margin: driver headroom is proven against a packaged minimal fixture at twice the tested ceiling.
- Repeatability: six rates and five repetitions per rate expose a supported point and its achieved-throughput spread rather than one best run.
- Behavior and cost: setup and teardown latency, process resource samples, response counts and errors are retained for every repetition.
- Finite ownership: bounded cleanup must stop admission, drain state and exit the supervised process group without escalation.
- Auditability: raw evidence includes manifests, environment and toolchain inventory, exact commands, seeds, artifact hashes and per-repetition JSON under
docs/comparison/load/.
The following are not inferred from this result: secure transports, connection churn, audio, proxy, registrar, routing or cluster behavior; those workloads belong to sipx.clstr.
Responder capacity: sipx
| Rate (calls/s) | Outcome | Median achieved (dialogs/s) | Spread [min, max] | Setup p99 (ms, median) |
|---|---|---|---|---|
| 32 | supported (5/5) | 32.0 | [32.0, 32.0] | 1 |
| 64 | supported (5/5) | 64.0 | [64.0, 64.0] | 3 |
| 128 | supported (5/5) | 128.0 | [128.0, 128.0] | 3 |
| 256 | supported (5/5) | 256.0 | [256.0, 256.0] | 2 |
| 512 | supported (5/5) | 512.0 | [512.0, 512.0] | 1 |
| 1024 | supported (5/5) | 1024.0 | [1024.0, 1024.0] | 0 |
Capacity is at least 1024 calls/s: the highest tested rate passed, so this ladder established a lower bound rather than finding a failure ceiling. Achieved interval [1024.0, 1024.0] dialogs/s over five repetitions.
- Caller (UAC) direction: not measured — the pinned build exposes a bounded responder but no neutral-profile caller command
- Internal state visibility:
endpoint-reported— post-drain dialog, transaction, route and task state comes from the endpoint's terminal summary
Responder capacity: sipgo
| Rate (calls/s) | Outcome | Median achieved (dialogs/s) | Spread [min, max] | Setup p99 (ms, median) |
|---|---|---|---|---|
| 32 | supported (5/5) | 32.0 | [32.0, 32.0] | 1 |
| 64 | supported (5/5) | 64.0 | [64.0, 64.0] | 1 |
| 128 | supported (5/5) | 128.0 | [128.0, 128.0] | 3 |
| 256 | supported (5/5) | 256.0 | [256.0, 256.0] | 1 |
| 512 | supported (5/5) | 512.0 | [512.0, 512.0] | 1 |
| 1024 | supported (5/5) | 1024.0 | [1024.0, 1024.0] | 1 |
Capacity is at least 1024 calls/s: the highest tested rate passed, so this ladder established a lower bound rather than finding a failure ceiling. Achieved interval [1024.0, 1024.0] dialogs/s over five repetitions.
- Caller (UAC) direction: not measured — the pinned library was adapted only as a neutral-profile responder; caller behavior is outside this endpoint comparison
- Internal state visibility:
harness-observed— the adapter reports active profile dialogs; transaction cleanup is evidenced by bounded process shutdown rather than subject-internal counters
The achieved-throughput intervals overlap, so this comparison is inconclusive on this machine and profile.
How this was produced
Each subject was cloned and checked out at the tag named in its rows, and each measured finding
carries the command that re-derives it from that tag — the commands in the tables are the ones that
were run, not illustrations of commands that could be. Re-running one is the intended way to
disagree with a row.
The dataset is refreshed by a procedure kept in the repository rather than by whoever remembers how it was done last time, and every observation expires. When one does, the build fails and names the refresh command; nothing here can quietly get old.
Where this comparison is weakest
The evidence is asymmetric, and the asymmetry flatters sipx. Its column is computed from a repository its authors know completely — the transport list is read from the enum, the codec list from the checker that already refuses an unbacked codec claim, the gate step count from the gate itself. Every other column is somebody reading unfamiliar code, at one tag, in a bounded amount of time. Structural facts survive that well; judgement about what a codebase is like does not, and none is offered here.
Reading a repository is not auditing it. These rows report what a project publishes about itself and what its build configuration admits. They do not report defects, and a quiet advisory history is evidence about attention rather than about safety — often the opposite of what it looks like. The stack here with by far the most published advisories is also the most scrutinised one.
This is not an interop result. Two stacks appearing to implement the same thing is not evidence that they work together. Interop is a property of a test run against another implementation, and sipx's live in its interop suite against independent peers, not in this table.
There is no winner. The registry refuses a weighted score by name, because one number hides the tier behind it, and a comparison whose asymmetry is hidden is worse than none.
Where sipx loses
Stated here as well as in the table, because it is the reason to trust the rest of the page.
- Age. sipx's repository history is measured in weeks. Two of the stacks compared here have been shipping releases since 2004 and 2005. Two decades of carrying real traffic against real peers is evidence that no gate, no test count and no generated table substitutes for.
- Adoption. There is no known production deployment outside its own authors, and no downstream ecosystem. Several stacks here are packaged by distributions or shipped through a package registry with a long consumer list.
- Audit. No third party has reviewed the code, and no vulnerability has ever been reported — which says only that nobody has looked yet.
- Codec breadth. The widest codec set on this page belongs elsewhere, and it is not close.
- Media engines and servers. Some of these projects ship a proxy, a registrar or a TURN server alongside the library. sipx is a library and a diagnostic CLI.
What sipx offers against that is narrower and easier to check: a memory-safe parser with unsafe
forbidden workspace-wide, a conformance record that is generated from the code rather than
asserted, and a gate that has to be green before anything lands. Whether that trade is the right
one depends on what you are building, which is why this page reports rather than concludes.
What is not here
There is no Zig entry, because no Zig SIP stack was found to compare — the option there is binding to one of the C libraries above. Server platforms are also out of scope: this page compares stacks you build an application with, not ones you deploy and configure.