Files
rutster/docs/PORT_PLAN.md
Aaron D. Lee da6b7c9ff0
All checks were successful
CI / fmt (push) Successful in 1m5s
CI / clippy (push) Successful in 1m53s
CI / test (1.85) (push) Successful in 5m54s
CI / test (stable) (push) Successful in 6m36s
CI / deny (push) Successful in 2m12s
adr: +0009 spend-gate rescope +0010 spearhead 4.5 benchmark/sim; amend 0004
Maintainer-ratified from the 2026-07-03 review (R1-R3):
- ADR-0009: retire "structurally impossible for a 3-vendor stack" post-0007;
  restate the gate's true guarantees (credential isolation, unskippable
  mediation, media-plane enforcement, audit co-location). Propagated to
  README pillar 3, ARCHITECTURE, PORT_PLAN s10; amendment note on ADR-0002.
- ADR-0010: insert step 4.5 (benchmark + simulation harness, rutster-sim
  seed, CI-regressed p50/p99 + kill-time) after barge-in; pull rung-2
  escalation ahead of steps 5-6. Spearhead lists updated.
- ADR-0004: delete the legally-broken AGPL escape hatch; GPL-3.0-or-later
  permanent, no CLA; tap protocol/SDKs intended permissive (future ADR).
- README: add brain-vendor-direct competitor row (review D2).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018vNe64BBDkgo5oQVkBa3XF
2026-07-04 00:00:57 -04:00

