Skip to main content

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

TierMeansWho may hold it
generatedComputed from this repository at render timethis repository only
measuredA reproduce command re-derives it from the subject at the version namedany subject whose source can be read
documentedThe subject's own documentation, release notes or advisories state itany subject
assessedReviewer judgment from indirect evidenceany 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

StackLanguageLicenceSource
sipxRustMIT OR Apache-2.0repository
PJSIP (pjproject)CGPL-2.0-or-later, or a commercial licencerepository
reSIProcateC++Vovida Software License 1.0 (per file)repository
baresip (with libre)CBSD-3-Clauserepository
sipgoGoBSD-2-Clauserepository
SIPSorceryC#BSD-3-Clauserepository
rsipstackRustMITrepository
JAIN-SIP (NIST reference implementation)JavaNIST conditions of use (public domain), with Apache-licensed dependenciesrepository

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.

StackFindingConfidenceEvidenceReproduce
sipxRust, 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.2the 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.17the SIP stack sources at the pinned tag · the advisory index, most entries buffer-handling defectsC=$(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
reSIProcateC++, 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.0the SIP stack sources at the pinned tagC=$(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.0the application sources at the pinned tag · the companion library holding the SIP, RTP and TLS machineryC=$(mktemp -d); git clone -q --depth 1 --branch v4.10.0 https://github.com/baresip/baresip "$C"; find "$C" -name '*.c' | wc -l
sipgoGo, 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.3the 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\
SIPSorceryC# 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.13the core library project, with no AllowUnsafeBlocks · the optional media packages that do enable itC=$(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
rsipstackRust, 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.21the stack sources at the pinned tag · the manifest, which declares no lint tableC=$(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-166the sources at the pinned tagC=$(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.

StackFindingConfidenceEvidenceReproduce
sipxUDP, 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.2the 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.17the transport-type enum, including the values with no module · the four transport modules that existC=$(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
reSIProcateThe 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.0the TransportType enumC=$(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.0registration switching on the transport · the default transportC=$(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
sipgoUDP, 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.3transport_udp, _tcp, _tls, _ws, _wss and the connection poolC=$(mktemp -d); git clone -q --depth 1 --branch v1.4.3 https://github.com/emiago/sipgo "$C"; ls "$C"/sip | grep transport_
SIPSorceryudp, 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.13the SIPProtocolsEnum declarationC=$(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"
rsipstackUDP, 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.21udp.rs, tcp.rs, tls.rs, websocket.rs · the feature table gating TLS and WebSocketC=$(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-166UDP, TCP and TLS message processors · the SCTP message processorC=$(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.

StackFindingConfidenceEvidenceReproduce
sipxTerminates 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.2the 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.17one source file per codec · transport_srtp, transport_srtp_dtls, transport_srtp_sdes, transport_ice, jbufC=$(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'
reSIProcateMedia 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.0the media flow layer, including Srtp2Helper · GStreamer and Kurento integrations, and the SDP containerC=$(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.0one module per codec and per media-security mechanismC=$(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'
sipgoSignalling 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.3the repository root, with no media directory · the README, which points media users at a companion libraryC=$(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
SIPSorceryTerminates 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.13RTCPeerConnection, RtpIceChannel, DtlsSrtpTransport and the SRTP context · the managed G.711, G.722 and G.729 codecsC=$(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|.*/||'
rsipstackSignalling 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.21the 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-166the specification's API surface, which is signalling onlyC=$(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.

StackFindingConfidenceEvidenceReproduce
sipxNo 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.2the 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.17the published advisory index · cifuzz, codeql-analysis, coverity-scan-daily, sanitiser builds · the reporting routegh 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
reSIProcateNo 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.0the advisory index, empty at the time of reading · linux-ci and windows-ci, and nothing elsegh 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.0sanitizers, valgrind, clang-analyze, coverage, lint · the advisory indexgh 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
sipgoThree 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.3the published advisories · the fuzzing corpus · the reporting routegh 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
SIPSorceryTwo 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.13the published advisories · the CI workflows, all build and testgh 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*'
rsipstackNo 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.21the advisory index, empty at the time of reading · a single CI workflowgh 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-166the advisory index, empty at the time of reading · the tree at the pinned tag, with no CI configurationgh 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.

StackFindingConfidenceEvidenceReproduce
sipx37 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.2the 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.17the message tests, including torture cases · the CI matrixC=$(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
reSIProcate217 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.0the 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.0the test directory · ten workflows, several of them analysis rather than buildC=$(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
sipgo83 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.3the table-driven torture suite · the single test workflowC=$(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
SIPSorcery1172 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.13the 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]\
rsipstack242 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.21tests 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-166the torture suite · the TCK message-flow harnessC=$(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.

StackFindingConfidenceEvidenceReproduce
sipxThis 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.2predicate 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.17the release history from 2005 onward · GPL-2.0; the commercial option is offered separately by the vendorC=$(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)
reSIProcateFirst 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.0the release history from 2004 onward · the proxy and registrar shipped alongside the stackC=$(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.0the release history · BSD-3-Clause, held by the Baresip FoundationC=$(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)
sipgoFirst 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.3the release history from 2022 onward · BSD-2-ClauseC=$(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)
SIPSorceryFirst 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.13the release history from 2019 onward · BSD-3-ClauseC=$(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)
rsipstackThe 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.21the release history: one tag · the published crateC=$(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-166the release history, ending in 2011 · the artefact still published to Maven CentralC=$(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.

CategoryCapabilitySubject versionConfidenceOwnershipStatusEvidenceStory or rationale
coreParse, build and serialize SIP requests and responsesv0.5.21measuredsipximplementedpublic SIP message, parser and serializer modules
coreTyped headers with editable unknown-header preservationv0.5.21measuredsipximplementedtyped and untyped header APIs
transactionsClient and server INVITE and non-INVITE transactionsv0.5.21measuredsipximplementedtransaction role and state drivers
lifecycleCancelable transaction timers and endpoint shutdownv0.5.21measuredsipximplementedtimer queue API
transportsUDP signalling transportv0.5.21measuredsipximplementedUDP connection and listener
transportsTCP signalling transport and connection reusev0.5.21measuredsipximplementedTCP connection implementation
transportsTLS signalling transportv0.5.21measuredsipximplementedTLS listener and client transport
transportsWebSocket signalling transportv0.5.21measuredsipximplementedWebSocket transport implementation
transportsSIP target resolution with an injectable lookup sourcev0.5.21measuredsipximplementedSIP resolver and lookup-source trait
operationsAtomic live reload of the TLS server identityv0.5.21measuredsipximplementedreloadable certificate resolver
endpointEndpoint message inspection and bounded policy hooksv0.5.21measuredsipximplementedMessageInspector and TransportEventInspector traits
endpointInjectable endpoint target selectionv0.5.21measuredsipximplementedTargetLocator and domain resolver hooks
operationsEndpoint counters and outstanding-transaction countv0.5.21measuredsipximplementedEndpointStats and running transaction access
operationsInspect stable identifiers for running transactionsv0.5.21measuredsipximplementedrunning transaction key access
authenticationDigest challenge response and verificationv0.5.21measuredsipximplementeddigest credential, calculation and verification APIs
dialogsAuthenticated registration with granted expiryv0.5.21measuredsipximplementedregistration client API
dialogsReport the public address learned during registrationv0.5.21measuredsipximplementedreceived and rport address extraction
dialogsClient and server INVITE dialogsv0.5.21measuredsipximplementedinvitation, client-dialog and server-dialog modules
dialogsReliable provisional responses and PRACKv0.5.21documentedsipximplementedpublic reliable-provisional capability
methodsCANCEL, BYE and re-INVITE dialog operationsv0.5.21measuredsipximplementedcancel, hangup and reinvite methods
methodsUPDATE requests on a dialogv0.5.21measuredsipximplementedUPDATE operation
methodsOPTIONS requests and endpoint responsesv0.5.21measuredsipximplementedOPTIONS operation
methodsREFER and transfer NOTIFY operationsv0.5.21measuredsipximplementedREFER and NOTIFY operations
methodsApplication-owned INFO in both directionsv0.5.21measuredsipximplementedINFO dialog operation
methodsApplication-owned MESSAGE in both directionsv0.5.21measuredsipximplementedgeneric dialog MESSAGE operation
methodsAuthenticated generic in-dialog request operationv0.5.21measuredsipximplementedgeneric dialog request method
dialogsCreate, refresh and terminate outbound subscriptionsv0.5.21measuredsipximplementedclient subscription dialog
dialogsAccept subscriptions and originate NOTIFYv0.5.21measuredsipximplementedserver subscription dialog
dialogsCreate, refresh and remove publications with entity tagsv0.5.21measuredsipximplementedclient publication dialog
dialogsAccept inbound publications through the endpointv0.5.21measuredsipximplementedserver publication dialog
lifecycleSerializable dialog snapshot for external restoration policyv0.5.21measuredsipximplementedDialogSnapshot public type
examplesStateful proxy with request and response forwardingv0.5.21measuredsipx-clstrtrackedstateful proxy exampletracking story
examplesRegistrar and in-memory location service examplev0.5.21measuredsipx-clstrtrackedREGISTER handling and location tabletracking story
examplesRegistering caller and answerer examplev0.5.21measuredsipximplementeduser-agent client example
mediaRTP file playbackv0.5.21measuredsipximplementedRTP playback and echo helper
examplesRunnable RTP echo endpoint examplev0.5.21measuredsipximplementedRTP echo helper
endpointDynamically restrict accepted transport sourcesv0.5.21measuredsipximplementedTransportWhitelist and its update API
operationsMachine-driven call-load responderv0.5.21measuredsipximplementedbenchmark user agent server mode
endpointWebAssembly endpoint targetv0.5.21documentednot-shippedabsentWASM remains unchecked in the public TODO
mediaBundled WebRTC media enginev0.5.21documentednot-applicableexcludedWebRTC is described as an integration use caseThe 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)OutcomeMedian achieved (dialogs/s)Spread [min, max]Setup p99 (ms, median)
32supported (5/5)32.0[32.0, 32.0]1
64supported (5/5)64.0[64.0, 64.0]3
128supported (5/5)128.0[128.0, 128.0]3
256supported (5/5)256.0[256.0, 256.0]2
512supported (5/5)512.0[512.0, 512.0]1
1024supported (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)OutcomeMedian achieved (dialogs/s)Spread [min, max]Setup p99 (ms, median)
32supported (5/5)32.0[32.0, 32.0]1
64supported (5/5)64.0[64.0, 64.0]1
128supported (5/5)128.0[128.0, 128.0]3
256supported (5/5)256.0[256.0, 256.0]1
512supported (5/5)512.0[512.0, 512.0]1
1024supported (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.