From 0370347642866d03d3ac2ce37c78dbf9667093de Mon Sep 17 00:00:00 2001 From: adlee-was-taken Date: Fri, 26 Jun 2026 21:49:36 -0400 Subject: [PATCH] =?UTF-8?q?ADR-0001:=20SIP=20strategy=20=E2=80=94=20native?= =?UTF-8?q?=20Rust=20core=20behind=20Kamailio=20+=20rtpengine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Record the SIP edge decision and align the docs: - docs/adr/0001-sip-strategy.md: layered strategy (own Rust parser, rent the interop tail via a Kamailio + rtpengine SBC, grow native core behind the shield); pjproject FFI explicitly rejected for breaking the memory-safety thesis at the most exposed seam. - PORT_PLAN Β§1 + open decisions: SIP row updated to the decided strategy. - ARCHITECTURE: "biggest technical risk" now points at ADR-0001. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01C2bfD7MkqEdfnMXxXBu456 --- docs/ARCHITECTURE.md | 8 ++-- docs/PORT_PLAN.md | 4 +- docs/adr/0001-sip-strategy.md | 78 +++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 5 deletions(-) create mode 100644 docs/adr/0001-sip-strategy.md diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index abe134a..d18d62b 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -49,6 +49,8 @@ microservices for stateful/business/billing logic. ## Biggest technical risk -The **SIP stack**. No mature pure-Rust option exists β†’ FFI pjproject or front the edge -with a battle-tested SBC initially; treat a pure-Rust stack as a long-term goal, not a -v1 dependency. Everything else builds on the existing Rust media ecosystem. +The **SIP stack** β€” **decided in [ADR-0001](adr/0001-sip-strategy.md)**: own the Rust +parser from day one (the security thesis depends on it), front the public edge with a +proven **Kamailio + rtpengine** SBC to absorb the interop tail, and grow the native Rust +transaction/dialog core behind that shield. No pjproject FFI. Everything else builds on +the existing Rust media ecosystem. diff --git a/docs/PORT_PLAN.md b/docs/PORT_PLAN.md index a5d937a..5affe2f 100644 --- a/docs/PORT_PLAN.md +++ b/docs/PORT_PLAN.md @@ -33,7 +33,7 @@ Grounded against the Asterisk 22.10.1 tree (~1.18M LOC C/H). | Asterisk subsystem | Module(s) | Disp. | Rationale | |---|---|---|---| -| SIP signaling | `chan_pjsip` + `res_pjsip*` (48 mods) | πŸ”Œ Edge/FFI | No mature pure-Rust SIP stack. FFI pjproject or front an SBC; pure-Rust long-term. The single biggest risk item. | +| SIP signaling | `chan_pjsip` + `res_pjsip*` (48 mods) | πŸ”Œ Edge β†’ πŸ¦€ Core | **Decided (ADR-0001):** public SIP edge = **Kamailio + rtpengine** (proven SBC shield); native **Rust** parser + transaction/dialog core (`rsip`/`ezk-sip`) grows behind it. **No pjproject FFI** β€” it would put C at the most exposed seam and break the memory-safety thesis. The single biggest risk item. | | ↳ registration (in/out) | `res_pjsip_registrar`, `_outbound_registration` | πŸ¦€ Core | Registrar state is core control-plane; back it with the state store. | | ↳ SDP / media negotiation | `res_pjsip_session`, `_sdp_rtp` | πŸ¦€ Core | Offer/answer drives the media plane directly. | | ↳ auth | `res_pjsip_outbound_authenticator_digest`, etc. | πŸ¦€ Core | Digest + token auth, deny-by-default, rate-limited. Security-critical. | @@ -205,7 +205,7 @@ These are the "modern" deltas β€” load-bearing, not optional polish. ## Open decisions -- **SIP:** FFI pjproject vs. SBC front (Kamailio/drachtio) vs. pure-Rust (`ezk-sip`). Affects Β§1 heavily. +- ~~**SIP:** FFI pjproject vs. SBC front vs. pure-Rust.~~ **Decided β€” ADR-0001:** native Rust core behind a **Kamailio + rtpengine** edge. - **WASM runtime:** `wasmtime` component model (presumptive). - **Event bus:** NATS vs. Kafka vs. Redis Streams. - **License** (see README). diff --git a/docs/adr/0001-sip-strategy.md b/docs/adr/0001-sip-strategy.md new file mode 100644 index 0000000..e70eb87 --- /dev/null +++ b/docs/adr/0001-sip-strategy.md @@ -0,0 +1,78 @@ +# ADR-0001 β€” SIP strategy: native Rust core behind a Kamailio + rtpengine edge + +- **Status:** Accepted +- **Date:** 2026-06 +- **Supersedes:** the `πŸ”Œ Edge/FFI (pjproject)` disposition in PORT_PLAN.md Β§1 + +## Context + +SIP is rutster's highest-risk subsystem. Three forces pull against each other: + +1. **The security thesis.** rutster's headline is "memory-safe by construction, fuzzed + parsers, no buffer-overflow/RCE class." The SIP parser is the *first* thing hostile + internet packets touch β€” the most exposed surface in the whole system. +2. **The interop tail.** The hard, *unbounded* part of SIP isn't RFC 3261 β€” it's 20 years + of device/carrier/NAT quirks. Owning that tail forever is what killed `chan_sip` and + drove Asterisk to retreat to pjproject. It is a permanent tax, not a one-time cost. +3. **Time-to-first-call.** We want interoperable SIP trunking early, without blocking on + a young stack maturing. + +These don't resolve as a single build-vs-buy call, because they live at *different +layers* of SIP: + +| Layer | CVE-class risk | Interop-tail cost | Verdict | +|---|---|---|---| +| Parser (bytes off the wire) | **Highest** | Low | **Own in Rust** β€” cheap, and the thesis depends on it | +| Transaction / dialog / timers | Medium | Medium (bounded by RFCs) | Own over time; sans-IO Rust fits | +| Interop tail (UA quirks, NAT, carrier glare) | Low | **Unbounded, forever** | **Rent** β€” this is what a mature edge is for | + +Pure FFI to pjproject was rejected: it pays the interop cost *and* puts C at the most +exposed seam, gutting the memory-safety headline at the one place it matters most. It is +the right default for a generic PBX and the wrong one for a security-first platform. + +## Decision + +A **layered** strategy: + +1. **Own the SIP parser in Rust, from day one.** Build on `rsip` (message types/parsing) + and the `ezk-sip` family (sans-IO transaction/dialog core). No hostile bytes ever hit + a C parser. This is non-negotiable β€” the security story requires it. +2. **Front the public SIP edge with Kamailio + rtpengine.** Kamailio handles signaling + (proxy, registrar, TLS termination, topology hiding, rate-limiting, anti-scan); + **rtpengine** handles the media leg (kernel-forwarded RTP relay, SRTP/DTLS-SRTP + bridging, ICE, NAT). This is the most-proven, most-scaled FOSS SIP edge β€” carrier-grade + on the open internet. It absorbs the interop tail and gives us a hardened edge in weeks. +3. **Grow the native Rust transaction/dialog core *behind* that shield.** It matures in + production where its early bugs aren't customer-facing outages. As it hardens, + Kamailio's role shrinks from "does all the SIP" β†’ "just the public edge" β†’ optionally + nothing. + +**Chosen edge: Kamailio + rtpengine** (over OpenSIPS and drachtio). Rationale: largest +proven deployment pedigree and max-scale proxy track record; rtpengine is the canonical +media companion. OpenSIPS and drachtio remain valid alternatives (drachtio noted for a +future *programmable* edge once rutster's API model is solid). + +## Topology + +``` + hostile internet trusted core + ───────────────► Kamailio ──signaling──► rutster control plane + (SIP trunks, + rtpengine ──media─────► rutster media plane + far-end UAs) (edge SBC) (native Rust SIP core + grows here, behind the shield) +``` + +WebRTC ingress is unaffected and ships *first* (WebRTC signaling is app-defined, not SIP), +so first-call does not block on any of this. + +## Consequences + +- **Positive:** security headline holds (Rust parser at the edge of *our* trust boundary); + interoperable SIP trunking available early; native stack de-risked by maturing behind a + proven shield; no pjproject license/threading/`unsafe` entanglement. +- **Negative / costs:** an operational dependency on Kamailio + rtpengine (config, + deployment, expertise) for the foreseeable future; two media-handling points during the + shield phase (rtpengine at the edge, rutster media plane inside) to reason about. +- **Open follow-ups:** Kamailio↔rutster control-plane integration (KEMI vs. dispatcher vs. + a custom control channel); how far inward rtpengine relays vs. rutster terminating media + directly; the graduation criteria for retiring the shield.