RFC compliance
This page is generated from the same checked registry as the repository report. Each status is a bounded statement: the roles and notes say what is reachable today and what remains incomplete.
What sipx implements, what it only parses, and what it has not started — measured
rather than asserted. scripts/rfc-report.py --check runs in CI and fails the build if
an entry names a header the parser does not know, or cites a file that does not exist.
It cannot check that behaviour is correct; the tests do that, and each entry points at them. What it can do is stop this table drifting away from the code, which is the way a compliance document usually becomes untrue.
Where it stands
82 RFCs tracked.
| Meaning | Count | |
|---|---|---|
| ✅ implemented | Behaviour present and tested for the roles listed | 36 |
| 🟡 partial | Some of the normative behaviour; the note says which part is missing | 37 |
| 🔤 syntax only | The parser represents it; nothing acts on it | 6 |
| ⬜ not started | Tracked as a target, not started | 2 |
| — superseded | Obsoleted by a later RFC that is tracked instead | 1 |
The list is bounded on purpose: it is what sipx already touches plus what it has decided to aim at. It is not every SIP-related RFC and it never will be — some update or obsolete others, some define alternatives to each other, and some belong to trust domains sipx does not operate in. The order to add them in is in the RFC roadmap.
Roles. sipx is a user agent. Where an RFC defines proxy or registrar behaviour, that
behaviour is not implemented even when the UA half is — the Roles column says which.
Wire — can the bytes be represented at all?
| RFC | Title | Status | Roles | Spec | Notes |
|---|---|---|---|---|---|
| 2606 | Reserved Top Level DNS Names | ✅ implemented | — | — | .invalid for the sent-by a WebSocket client invents, which RFC 7118 §5.2 requires never to resolve. |
| 4475 | SIP Torture Test Messages | ✅ implemented | uac, uas | — | The whole corpus, recovered bit-exactly from the RFC's own archive and classified by which layer must object to each message. |
| 5118 | SIP Torture Test Messages for IPv6 | ✅ implemented | uac, uas | — | All twelve Appendix A messages, recovered bit-exactly from the RFC's own archive and classified by which layer must object, asserted at the SIP layer and — for the SDP bodies of §4.6, §4.8 and §4.9 — at the SDP layer too. §4.10's three-colon reference is tolerated as the narrow carve-out in sip-parser.md §4.8: ::: reads as :: only immediately before an embedded IPv4 address, the one position RFC 3261's inherited ABNF derives it. rfc5118::DEVIATIONS is empty. |
| 3261 | SIP: Session Initiation Protocol | 🟡 partial | uac, uas | — | Parser, all four transaction machines, dialogs, UAC and UAS. No proxy role: sipx does not fork, and Record-Route is read rather than written. Existing SIP/SIPS URI user parts can be replaced through the structured grammar with typed, atomic validation; on parsed URIs only the parser-retained user span changes and every other wire byte remains exact. Request-URI and nested URIs in every typed address field can likewise be replaced through parser-owned spans, and flattened address-list values can be removed across repeated rows without byte search or rebuilding surviving syntax. The call dispatcher answers rather than drops what belongs to no live call: 481 for an orphaned in-dialog request (§12.2.2), 405 with Allow for an unsupported method (§8.2.1), 482 for a merged INVITE (§8.2.2.2), and 200 with the capability list for an in-dialog OPTIONS (§11.2). A registering UA retains the top response Via's received/rport pair as a typed, non-authoritative observation; missing or invalid observation data does not alter the successful binding. A confirmed quiescent dialog's §§12.1–12.2 state can be captured in a bounded versioned snapshot and attached to explicit fresh drivers: identifier orientation, route order, refreshed target and independent sequence numbers survive, while transactions, sockets, tasks, clocks, credentials and media keys do not. The bounded signalling responder sends final INVITE responses, validates ACK and remote sequence ordering, accepts or originates BYE, retransmits its final response through Timer H, and joins owned timer work before release. An outbound request above §18.1.1's derived unreliable-transport limit switches from UDP to TCP at the same peer before transaction creation, reports the switch in endpoint counters, and returns a typed cause when the selected TCP path is unavailable; oversized responses retain their §18.2.2 transport. |
| 3986 | Uniform Resource Identifier: Generic Syntax | 🟡 partial | — | — | The SIP and SIPS subset. Percent-escape shape is validated for every scheme and the SIP reserved sets are handled; the remainder of the general grammar is not. |
| 3966 | The tel URI for Telephone Numbers | 🔤 syntax only | — | uri-rewriting | Represented and round-tripped; parsing validates the telephone-subscriber, the exact subscriber/parameter-tail view is public, and a validated subscriber replacement changes only its parser-owned span. Parameters are retained but not interpreted or validated, and nothing normalises or routes on a tel URI. |
| 8217 | Clarifications for When to Use the name-addr Production in SIP Messages | 🔤 syntax only | — | sip-asserted-identity | The shared address parser and typed asserted-identity values require name-addr when a URI contains a comma, semicolon or question mark. A bare semicolon tail is interpreted as header parameters rather than URI content and is rejected for RFC 3325 fields, which define no such tail. Serialization always emits the unambiguous name-addr form. |
Transport — how a message travels, and how the far end is found
| RFC | Title | Status | Roles | Spec | Notes |
|---|---|---|---|---|---|
| 2782 | A DNS RR for specifying the location of services (SRV) | ✅ implemented | — | — | Priority then the weighted shuffle, asserted against a seeded distribution rather than eyeballed. |
| 3263 | SIP: Locating SIP Servers | ✅ implemented | uac | — | NAPTR, SRV and A/AAAA, with the candidate list tried in order. The verification name survives resolution. |
| 3327 | SIP Extension Header Field for Registering Non-Adjacent Contacts (Path) | ✅ implemented | — | — | Parsed with route-header list semantics, offered as Supported: path on every REGISTER, and the returned path vector kept with the binding. Not turned into a route set for outbound requests: §5.1 says a UA ignores it, and Service-Route (RFC 3608) is the header for that. |
| 3581 | An Extension to SIP for Symmetric Response Routing | ✅ implemented | uac, uas | — | rport is observed and echoed, with quote-aware parameter surgery so the value replaces the flag rather than following it. A registering UA also reports one unambiguous received/rport pair from the final successful response as informational state, without turning it into Contact, routing or media policy. |
| 3608 | SIP Extension Header Field for Service Route Discovery During Registration | ✅ implemented | — | — | Parsed with route-header list semantics and order preserved (§6.1), stored against the registration and replaced on every 2xx — so a response with no Service-Route clears it, which is what §6.1 requires. Exercised as a pre-loaded Route set on outgoing initial requests via DialOptions::with_service_route, not attached behind the caller's back. A hop missing the ;lr §5 requires is reported rather than dropped. |
| 6455 | The WebSocket Protocol | ✅ implemented | — | — | Through tokio-tungstenite. sipx owns the subprotocol negotiation and the framing rules that RFC 7118 layers on top. |
| 7118 | The WebSocket Protocol as a Transport for SIP | ✅ implemented | uac, uas | — | Subprotocol negotiation, one message per frame, and the invented sent-by. A target names the resource the handshake asks for, since RFC 7118 fixes neither that nor the port. The browser-audio proof repeats both roles over authenticated WSS against a native browser, including dialing back over the browser's exact authenticated inbound connection. |
| 9000 | QUIC: A UDP-Based Multiplexed and Secure Transport | ✅ implemented | — | — | QUIC v1 through quinn: authenticated connections, bounded bidirectional streams, migration and transport PING. The SIP mapping itself is experimental and specified by sipx because no RFC defines SIP over QUIC; bare-peer vectors check its stream framing rather than trusting two sipx endpoints to agree. |
| 5389 | Session Traversal Utilities for NAT (STUN) | 🟡 partial | uac | — | A Binding client, as much as RFC 5626 §4.4.2's keep-alive needs: a request with no attributes, and a response read for XOR-MAPPED-ADDRESS. Decoding is verified against the vectors RFC 5769 publishes, including the padded attribute that trips a decoder ignoring §15's alignment rule. No MESSAGE-INTEGRITY, no FINGERPRINT, no credentials, and no server role — ICE (RFC 8445) needs the full protocol, not more attributes on this. |
| 5626 | Managing Client-Initiated Connections in SIP (Outbound) | 🟡 partial | uac | — | Implemented and tested inside the crate but still reached by nothing above it: ob on a dialog-forming Contact (§4.3, UserAgent::dialog_contact); §4.4's keep-alives — CRLF for connection-oriented flows, STUN Binding for UDP, including §4.4.2's rule that a changed reflexive address is a failed flow (UserAgent::keepalive_after); and one registration per outbound proxy with each flow failing independently under §4.5's backoff (Flows, Attempt). Not implemented at all: the registrar side (flow tokens in Path, §5), which is what makes the mechanism useful to the network rather than to the client. |
| 5923 | Connection Reuse in SIP | 🟡 partial | — | — | A response returns over the connection its request arrived on, and the pool distinguishes inbound from outbound. The alias parameter is not implemented. |
| 7339 | SIP Overload Control | 🟡 partial | uac, uas | sip-transport | The UAC behavior is explicit through Config::overload.advertise, off by default because RFC 7339 §2 scopes the requirements to entities supporting the extension. Once enabled it advertises loss,rate on every request, accepts feedback only from responses matched to its live client transactions, keeps sequenced per-peer validity state within a configured finite LRU bound, applies seeded loss control with a local two-category policy hook, and rejects locally with a counter; a finite count/concurrency/cleanup load witness drives that endpoint path. The UAS selects an offered algorithm on every response, including transaction-generated 100 Trying, reports explicit control-off state normally, and retains active feedback on queue-full 503 and application responses for the detector's stated lifetime. Missing: recognition of oc-seq wraparound and a load estimator beyond the bounded queue's concrete overload signal. |
| 7415 | SIP Rate Control | 🟡 partial | uac, uas | sip-transport | With Config::overload.advertise enabled, the UAC offers and obeys rate with the RFC leaky bucket, driver-supplied time, and configurable TAU1/TAU2 thresholds that preserve protected traffic after ordinary traffic is blocked; the UAS can report a configured requests-per-second limit from its queue-overload path. Missing: autonomous periodic server rate estimation/allocation and the optional randomized rate algorithm. |
| 8599 | Push Notification with SIP | 🟡 partial | uac | — | The UA half only. pn-provider, pn-param and pn-prid go on the REGISTER's Contact as the URI parameters §8.7 registers them as; the sip.pns, sip.pnsreg and sip.pnspurr feature-capability indicators (§8.2) are read out of Feature-Caps (RFC 6809 §4) so a client can tell whether the registrar supports the push service it named; 555 (§8.1) is surfaced as its own outcome rather than a generic failure; and a push leads to §4.1.3's binding-refresh REGISTER before the pending request is expected — UserAgent::woken is that ordering as a type. The push service itself is a trait sipx ships no implementation of, and pn-purr is read and carried but not matched against stored bindings — matching is for the party that stores them, which is §5.6's proxy role and not played here. Not the registrar or proxy half: minting the PURR (§4.2), the push-bucket hold of §5.6, and sending the push itself are out of scope. |
Core — transactions, dialogs, negotiation
| RFC | Title | Status | Roles | Spec | Notes |
|---|---|---|---|---|---|
| 3262 | Reliability of Provisional Responses in SIP (100rel) | ✅ implemented | — | — | Offered in Supported, honoured in Require. A reliable provisional carries RSeq and is retransmitted from T1 with no T2 cap until PRACK or 64*T1; PRACK carries RAck and is answered 2xx, or 481 when it matches nothing. A peer that requires 100rel while it is off gets 420 with Unsupported. An offer arriving in the provisional is answered in the PRACK, and an offer arriving in the INVITE may be answered in the provisional (ring_early), which is what makes an early-dialog renegotiation by UPDATE legal. |
| 3264 | An Offer/Answer Model with SDP | ✅ implemented | uac, uas | — | A pure function from offer and capabilities to answer. docs/specs/sdp-format-identity.md §4.4 states the property. |
| 3311 | The SIP UPDATE Method | ✅ implemented | uac, uas | — | Sent and received by both roles, in an early dialog and a confirmed one — §5.1's 'either caller or callee'. The answering side sends and receives through ring_early and Ringing; the calling side through dial_early and Dialing, which hands back the early dialog instead of consuming it inside dial. Both run one implementation of §5.1 and §5.2 (sipx-call's update::offer and update::receive), so the two roles cannot drift apart. Allow lists UPDATE on the INVITE, its provisional and 2xx responses, and on in-dialog requests (§4). §5.2's three refusals are three answers: 500 with a random Retry-After for a second UPDATE before the first is answered, 491 for glare, 500 with Retry-After when an answer is owed; an unusable description is 488 and the dialog survives. An early dialog is renegotiable only after the INVITE offer is answered in a reliable provisional (RFC 3262 §5) — ring_early does that as UAS, and as UAC Dialing adopts an answer only out of a reliable provisional and reports it as has_early_session. |
| 4028 | Session Timers in SIP | ✅ implemented | — | — | Negotiated on the INVITE and its 2xx, Table 2 election, 422 with Min-SE below the floor, and a local BYE when no refresh arrives. The refresh is an UPDATE with no body when the peer's Allow lists the method and a re-INVITE otherwise (§7.4). A durable confirmed-dialog snapshot retains only the remaining duration; restore rebases a positive remainder on explicit fresh time, surfaces a due refresh or expiry when it is zero, and rejects contradictory or overflowing values rather than silently renewing the interval. |
| 6026 | Correct Transaction Handling for 2xx Responses to INVITE | ✅ implemented | uac, uas | — | The Accepted state. Without this a 2xx retransmission is absorbed by a transaction that should already have ended. |
| 2543 | SIP: Session Initiation Protocol (obsoleted by 3261) | 🟡 partial | — | — | Not implemented as a protocol. The transaction key falls back to 2543-style matching when a branch lacks the magic cookie. Server-side that is for requests that still arrive from such senders; client-side it is only reachable by an application that supplies its own cookieless Via, since the transport brands ours with z9hG4bK. The two fall back differently, as RFC 3261 §17.2.3 and §17.1.3 do: the client key drops the Request-URI and the To tag, which a response cannot reproduce, so a legacy client transaction matches its own responses instead of hanging until Timer F. |
| 3326 | The Reason Header Field for SIP | 🟡 partial | uac, uas | sip-history-info | The UA behavior is implemented for both call roles: a typed comma-separated header validates SIP and Q.850 causes, locally generated CANCEL and BYE requests carry causes matching the action, and the call API accepts an explicit reason so a coupled leg can report the winning SIP status from §3.1. The initial INVITE never carries Reason. Received values remain available through typed headers and RFC 7044 retargeting embeds them in the prior target URI. Partial because sipx has no proxy/fork controller and does not generate Reason in responses whose defining RFC separately permits it. |
| 5876 | Updates to Asserted Identity in the Session Initiation Protocol (SIP) | 🟡 partial | — | sip-asserted-identity | Section 4.5 general receive handling is implemented across comma-joined and repeated rows: valid values survive in wire order, while unexpected schemes, duplicates and SIP/SIPS combinations are reported with stable indices and reasons so a proxy can remove every value it MUST NOT forward. Partial because RFC 5876's UAC, proxy, registrar and UAS method-specific behavior is application behavior outside this syntax surface. |
| 6665 | SIP-Specific Event Notification | 🟡 partial | uac, uas | event-notifier | Both generic roles are socket-reachable through the call dispatcher. The notifier uses one bounded store keyed on dialog/package (§4.4.1), sends the mandatory initial NOTIFY, negotiates expiry and owns observable termination. The subscriber issues authenticated SUBSCRIBE, accepts the initial-NOTIFY race through an explicit origin policy, rejects forks, tracks target/route/CSeq and refreshes or unsubscribes under finite timer generations. Package bodies are injected bounded consumers; registration discovery ships a concrete consumer, while dialog and presence subscription consumers remain later package policy. REFER's implicit subscription uses the same typed Subscription-State vocabulary. |
| 3323 | A Privacy Mechanism for the Session Initiation Protocol (SIP) | 🔤 syntax only | — | sip-privacy | Privacy is a typed comma-delimited list under verified RFC 3323 erratum 5184, with checked construction and deterministic serialization. Comma-joined and repeated rows form one validated list. The seven currently registered tokens are explicit variants, later extension tokens retain their spelling, and malformed tokens, message-wide duplicates, none combined with another value, or misplaced critical return typed errors. This syntax claim does not provide RFC 3323's privacy-service behavior; the narrower RFC 7044 history policy is tracked in that RFC's own row. |
| 3325 | Private Extensions to the Session Initiation Protocol (SIP) for Asserted Identity within Trusted Networks | 🔤 syntax only | — | sip-asserted-identity | P-Asserted-Identity and P-Preferred-Identity are recognized typed address lists. Strict complete-list construction enforces the one-or-two-value rule and SIP/SIPS-plus-TEL pairing; checked element construction prevents unsupported schemes, parameter tails and unsafe display names from bypassing those invariants. Receive handling follows RFC 5876 rather than rejecting an entire field for an unexpected value. RFC 3325's id privacy token is an explicit typed Privacy value. Trust-domain assertion, authorization, forwarding and privacy-service behavior remain application policy. |
| 3841 | Caller Preferences for SIP | 🔤 syntax only | — | — | The headers parse and round-trip. Nothing acts on a preference. |
Security — transport security, authentication, identity
| RFC | Title | Status | Roles | Spec | Notes |
|---|---|---|---|---|---|
| 2617 | HTTP Authentication: Basic and Digest Access Authentication | ✅ implemented | uac | — | Checked against the worked example the RFC publishes for itself, rather than against sipx's own idea of the answer. Reachable from both UAC paths: sipx register --password answers a registrar's 401/407, and sipx dial --password answers a challenged INVITE with the matching Authorization or Proxy-Authorization header. Both prefer SIPX_PASSWORD because argv is world-readable. |
| 5922 | Domain Certificates in SIP | ✅ implemented | uac, uas | — | The identity checked is the host from the URI, never the name resolution produced. There is no way to disable verification. DPH-1 connects to a trusted TLS peer; DPH-2 requires a WSS name mismatch to surface as a typed TLS failure without a cleartext retry. |
| 6125 | Representation and Verification of Service Identity in TLS | ✅ implemented | — | — | Through rustls. §6.4.4 in particular: a certificate with a non-matching SAN is refused rather than falling back to the CN. DPH-2 proves the same refusal through the shipped WSS command surface. |
| 7616 | HTTP Digest Access Authentication | ✅ implemented | uac | — | MD5, SHA-256 and their session variants, checked against the §3.9.1 worked example rather than against sipx's own arithmetic. See RFC 8760 for SHA-512-256. One sans-I/O implementation is shared by registration and outbound calls; both have CLI credential selection and bounded challenge retries. |
| 8224 | Authenticated Identity Management in SIP (STIR) | ✅ implemented | uac, uas | — | UAC calls explicitly select an authentication service through DialOptions; UAS dispatchers explicitly select required or optional verification before an invitation can reach the application. The services remain sans-I/O: callers supply authority policy, time, credential acquisition, and credential trust. Live tests cover the ordinary silent default, independent verification of an outbound wire signature, and distinct 428 and 438 inbound refusals before answer. |
| 8225 | PASSporT: Personal Assertion Token | ✅ implemented | uac, uas | — | UAC and UAS calls reach full-form baseline PASSporT with deterministic typ, alg, x5u, dest, iat, and orig serialization and ES256 signing and verification. The outbound live-call proof parses the wire token independently and verifies its signature without sipx's serializer or verifier. Unsupported ppt values are refused; later-profile attestation levels and origid are deliberately not claimed. |
| 8446 | The Transport Layer Security (TLS) Protocol Version 1.3 | ✅ implemented | — | — | Through rustls, preferred over 1.2. |
| 8760 | The SIP Digest Access Authentication Scheme | ✅ implemented | uac | — | SHA-512-256 and SHA-512-256-sess, and multiple challenges resolved by strength rather than by the server's order — §2.4's unless a local policy dictates otherwise, taken because §3 names the downgrade attack that ordering enables. topmost_supported is the literal rule for callers that want it. The shared selector is reached by both registration and outbound-call retries. |
| 8996 | Deprecating TLS 1.0 and TLS 1.1 | ✅ implemented | — | — | 1.2 is the floor and is not configurable downward. A negative obligation, so the evidence is an attempt: tls_versions.rs writes a 1.0 and a 1.1 ClientHello at a sipx TLS listener and requires a fatal protocol_version alert, with the same hello at 1.2 accepted as the control (docs/specs/sip-tls.md §6, vector L9). The property belongs to rustls, not to sipx: sipx implements no TLS and names no version, so 1.0 and 1.1 are excluded because the library offers nothing below 1.2 to select — a change of TLS backend would move this claim without touching any sipx code, which is why the same test asserts the library's version set. |
| 9001 | Using TLS to Secure QUIC | ✅ implemented | — | — | QUIC reuses the one rustls certificate policy used by TLS and WSS, requires ALPN sip/2, and configures both ends to refuse 0-RTT request data. Tests distinguish wrong host, unknown issuer and wrong/no ALPN before any SIP is delivered. |
| 4474 | Enhancements for Authenticated Identity Management in SIP | 🔤 syntax only | — | — | The obsolete Identity-Info name remains parse-only because it still arrives on the wire. RFC 8224's revised Identity field has a typed parser and is tracked separately below; this row does not claim the RFC 4474 signing mechanism. |
Media — describing it, carrying it, encoding it
| RFC | Title | Status | Roles | Spec | Notes |
|---|---|---|---|---|---|
| 3550 | RTP: A Transport Protocol for Real-Time Applications | ✅ implemented | — | — | RTP and RTCP, including sender and receiver reports and the §A.8 interarrival jitter recurrence — which is not a variance, and is usually implemented as one. Reached by every call: call.rs asserts the audio one endpoint played arrives sample-for-sample at the other, and mute.rs asserts the sent and received packet counts agree with no loss. The report codec itself is tested in sipx-rtp. |
| 4733 | RTP Payload for DTMF Digits, Telephony Tones, and Telephony Signals | ✅ implemented | — | — | Events 0-15, with the payload type read from the negotiation rather than assumed. End retransmissions share the event's timestamp, which is the part that is usually wrong. Reachable from a call: Call::send_digit, send_digits and recv_digit are public, and call.rs's tests carry 1234# across a live call and assert the digit leaves on the payload type the answer named rather than on 101. |
| 6716 | Definition of the Opus Audio Codec | ✅ implemented | — | — | Encode and decode, and both a library call and the diagnostic phone can select it. Codecs is the library choice: DialOptions::with_codecs on the offering side, answer_with, answer_ringing_with, answer_replacing_with, Invitation::answer_with and ring_early_with on the answering side, and Call carries the set so a re-INVITE does not narrow to G.711 mid-call. The diagnostic phone maps an ordered --codec opus selection directly to the same policy, refuses it before network I/O when the codec feature is absent, and reports the codec read from the established call. sipx-call/tests/opus.rs places a call with Opus selected and asserts 111 in the offer with its a=rtpmap, both ends settling on Codec::Opus, and 300 ms of a 48 kHz tone arriving sample-for-sample — a count only an Opus encode and decode can produce, since a G.711 read of an Opus packet yields a sample per byte. interop_opus.rs repeats the live audio proof against an independently implemented codec in both offer/answer roles: each exact Opus-only call sends a distinct 48 kHz signal through the peer's decoder and encoder, then requires non-silent, signal-correlated output from sipx's decoder. A G.711 fallback cannot negotiate and cannot satisfy the dynamic-payload assertion. Behind sipx-call's opus feature, off by default, because it links a C library through sipx-audio; the default set stays the mandatory-to-implement G.711 pair, so the codec cannot become the default by accident. Negotiation refuses to settle outside the selected set, so an Opus offer reaching a G.711 call is answered G.711 rather than silently upgraded, never quietly better than what was asked for. |
| 7587 | RTP Payload Format for the Opus Speech and Audio Codec | ✅ implemented | — | — | The payload format is reachable from a library call and the diagnostic phone. Implemented and tested: the 48000 RTP clock whatever the sample rate (§7), the mandatory opus/48000/2 rtpmap regardless of the channel count actually sent, mono audio which is §7.1's stereo=0 default, and matching by encoding name rather than by payload number — a format is read from its a=rtpmap, so 111 is Opus only because the description said so, and the number the far end assigned travels with the codec on Config::payload_type instead of being reassumed. sipx-call/tests/opus.rs carries live Opus packets across a call in both Codecs::Opus directions of the negotiation; interop_opus.rs makes the same two roles agree with an independently implemented peer, asserts a dynamic negotiated payload number and the 48000 stream clock, and requires recognisable decoded audio rather than a codec name. The native-browser proof repeats both roles, reads the selected 48 kHz Opus codec from browser statistics and sipx independently, and requires non-silent decoded audio in both directions. The diagnostic phone maps --codec opus to the same policy and reports the established codec. No a=fmtp for Opus, in either direction: maxaveragebitrate, useinbandfec, usedtx, maxplaybackrate, cbr and the stereo pair are §7.1's optional parameters, and sipx neither offers them nor reads one a peer offered — so a peer that asks for a bitrate ceiling or in-band FEC is not refused, it is simply not accommodated. Everything the format requires is here; everything it lets a receiver ask for is not. |
| 8866 | SDP: Session Description Protocol | ✅ implemented | — | — | A real AST with round-tripping, rather than string manipulation over the body. Every call goes through it: call.rs builds the offer and parses and answers the peer's, on the INVITE, on a re-INVITE and on an UPDATE. §6.6's a=rtpmap is read in exactly one place (rtpmap.rs), which decides format identity once — the encoding name case-insensitively, the clock rate and the channel count by value, with an omitted channel count meaning one. A clock rate that is empty, is not a decimal digit string, or overflows a u32 is a typed error and a non-match, never a panic. docs/specs/sdp-format-identity.md is normative for the rule and lists the vectors. |
| 3551 | RTP Profile for Audio and Video Conferences | 🟡 partial | — | — | The static payload types sipx carries are PCMU 0, PCMA 8, and mono 44.1 kHz L16 11. Mono 8 kHz L16 is offered with an explicit dynamic mapping; stereo type 10 is refused because the public PCM boundary is mono. L16 encode/decode is signed 16-bit network byte order, selected through the call policy, and the negotiated clock is retained independently of the codec. The rest of the profile is not implemented. |
| 3711 | The Secure Real-time Transport Protocol (SRTP) | 🟡 partial | uac, uas | srtp | The default transform, AES_CM_128_HMAC_SHA1_80, for RTP and RTCP, with separate SRTP and SRTCP replay windows and rollover inference. Both roles reach it on a live call, keyed by RFC 4568: in secure_media.rs each end asserts the call negotiated SRTP, and audio crosses the encrypted stream from the calling end to the answering one — one direction, which is the encrypt path on one side and the decrypt path on the other. interop_srtp.rs covers both directions against a peer that keyed the session independently, but it is #[ignore]d and runs only in the interop matrix, not in the local gate. Only one transform: no AES-192/256, no f8, no null cipher, and no rekeying (the key derivation rate is fixed at zero). |
| 3960 | Early Media and Ringing Tone Generation in SIP | 🟡 partial | uac, uas | call-early-media | The §3 gateway model is implemented in both roles: a reliable provisional completes the INVITE's offer/answer, starts bidirectional media on the early dialog, signals the application to replace its local ringing tone, and hands that same running session to the confirmed call. A refusal or abandoned branch drops its session. Partial because §3.1's selection among simultaneous forked early streams remains application policy and the §4 application-server model / RFC 3959 early-session disposition is not implemented. |
| 4145 | TCP-Based Media Transport in SDP | 🟡 partial | — | — | a=setup is emitted and acted on by initial DTLS-SRTP calls at media or session level: the offer says actpass, the answer prefers active or selects passive when that is the role available, and the offerer takes the complementary role. Missing, unresolved and locally unsupported answer roles are typed refusals before the handshake; a DTLS holdconn offer is refused before answer resources are acquired. The selected role decides which side sends ClientHello and which exporter half protects outbound media. The a=connection attribute and TCP media transport itself are not implemented: sipx carries media over UDP. |
| 4568 | SDP Security Descriptions for Media Streams (SDES) | 🟡 partial | uac, uas | srtp | So both of §5.1's MUSTs now hold on a call and not only in the crates below it. Partial, and for what the RFC defines beyond that exchange: no MKI, no key lifetimes, no session parameters, and no RTP/SAVPF. See docs/specs/srtp.md §12.3. |
| 5761 | Multiplexing RTP Data and Control Packets on a Single Port | 🟡 partial | uac, uas | rtcp-mux-setup | Both initial-call roles offer, answer and act on media-level a=rtcp-mux. Agreement gives one socket owner and one ICE component that classify RTP/SRTP from RTCP/SRTCP before cryptographic processing, and reports leave from and return to the RTP port. An initial ICE offer retains component 2 and an explicit a=rtcp destination for fallback; a mux answer emits component 1 alone. Payload types 64 through 95 are refused, rejected media cannot settle mux, and an in-dialog mode change is a typed refusal before ICE or media state changes. Partial because SIP forking's simultaneous mux/non-mux branches, multicast modes and header-compression interactions are not implemented. |
| 5763 | Framework for Establishing an SRTP Security Context Using DTLS | 🟡 partial | uac, uas | srtp | Both initial-call roles are implemented. Keying::DtlsSrtp is an explicit dial or answer policy, and the diagnostic phone selects it through --media-security dtls-srtp in both roles, reports the negotiated result and carries audio between command processes. It emits UDP/TLS/RTP/SAVP, a=fingerprint and a=setup, and retains the matching identity through the handshake. The offerer sends actpass; the answerer prefers active but may select passive from its typed capabilities, and the offerer refuses a missing, unresolved or locally unsupported answer role before starting the complementary handshake. Media-level setup overrides the session-level default in both signalling and the actual handshake. A DTLS holdconn offer is a typed refusal before binding or gathering. The answer leaves before an active answerer starts, and the offerer begins only after ACK. A build without the optional handshake feature refuses the selection before signalling and never substitutes SDES or cleartext. Partial because reliable early media and rekeying are not offered through this mechanism. |
| 5764 | DTLS Extension to Establish Keys for SRTP | 🟡 partial | uac, uas | srtp | Both call roles are reachable when the application explicitly selects Keying::DtlsSrtp and the off-by-default dtls feature supplies OpenSSL. The diagnostic phone maps --media-security dtls-srtp to that policy for dial and answer, reports the negotiated keying from the running calls, and carries audio between the two command processes; a build without the feature refuses before signalling and never substitutes SDES or cleartext. The initial offer and answer carry UDP/TLS/RTP/SAVP, a fresh certificate fingerprint and the negotiated setup role; the final response and UAC ACK leave before the handshake begins. The verified exporter output reaches Config::srtp on the same bound media socket, and a live call test carries encrypted audio in both roles. The browser-audio profile composes ICE and DTLS-SRTP on one nominated component; the native-browser proof observes DTLS and SRTP in both roles and changes one signalled fingerprint while retaining the real certificate to prove that mismatch installs no usable media path. The separate independent-peer case is interop_srtp.rs. Partial: only AES_CM_128_HMAC_SHA1_80 is offered; reliable early media and rekeying remain refused rather than downgraded. |
| 7874 | WebRTC Audio Codec and Processing Requirements | 🟡 partial | uac, uas | webrtc-audio | The named browser-audio profile requires Opus, PCMU, PCMA, 8 kHz comfort noise and 8 kHz telephone-event in both offer/answer roles, refuses a missing Opus build before I/O, and negotiates Opus on the live CLI path. The native-browser proof runs both SIP roles, reads the negotiated codec independently at both ends, and requires non-silent Opus audio in both directions. Partial because optional audio processing requirements and comfort-noise packet production are not implemented. |
| 8122 | Connection-Oriented Media Transport over TLS in SDP | 🟡 partial | uac, uas | srtp | Both initial-call roles emit and consume SHA-256 certificate fingerprints through explicit DTLS-SRTP policy, and §6.2's certificate comparison completes before exported keys reach a media session. A mismatch yields no keys. Media- and session-level attributes, digest-length validation, uppercase generation and MD5/MD2 refusal remain covered below the call layer. Partial because this stack implements the fingerprint for UDP DTLS-SRTP, not the connection-oriented TLS media transport named by the RFC title, and early-media DTLS is not exposed. |
| 8445 | Interactive Connectivity Establishment (ICE) | 🟡 partial | uac, uas | — | Both call roles are reachable through sipx_call::MediaPolicy: an application can select host gathering or configure a STUN server for server-reflexive gathering, while the default selects no ICE and preserves symmetric RTP. The diagnostic phone exposes the same choices as --ice disabled, --ice host and --ice stun; its STUN process test makes both host/default destinations unusable, carries audio on the nominated server-reflexive pair and reports that selected path from each running call. The initial offer/answer path gathers on the media port, emits fresh credentials and candidates, accepts the peer description and starts the session with the resulting agent. ice_call.rs independently makes both SDP default destinations unusable and proves audio crosses the lower-priority candidate pair only after nomination; it also proves an unavailable configured STUN server degrades to host candidates. The native-browser proof independently observes the same nominated component from browser statistics and sipx, cross-checks the endpoints in reverse, and closes a complete browser answer before nomination to prove the typed no-pair refusal starts neither DTLS nor RTP. A restart re-signals the sockets the session is already running on and does not re-run the STUN transaction — docs/specs/ice.md §13.5 records why and what that does not recover. Aggressive nomination is deprecated by §4 and deliberately absent. |
| 8825 | Overview: Real-Time Protocols for Browser-Based Applications | 🟡 partial | uac, uas | webrtc-audio | sipx implements the overview's separation of signalling, description, ICE, DTLS-SRTP and media ownership for one named audio profile. Partial by design: this is a SIP endpoint profile, not a browser API, full WebRTC stack, data-channel implementation or video session. |
| 8827 | WebRTC Security Architecture | 🟡 partial | uac, uas | webrtc-audio | The named profile requires authenticated WSS, ICE nomination before DTLS, verification of the signalled SHA-256 fingerprint, a negotiated SRTP profile and atomic key installation; weaker media is a typed refusal. Partial because browser identity assertions, consent UI and the wider browser-origin model are outside this endpoint library. |
| 8829 | JavaScript Session Establishment Protocol (JSEP) | 🟡 partial | uac, uas | webrtc-audio | The pure SDP boundary validates and answers the one-audio subset, including a completed native-browser offer shape, dynamic required payloads, safe extras, setup roles and fail-closed answer application. Partial because sipx does not implement the JavaScript API, multiple sections, BUNDLE behavior, incremental trickling or the complete JSEP state machine. |
| 8834 | Media Transport and Use of RTP in WebRTC | 🟡 partial | uac, uas | webrtc-audio | One secure audio stream uses RTP/SAVPF, mandatory RTP/RTCP multiplexing, one ICE-nominated component, DTLS-SRTP and the required audio formats in both call roles. Partial because the broader feedback, congestion, header-extension, multi-stream and video requirements are not implemented as a general WebRTC media engine. |
| 8839 | SDP Offer/Answer Procedures for ICE | 🟡 partial | uac, uas | — | Both initial-exchange roles are reachable from a call and from the diagnostic phone's direct mapping of --ice into that call policy. Its real-process STUN vector drives both offer/answer roles, makes the host/default paths unusable, carries audio on a nominated server-reflexive pair and reports the selected path rather than the requested policy. MediaPolicy selected on DialOptions, the free answer functions, dispatcher invitations and reliable early answers emits §4.2 credentials, candidates, ice2 and the highest-priority default destination; it then feeds the peer's answer or offer back into the retained media-port agent. Hold stays a=sendonly/a=inactive and never c=0.0.0.0, which §4.4.1.1.1 would read as a restart. Not implemented: remote-candidates is parsed but not generated for a Completed stream (§5.2). Trickle ICE (RFC 8838/8840) is a separate offer/answer model and is out of scope. |
| 4566 | SDP: Session Description Protocol (obsoleted by 8866) | — superseded | — | — | Superseded. Listed so a reader looking for it knows where it went. |
Services — what is built on top
| RFC | Title | Status | Roles | Spec | Notes |
|---|---|---|---|---|---|
| 3515 | The SIP Refer Method | ✅ implemented | uac, uas | — | Blind transfer, with the outcome reported by NOTIFY and the implicit subscription terminated. A 202 is modelled as Trying, never as success. |
| 3863 | Presence Information Data Format (PIDF) | ✅ implemented | — | — | A typed document rather than a string template: tuples with an id, a basic status of open or closed, an optional contact with a priority clamped to §4.1.4's range, and an optional note. XML metacharacters are escaped, because one unescaped & in a SIP URI makes the whole document unparseable and a watcher then sees nothing at all. RFC 4480's richer vocabulary is a different document and is not implemented. |
| 3891 | The SIP Replaces Header | ✅ implemented | uas | — | Matched on Call-ID and both tags. Matching on the Call-ID alone is a call-hijack primitive, and the refusal is the same 481 whichever field was wrong. |
| 3903 | SIP Extension for Event State Publication | ✅ implemented | uac, uas | publication-endpoint | Live UAC and UAS endpoint paths implement bounded RFC 3903 publication soft state. Inbound PUBLISH uses an injected authorization policy and the exact compositor allocation; every accepted create, refresh, modify or remove returns a fresh SIP-ETag, stale or cross-resource conditions return 412, and interval policy returns 423. The public outbound publisher retains each granted expiry and fresh entity tag across authenticated conditional operations, never retries 412, and exposes typed terminal state and owned-work counts. Publication is intentionally not a dialog, and applications still own durable or distributed storage, identity policy and projection of published presence into later NOTIFY documents. The dispatcher handles PUBLISH when this service is attached but does not use that optional attachment to change its static OPTIONS advertisement. |
| 4488 | Suppression of SIP REFER Method Implicit Subscription | ✅ implemented | uac, uas | — | Refer-Sub: false on the REFER and echoed in the 2xx, because §3 makes it a request and an agreement rather than a declaration — a transferor that assumed agreement would stop watching for notifications the transferee is still sending. |
| 3428 | SIP Extension for Instant Messaging | 🟡 partial | uac, uas | — | Applications can send and answer bounded MESSAGE requests inside an established dialog, including a bounded digest-authentication retry. Out-of-dialog instant messaging and message-store policy are not implemented. |
| 3680 | A SIP Event Package for Registrations | 🟡 partial | uac, uas | — | Both endpoint roles are live. The UAS emits bounded full reginfo through the dispatcher. The UAC subscribes through the generic authenticated event runtime and atomically maintains a typed, bounded current-contact set from full and partial documents; sipx peers --registrar exposes it with source and observation age. Partial because a version gap is rejected without automatically starting a fresh full-state subscription, and the UAS notifier is not automatically projected from an application registration store. |
| 3856 | A Presence Event Package for SIP | 🟡 partial | uas | — | The dispatcher serves a bounded inbound presence subscription and immediately originates a typed PIDF NOTIFY. The compositor and package document lifecycle remain available as library state. Missing: the live socket notifier is not yet fed by the PUBLISH compositor, and watcher information (RFC 3857) and the presence.winfo template are not implemented. |
| 3892 | The SIP Referred-By Mechanism | 🟡 partial | — | — | Sent and surfaced to the application. The signed Referred-By token is not implemented, so the identity is a claim rather than a proof. |
| 4235 | An INVITE-Initiated Dialog Event Package for SIP | 🟡 partial | uas | — | dialog-info documents carry the five states of §3.7.1, full-then-partial discipline and a per-subscription version counter. The dispatcher now serves a bounded inbound dialog subscription and originates its full initial document. Missing: the package is not yet projected from the dispatcher's live call table, so subsequent call-state changes are not automatic. |
| 5627 | Obtaining and Using GRUUs in SIP | 🟡 partial | uac, uas | — | The UA side, for both roles: Supported: gruu and +sip.instance on REGISTER (§4.1) under the same instance identity RFC 5626 registers with, pub-gruu and temp-gruu read off the binding that names this instance and discarded with it (§4.2), a GRUU in the Contact of dialog-forming and target-refresh requests (§4.4), and §4.5's gr recognition — which is a comparison of its own because §5.4 notes a public GRUU is URI-equivalent to the AOR. A registered UA refuses an initial INVITE bearing an unowned GRUU with the 404 §6.1 gives an unresolved GRUU, so upstream misrouting cannot establish a call at another instance. The choice between public and temporary is the application's and asking for a temporary one never silently yields the public one, since §5.4's unlinkability is not something the other can stand in for. Not the registrar or proxy role: minting GRUUs, the temporary-GRUU construction of §5.4, the 480 of §5.2, and ordinary location-service resolution in §6.1 remain server behavior sipx does not implement. |
| 6086 | SIP INFO Method and Package Framework | 🟡 partial | uac, uas | — | Applications can send and answer bounded INFO requests inside an established dialog, including a bounded digest-authentication retry. INFO package negotiation and package semantics remain application policy; sipx sends DTMF as RFC 4733 rather than as INFO. |
| 7044 | An Extension to SIP for Request History Information | 🟡 partial | uac, uas | sip-history-info | The UA side for both call roles: History-Info is a typed ordered list with hierarchical indices and rc/mp/np target-change references. Initial calls advertise histinfo and index their first target at 1; non-100 responses return the cache and calls retain the received history. sipx_ua::retarget preserves received entries, exposes an omitted previous hop with .0, appends the forwarding .1, and embeds the typed Reason in the previous SIP/SIPS URI; tel targets correctly have no URI-header component. Message- and entry-level history privacy anonymize identifying targets while retaining indices. Partial because sipx is not a proxy and does not build a fork's sibling branches; automatic cache extension from every received 3xx/timeout remains the retargeting caller's explicit status input. |
| 7092 | A Taxonomy of Session Initiation Protocol (SIP) Back-to-Back User Agents | 🟡 partial | uac, uas | call-coupling | The taxonomy is informational rather than a protocol conformance claim. sipx supplies the logical UAS/UAC concatenation as one owner of two dialogs: its owning constructor creates the target initial INVITE from the source offer; a target reliable-provisional offer is relayed to a source reliable provisional and its source PRACK answer is relayed in the held target PRACK; per-leg policy covers every carrier; UPDATE and re-INVITE relay live; glare is refused before forwarding; and a BYE on either confirmed leg ends its peer. Attaching the media bridge gives §3.2.3 media termination with explicit transcoding. Partial because leaving the bridge unattached still leaves two locally terminating media sessions rather than the truly off-media-path §3.1.3 role. Both uac and uas are named because a B2BUA is their logical concatenation, not because sipx ships routing, a registrar or a dial plan. |
| 7865 | SDP Recording Metadata | ⬜ not started | — | — | The metadata half of SIPREC. |
| 7866 | Session Recording Protocol | ⬜ not started | — | — | Recording as a protocol rather than as a file written locally. |
How to read a status
Syntax only is not a half-measure, it is a different thing. sipx parses RAck and
RSeq and does nothing with them: a message carrying them survives the wire and is
forwarded intact, and no PRACK is ever sent. That is worth recording separately, because
"we support RFC 3262" would be false and "we reject it" would also be false.
Partial always says what is missing. An entry that cannot name the gap should be
none until somebody works out what it is.
The Spec column is the normative contract, not the evidence. Where it is filled in, that document says what sipx must do about the RFC — RFC citations, types, state, and the byte-level vectors the tests are derived from — and the entry's status is measured against it. An em dash means the subsystem has no spec yet, which for a non-trivial one is a gap worth a story rather than a fact about the table.