Files
rutster/docs/adr/0003-sip-rust-native-trunk.md
adlee-was-taken 075e984fb5 docs: ratify vision revision across README/ARCHITECTURE/PORT_PLAN + ADRs 0002-0006
Folds the 2026-06-26 vision-revision pressure-test into the canonical
docs. The spec moves from Proposed to Ratified; the decisions it produced
land in the docs it said it would amend, with ADRs recording each
load-bearing reversal.

- README.md: reframe north star (AI-era contact-center engine, not
  Asterisk successor); persona; revised pillars (add data-ownership,
  demote WASM, promote spend-control); update 'what it is/isn't'.
- ARCHITECTURE.md: replace three-plane framing with fused per-call
  vertical + composable horizontal platform; remove control<->media
  hot-path gRPC hop; make the agent tap the central interface; add
  DX spine + GUI-as-API-client + k8s declarative/operational model.
  Also: 'too slow to police' -> 'too slow to enforce' (terminology).
- PORT_PLAN.md: recharacterize as capability checklist (not template);
  graduate contact-center capabilities to first-class domain; Rust-native
  trunk SIP rows; WASM demoted; thin-slice + capability ladder phasing.
- ADR-0001: marked Superseded by ADR-0003.
- ADR-0002 (new): north star + fused per-call core.
- ADR-0003 (new): Rust-native trunk SIP, no SBC shield.
- ADR-0004 (new): GPL-3.0-or-later license.
- ADR-0005 (new): Valkey as event bus + state store.
- ADR-0006 (new): WebRTC-first ingress; SIP endpoint deferred.
- vision-revision spec: status -> Ratified 2026-06-26.
2026-06-28 09:33:29 -04:00

3.9 KiB
Raw Blame History

ADR-0003 — SIP strategy: Rust-native trunk SIP, no SBC shield

Context

ADR-0001 layered the SIP strategy: own the Rust parser, but front the public edge with a proven Kamailio + rtpengine SBC to rent the "20-year device/carrier interop tail." That tail is real for a generic PBX — thousands of desk-phone quirks, NAT behaviors, carrier glare.

Reconsidered under the AI-era contact-center scope (ADR-0002), the tail collapses: rutster talks to a few documented, cooperative SIP-trunk providers (Telnyx, Bandwidth, Twilio SIP), IP-allowlisted, not thousands of far-end desk-phone UAs. An inbound/outbound SIP-trunk client against cooperative carriers is tractable in rsip / ezk, bounded by carrier documentation, not device quirks.

The C SBC shield, meanwhile, directly contradicts the wedge:

  • It puts C at the most-exposed seam (the public internet) — gutting the memory-safety headline at the one place it matters most.
  • It terminates media twice — rtpengine at the edge, rutster's media plane inside — breaking "terminate media once" / "one auditable boundary."

Decision

Own trunk SIP and media termination directly in the Rust core; IP-allowlist the handful of trunk providers. No Kamailio + rtpengine shield. No pjproject FFI.

  • Parser: rsip (message types/parsing). Sans-IO transaction/dialog/core: the ezk family. No hostile bytes ever hit a C parser. Fuzzed.
  • The memory-safety thesis becomes literally true at the wire: hostile bytes hit a fuzzed Rust parser first, at the edge of our trust boundary.
  • Interop surface is bounded by carrier docs (a finite, knowable set), not the unbounded device tail ADR-0001 rented the shield for.

Scope boundary — this ADR is about trunk SIP, not endpoint SIP

Inbound SIP endpoint registration (desk/soft phones: REGISTER, BLF/MWI, DTMF variants, per-device NAT/provisioning) is a different axis and is deferred — it re-imports the unbounded device-interop tail this ADR deliberately closes. Human-participant ingress is WebRTC. See ADR-0006.

Topology

  hostile internet              trusted core
  ───────────────►  (carrier trunk)  ──►  rutster control + media plane
   (SIP trunks,                              (native Rust SIP parser +
    far-end UAs)                               transaction/dialog core,
                                               owned end-to-end here)

WebRTC ingress ships first and is unaffected (WebRTC signaling is app-defined, not SIP), so first-call never blocks on SIP.

Consequences

  • Positive: memory-safety headline literally true at the wire; one media-termination point; no C operational dependency (no Kamailio/rtpengine config/deploy/expertise); no pjproject license/threading/unsafe entanglement; the trunk client is bounded by carrier docs, not device quirks.
  • Negative / cost: we own a (bounded) SIP-trunk client early; carrier-specific interop must be maintained as trunks are added.
  • Mitigation: WebRTC-first ordering means first-call doesn't block on SIP; the thin-slice steps 14 (media core → tap → brain → barge-in) all land before step 5 (real PSTN trunk).
  • Graduation: unlike ADR-0001, there is no shield to retire — the question is only how the trunk client matures in production.

References

  • ADR-0001 — superseded (the layered Kamailio+rtpengine + Rust-core plan)
  • ADR-0002 — the fused-vertical reframe this SIP decision enables
  • ADR-0006 — WebRTC-first ingress; SIP endpoint deferred