256 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Rutster — Asterisk Capability Checklist
This is the **capability checklist**, not the architecture spine. It walks the Asterisk
subsystem surface as a completeness audit — *"what must a telephony system handle"* — and
assigns every capability a **disposition** in the rutster architecture, with a one-line
rationale. We keep the checklist; we **reject** the Asterisk channel/bridge/dialplan model
as the architecture (see [ADR-0002](adr/0002-north-star-and-fused-core.md)). The crate/service
boundaries fall out of the "Core / Service" rows; the threat model hangs off the "attack
surface" of each row.
Grounded against the Asterisk 22.10.1 tree (~1.18M LOC C/H).
## Disposition legend
| | Disposition | Meaning |
|---|---|---|
| 🦀 | **Core** | In the hardened, memory-safe Rust core. Reserved for the hot media path, call model, and signaling glue — fast, always-present, trusted. |
| 🧩 | **Plugin (WASM)** | Sandboxed extension. Logic that benefits from isolation and third-party extensibility. Cannot crash the core or cross tenant boundaries. |
| ☁️ | **Service** | Independent microservice. Stateful, independently-scaled, or business/billing logic. Talks over API + event bus. |
| 🔌 | **Edge / FFI** | Wrap mature external C (pjproject/spandsp) or front with an SBC. Used where a pure-Rust rewrite is a tar pit. |
| ⛔ | **Drop** | Legacy hardware or dead protocol. Gone. |
| 🔁 | **Replace** | Capability survives but the mechanism is entirely redesigned (no 1:1 mapping). |
## Design rules (the heuristics behind every row)
1. Hot media path & the call model → **Core**. Trusted, fast, always-on. Inside the fused
per-call vertical.
2. Anything that benefits from isolation or third-party extension → **Plugin** *(now softened —
WASM demoted out of the core story per [ADR-0002](adr/0002-north-star-and-fused-core.md); the
agent **tap** is the primary extension point for in-call logic).*
3. Stateful, independently-scaled, or business/billing logic → **Service**. Around the core,
horizontal platform.
4. Mature C that's a tar pit to rewrite (SIP) → **rent it, don't own it.**
*[ADR-0007](adr/0007-trunk-rented-transport.md) restores this rule's original instinct for the
trunk: rutster owns no SIP stack — carrier transport is a rented CPaaS raw-media fork (primary) or
an out-of-tree SBC (on-prem graduation); ADR-0003's from-scratch Rust trunk SIP is superseded. FFI
retained only for mature codec/DSP/EQ libraries (libopus, speexdsp, WebRTC APM).*
5. Legacy hardware / dead protocols → **Drop**.
6. Security & multi-tenancy are cross-cutting — never a row, always a property. **Spend / abuse
control is constitutive**, pulled into the boundary, not externalized.
---
## 1. Signaling & channel drivers (`channels/`, `res/res_pjsip*`)
| Asterisk subsystem | Module(s) | Disp. | Rationale |
|---|---|---|---|
| SIP signaling (trunk) | `chan_pjsip` + `res_pjsip*` (48 mods) | 🔌 Rented / out-of-tree | **Re-decided ([ADR-0007](adr/0007-trunk-rented-transport.md), supersedes ADR-0003):** rutster owns **no SIP stack.** PSTN audio arrives as a media-leg ingress from a rented CPaaS raw-media fork (Twilio Media Streams / Telnyx — primary), or an out-of-tree SBC (Kamailio/FreeSWITCH/drachtio + rtpengine) when media must stay on-prem. rutster parses no SIP → SIP is removed from the attack surface entirely, not rewritten. The from-scratch Rust trunk SIP of ADR-0003 was the highest-cost / lowest-differentiation square on the board, with no production-hardened Rust SIP stack to stand on. |
| ↳ outbound registration (to carriers) | `res_pjsip_outbound_registration` | 🔌 Rented / out-of-tree | Handled by the rented transport or the out-of-tree SBC ([ADR-0007](adr/0007-trunk-rented-transport.md)), not by rutster — the provider/SBC owns register-to-carrier. |
| ↳ **inbound endpoint registration (desk/soft phones)** | `res_pjsip_registrar` | ⛔ **Deferred — community edge** | **Split from trunk.** An inbound endpoint server re-imports the *unbounded device interop tail* — it fails all three FOB tests ([ADR-0008](adr/0008-fob-and-green-zone.md)), so it's **green-zone, out-of-tree, never first-party** ([ADR-0007](adr/0007-trunk-rented-transport.md)): Polycom/Yealink/Zoiper `REGISTER`, BLF/MWI, DTMF variants, per-device NAT/provisioning. In an SSO/browser-agent UX, the desk-phone demographic has collapsed. Out-of-tree SBC-style adapter (fronting the public API / B2B-SIP) for anyone who needs it — **outside the trust boundary**, so its tail can't compromise the wedge. See [ADR-0006](adr/0006-ingress-posture.md). |
| ↳ 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. |
| ↳ NAT / ICE / rtcp | `res_pjsip_nat`, `res_rtp_asterisk` ICE | 🦀 Core | ICE/STUN/TURN via `str0m`/`webrtc-rs`. |
| ↳ pub/sub (presence, MWI, BLF) | `res_pjsip_pubsub`, `_exten_state`, `_mwi` | 🦀 Core + ☁️ | Signaling in core; presence/MWI state aggregation as a service. |
| ↳ T.38 fax | `res_pjsip_t38` | ☁️ Service | Isolate fax entirely (see §5). |
| WebRTC signaling (agent ingress) | `chan_websocket`, `res_http_websocket` | 🦀 Core | **First-party human-participant ingress** ([ADR-0006](adr/0006-ingress-posture.md)). The browser is the softphone: SSO in, one peer-connection, DTLS-SRTP + ICE handled by the browser, zero device-provisioning tail. Also the modern hosted UX and the escalation path (rung 2): agent clicks *take this call* → WebRTC leg + audiohook/barge handoff. WSS + DTLS-SRTP via the media stack. |
| External media | `chan_audiosocket` | 🦀 Core | Modern, relevant pattern (stream media to an external process); keep as a media tap/source API. |
| Local console audio | `chan_console` | 🧩 Plugin | Dev/testing convenience; not core. |
| RTP-only pseudo-channel | `chan_rtp`, `chan_bridge_media` | 🦀 Core | Media-plane primitives. |
| IAX2 | `chan_iax2` | ⛔ Drop | Dead inter-Asterisk protocol. SIP trunks instead. |
| TDM / analog / ISDN | `chan_dahdi`, `sig_analog`, `sig_pri`, `sig_ss7` | ⛔ Drop | No hardware era. PSTN via SIP trunk / SBC. |
| H.323 | `addons/chan_ooh323` | ⛔ Drop | Dead protocol. |
| SCCP / Skinny, MGCP | (Skinny/MGCP) | ⛔ Drop | Dead protocols. |
| Unistim | `chan_unistim` | ⛔ Drop | Dead vendor protocol. |
| XMPP / Jingle | `chan_motif`, `res_xmpp` | ⛔ Drop | Dead path. |
---
## 2. Media plane (`main/rtp_engine.c`, `res_rtp_asterisk`, `bridges/`, `main/dsp.c`)
| Asterisk subsystem | Module(s) | Disp. | Rationale |
|---|---|---|---|
| RTP / RTCP engine | `rtp_engine.c`, `res_rtp_asterisk` | 🦀 Core | **Crown jewel.** Built on `str0m`/`webrtc-rs`, sans-IO, dedicated timing (not the shared tokio pool). |
| SRTP / DTLS | `res_srtp` | 🦀 Core | Mandatory-by-default encryption. |
| Bridging framework | `main/bridge.c` | 🦀 Core | The 2-party + N-party call topology engine. |
| ↳ softmix conference mixer | `bridges/bridge_softmix` | 🦀 Core | N-party mixing on the hot path. |
| ↳ simple / native-RTP bridge | `bridge_simple`, `bridge_native_rtp` | 🦀 Core | Direct media flow (re-INVITE peers together) when no mixing needed. |
| ↳ holding bridge | `bridge_holding` | 🦀 Core | Parking/hold media state. |
| ↳ native DAHDI bridge | `bridge_native_dahdi` | ⛔ Drop | Hardware. |
| DSP (DTMF/MF detect, silence, tone) | `main/dsp.c` | 🦀 Core | Media-plane primitive; exposed to plugins as events. |
| Echo cancellation | (hardware / sw) | 🔌 Edge/FFI | No mature pure-Rust EC; FFI speexdsp / WebRTC APM. |
| Audio hooks (tap/inject/volume) | `main/audiohook.c` | 🦀 Core | Primitive behind recording, ChanSpy, whisper/barge. |
| Music on hold | `res_musiconhold` | 🦀 Core | Just a media source; small, keep in core (asset from object storage). |
| Jitter buffer | `func_jitterbuffer`, abstract jb | 🦀 Core | Quality-critical; part of the RTP path. |
---
## 3. Call model & PBX core (`main/`)
| Asterisk subsystem | Module(s) | Disp. | Rationale |
|---|---|---|---|
| Channel abstraction | `main/channel.c` (`ast_channel`) | 🦀 Core | The unifying "leg" object (signaling + media state). Reimagined as a `Channel`/leg. |
| PBX / dialplan engine | `main/pbx.c` | 🔁 Replace | → declarative routing (data) + event-driven apps. No `extensions.conf`. |
| Call features (transfer/park/pickup) | `res_features`, `res_parking` | 🦀 Core | Call-control primitives, exposed via the API. |
| Scheduler / taskprocessor / threadpool | `main/sched.c`, `taskprocessor.c`, `threadpool.c` | 🦀 Core | tokio for control; dedicated timing threads + timer wheel for the 20ms media loop. |
| Internal message bus (Stasis) | `main/stasis*.c` | 🔁 Replace | In-core dispatcher for intra-core; external event bus (NATS/Kafka) for cross-service. |
| Module loader | `main/loader.c` | 🔁 Replace | → WASM host + service registry. No in-process `.so`. |
| ACL / named ACL | `main/acl.c`, `res_named_acl` | 🦀 Core | Security primitive; policy-driven. |
| astdb (internal KV) | `main/db.c` | 🔁 Replace | → state store (**Valkey** KV, [ADR-0005](adr/0005-event-bus.md)). |
---
## 4. Dialplan & programmability (`pbx/`, `res_agi`, `res_ari`, `manager`)
| Asterisk subsystem | Module(s) | Disp. | Rationale |
|---|---|---|---|
| `extensions.conf` dialplan | `pbx_config` | 🔁 Replace | Declarative routing as data (GitOps) for the 90% case. |
| AEL / Lua dialplan | `pbx_ael`, `pbx_lua` | ⛔ Drop | Replaced by WASM call-flow + routing config. |
| Realtime dialplan | `pbx_realtime` | 🔁 Replace | Routing in the config/state layer. |
| Call files / spool | `pbx_spool` | 🔁 Replace | → originate via API. |
| Macro / Gosub / Stack | `app_stack`, `app_macro` | 🔁 Replace | → WASM call-flow logic. |
| AGI / FastAGI | `res_agi` | 🔁 Replace | → WASM plugins + gRPC. Same "external program controls a call," sandboxed and typed. |
| ARI (REST + stasis apps) | `res_ari*`, `res_stasis*` | 🦀 Core | **Becomes THE API** — the control-plane resource model. `api-docs/*.json` is the reusable spec. |
| AMI (manager) | `main/manager*.c` | 🔁 Replace | Drop the plaintext TCP protocol → mTLS gRPC + RBAC + audit log. |
| CLI | `main/cli.c` | 🔁 Replace | Thin CLI that's an API client, not an in-process console. |
| DUNDi (distributed routing) | `pbx_dundi` | ⛔ Drop | Dead/niche. |
| ENUM lookup | `func_enum` | 🧩 Plugin | Routing-time lookup; pluggable. |
---
## 5. Applications (`apps/`) — the contact-center domain
> **Scope lift** ([vision-revision](superpowers/specs/2026-06-26-vision-revision-design.md) §4): the
> contact-center capabilities Asterisk kept as apps are **rutster's core domain**, not peripheral.
> This is the difference between "Asterisk successor" and "AI-era contact-center engine." Every row
> here is load-bearing for the wedge (the thing LiveKit will never ship).
| Asterisk app | Module | Disp. | Rationale |
|---|---|---|---|
| Dial / originate | `app_dial`, `app_originate` | 🦀 Core | Core call origination, exposed via the API. |
| Queues / ACD | `app_queue` | ☁️ Service (**domain core**) | Stateful, independently scaled. **First-class** — ACD is the beating heart of a contact center, not a peripheral app. The AI self-serve + human-escalation loop (capability ladder) is an ACD-shaped problem. |
| Voicemail | `app_voicemail` | ☁️ Service | Stateful storage + business logic; isolate it. |
| Conferencing | `app_confbridge` | 🦀 Core + ☁️ | Mixing in core (softmix); orchestration/features as a service. |
| MeetMe | `app_meetme` | ⛔ Drop | DAHDI-timing legacy; ConfBridge supersedes. |
| Recording | `app_mixmonitor`, `app_monitor` | 🦀 Core + ☁️ | Audiohook tap in core; storage/lifecycle as a recording service (object-storage-backed, retention policy, tenant-scoped — **durable, not on the bus**). |
| ChanSpy / whisper / barge | `app_chanspy` | 🦀 Core | Audiohook primitive; gated by RBAC. **The escalation primitive** (rung 2). |
| Answering-machine detect | `app_amd` | 🦀 Core + 🧩 | DSP primitive in core; policy/thresholds as a plugin. |
| IVR primitives (playback/read/say) | `app_playback`, `app_read`, `app_sayunixtime`, `app_directory` | 🦀 Core | Media primitives (play/collect-digits/say) exposed via the API/tap. The IVR *flow* dissolves into the agent; these are the primitives left over. |
| Echo / test | `app_echo`, `app_milliwatt` | 🦀 Core | Trivial diagnostics. |
| FollowMe | `app_followme` | 🧩 Plugin | Routing policy → plugin/service. |
| Speech recognition | `app_speech_utils`, `res_speech` | ☁️ Service | Modern ASR is external; expose as a service integration. |
| Fax | `res_fax`, `res_fax_spandsp` (T.38) | ☁️ Service (FFI) | Niche, isolate fully; spandsp via FFI. Optional. |
| **Supervisor / agent-state** | (scattered manager/AMI surfaces) | ☁️ Service (**domain core**) | **First-class.** Agent presence, state, real-time monitoring, coaching/whisper — the contact-center operator surface. Pure API client (the dashboard). |
| **Dialer (predictive/progressive)** | (custom/`app_dial`-derived) | ☁️ Service (**domain core**) | **First-class.** Outbound campaign dialing with pacing — where the in-boundary spend/abuse gate (§10) *bites hardest*; structurally co-located. |
---
## 6. Telephony features / subscriptions
| Asterisk subsystem | Module(s) | Disp. | Rationale |
|---|---|---|---|
| Presence / BLF | `res_pjsip_pubsub`, `res_pjsip_exten_state` | 🦀 Core + ☁️ | Signaling in core; state aggregation service. |
| MWI (message-waiting) | `res_mwi_external`, `app_voicemail` MWI | ☁️ Service | Tied to the voicemail service. |
| STIR/SHAKEN (attestation) | `res_stir_shaken` | ☁️ Service | **Keep — modern, anti-robocall/fraud.** Core to the security story. |
| Call recording lifecycle | `app_mixmonitor` + storage | ☁️ Service | Object-storage backed, retention policy, tenant-scoped. |
| CDR (call detail records) | `cdr/`, `cdr_*` | ☁️ Service | Event stream → billing/analytics pipeline. |
| CEL (channel event logging) | `cel/`, `cel_*` | ☁️ Service | Folded into the unified event bus + analytics. |
---
## 7. Codecs & formats (`codecs/`, `formats/`)
| Codec / format | Module | Disp. | Rationale |
|---|---|---|---|
| G.711 a-law / µ-law | `codec_alaw`, `codec_ulaw`, `codec_a_mu` | 🦀 Core | Trivial, ubiquitous. Native Rust. |
| G.722 (HD) | `codec_g722` | 🦀 Core | Common wideband. Native Rust. |
| Opus | (external `codec_opus`) | 🦀 Core (FFI) | WebRTC default; libopus via FFI. |
| Sample-rate resample | `codec_resample` | 🦀 Core | Needed for any transcoding/mixing. |
| GSM | `codec_gsm` | 🧩 Plugin (FFI) | Optional; FFI libgsm. |
| G.726 / G.729 | `codec_g726` / (external) | 🧩 Plugin (FFI) | Optional; G.729 now patent-free. |
| Speex / iLBC / LPC10 / ADPCM / codec2 | `codec_speex`, `codec_ilbc`, … | ⛔ Drop | Obsolete; drop or community plugin. |
| Hardware transcode | `codec_dahdi` | ⛔ Drop | Hardware. |
| File formats (wav/sln/ogg-opus) | `format_wav`, `format_sln`, … | 🦀 Core | Small set for prompts/recordings in core; rest as plugins. |
| Legacy file formats (gsm/g729/g726 files) | `format_*` | 🧩 Plugin | Optional. |
---
## 8. Config, state & data (`res_config_*`, `res_sorcery*`)
| Asterisk subsystem | Module(s) | Disp. | Rationale |
|---|---|---|---|
| `.conf` files | `main/config.c` | 🔁 Replace | Config-as-data (declarative, validated, GitOps). |
| Sorcery (object config abstraction) | `res_sorcery*` | 🔁 Replace | Typed config + state-store layer. |
| Realtime backends (ODBC/PgSQL/SQLite/cURL/LDAP) | `res_config_*` | 🔁 Replace | One config/state service with pluggable backends. |
| HTTP media cache | `res_http_media_cache` | 🦀 Core | Prompt/asset fetch → object storage + cache. |
| Security event logging | `res_security_log` | 🦀 Core | Structured audit log; security-critical. |
---
## 9. Management & APIs
| Asterisk surface | Module(s) | Disp. | Rationale |
|---|---|---|---|
| ARI (REST resource model + event WS) | `res_ari*` | 🦀 Core | The control-plane API. Reuse the resource model. |
| AMI (plaintext TCP admin) | `manager*.c` | 🔁 Replace | → mTLS gRPC + RBAC + audit. No plaintext admin channel. |
| HTTP server | `res_http*` | 🦀 Core | TLS-only; axum/hyper. |
| CLI | `cli.c` | 🔁 Replace | API-client CLI. |
---
## 10. New subsystems (no Asterisk equivalent)
These are the "modern" deltas — load-bearing, not optional polish.
| Subsystem | Disp. | Purpose |
|---|---|---|
| WASM plugin host | 🧩 **Demoted (candidate)** | **Out of the core story** ([ADR-0002](adr/0002-north-star-and-fused-core.md)). The agent **tap** is the extension point for in-call logic; ops-simplicity wants one binary. Softly retained as a *candidate* mechanism for community call-flow/routing plugins — undecided vs. webhooks/scripting. |
| Multi-tenancy / isolation | 🦀 Core | Per-tenant keys, quotas, media separation, RBAC. |
| Toll-fraud / spend / abuse engine | 🦀 Core (**promoted**) | **Constitutive, not a service** ([ADR-0002](adr/0002-north-star-and-fused-core.md), re-scoped by [ADR-0009](adr/0009-spend-gate-honest-rescope.md)). Spend caps, pacing caps, deny-by-default routing, rate limits, pattern detection — **co-located with the tap + the provider call-control client inside the boundary**: the brain never holds provider credentials, the gate is unskippable on everything rutster mediates (which is everything the brain can do), and pacing/playout are enforced over media the core terminates ([ADR-0007](adr/0007-trunk-rented-transport.md)). Not a physical wire monopoly — deployments pair it with provider-side caps. This is what makes the boundary *auditable* on spend. |
| Secrets / KMS | 🦀 Core | Vault/KMS integration; no plaintext credentials in config. |
| Observability (OTel) | 🦀 Core | Distributed traces that follow a single call across services. |
| Event bus | ☁️ Service | **Valkey** ([ADR-0005](adr/0005-event-bus.md)): streams + consumer groups (CDR/CEL/analytics), pub/sub (presence/MWI/BLF), KV (state store). Pluggable to NATS later at a config seam. The 20ms media loop never rides the bus. |
| SBOM / supply chain | — | cargo-deny, SBOM generation, reproducible builds. |
| Fuzzing harness | — | Continuous fuzzing of every wire parser (SIP/SDP/RTP). |
---
## Phasing → the thin-slice first proof + capability ladder
The old Phase 04 capability-rolldown is **replaced** (see [vision-revision §1011](superpowers/specs/2026-06-26-vision-revision-design.md)) by a **brutally thin vertical slice** that proves the *combination* (the wedge) first, then a **capability ladder** that grows the contact center feature by feature. The spearhead proves the hardest, most-differentiating part before trunk integration piles on.
### The thin-slice first proof (spearhead)
1. **WebRTC media loopback** — terminate RTP/SRTP, echo audio to a browser. Proves the media core.
2. **Add the tap** — route audio to an external echo process and back. Proves the tap interface.
3. **Swap echo for the brain** — ideally a single speech-to-speech API (e.g. OpenAI Realtime) to collapse STT+LLM+TTS into one integration. Proves agent integration.
4. **Add barge-in** — VAD-driven playout kill. Proves the reflex.
- **4½ — Benchmark + simulation harness** ([ADR-0010](adr/0010-spearhead-benchmark-sim-harness.md)):
synthetic-caller ingress drives p50/p99 mouth-to-ear + barge-in kill-time under concurrent
load, CI-regressed. Proves the reflex *measurably*; seeds `rutster-sim`.
5. **Add a real phone number via rented transport** — a CPaaS raw-media fork (e.g. Twilio Media Streams) lands a PSTN call on the reflex loop; no first-party SIP ([ADR-0007](adr/0007-trunk-rented-transport.md)).
6. **Add the spend cap** — hard-stop at threshold. Proves the boundary.
Steps 14 *are* the reflex loop. Keep the dev loop on WebRTC ingress until step 5.
Post-review re-sequencing ([ADR-0010](adr/0010-spearhead-benchmark-sim-harness.md)): after
step 4½, **rung-2 escalation is pulled forward ahead of steps 56**.
### The capability ladder (the grand vision, incrementally)
| Rung | Capability | Reuses |
|---|---|---|
| 1 | **Self-serve** — AI answers, contains the call | the thin-slice first proof |
| 2 | **Escalation** — human agent barges in / takes over when AI breaks down | the audiohook/barge primitive (§5) |
| 3 | **Measurement** — containment rate, where/why AI failed | CDR + analytics on calls *you own* |
| 4 | **Self-improvement** — every takeover → auto-labeled training data → loop | rungs 13 compounding |
## Open decisions
- ~~**SIP:** FFI pjproject vs. SBC front vs. pure-Rust.~~ ~~Decided — ADR-0003: Rust-native trunk SIP.~~ **Re-decided — [ADR-0007](adr/0007-trunk-rented-transport.md):** rutster owns no SIP stack; the trunk is rented transport (CPaaS raw-media fork) or an out-of-tree SBC.
- ~~**License**~~ **Decided — ADR-0004:** GPL-3.0-or-later.
- ~~**Event bus:** NATS vs. Kafka vs. Redis Streams.~~ **Decided — ADR-0005:** Valkey.
- ~~**Ingress:** WebRTC vs. inbound SIP endpoint.~~ **Decided — ADR-0006:** WebRTC-first; SIP endpoint deferred.
- **WASM runtime** — `wasmtime` component model, *if* WASM is retained at all (demoted to candidate per [ADR-0002](adr/0002-north-star-and-fused-core.md)). Uncoupled from the critical path.
- **Plugin mechanism** — WASM vs. webhooks vs. scripting for community call-flow/routing extensions. Deferred.
- **Agent-tap protocol** — presumptively WSS + core-as-client + clean PCM + core-authoritative playout (see ARCHITECTURE.md, "Agent tap"). To harden against the thin-slice first proof, *not* a decided ADR yet.