Files
rutster/README.md
opencode controller 7616878bb1 docs: propagate ADR-0007/0008 to README, ARCHITECTURE, PORT_PLAN, AGENTS
ADR-0007 (rent the trunk) + ADR-0008 (FOB/green-zone doctrine) propagation
into the narrative docs that orient contributors + readers. No code changes.

README:
- Wedge bullet 2: 'one secure auditable boundary' now lists media + local
  reflexes + spend + tap + audit (was: trunk termination + media + spend).
  Adds the honest caveat that PSTN media inside the boundary is the on-prem
  *graduation* (ADR-0007), not a day-one claim.
- 'Is / isn't': 'isn't a TDM/PSTN-hardware PBX — *and not a SIP stack.*'
  No first-party SIP (ADR-0007). PSTN reach is rented transport.
- Memory-safety pillar: 'rutster parses no SIP at all' — entire first-party
  wire surface is WebRTC/RTP/SRTP + WebSocket tap/ingress, all memory-safe
  Rust. The carrier-SIP interop tail lives outside the trust boundary.
- Spearhead step 5: 'Add a real phone number via rented transport' (was:
  'Replace WebRTC ingress with a real PSTN trunk call'). Re-aims at the
  AI-telephony frontier; no first-party SIP stack.
- References: 0007 supersedes 0003 in the highlighted ADR list.

ARCHITECTURE:
- New 'FOB and the green zone' section after the fused-vertical framing —
  names the build-vs-reuse doctrine with the FOB member list + green-zone
  member list, and restates memory-safety precisely ('FOB is 100%
  memory-safe Rust; the green zone is trusted OSS kept outside the
  boundary — not an over-claim that every byte is Rust').
- 'Inside the boundary': 'Carrier trunk — rented transport, not first-party'
  replaces 'Trunk SIP termination — Rust-native.' PSTN audio arrives as
  a media-leg ingress from a rented CPaaS raw-media fork or an out-of-tree
  SBC for on-prem sovereignty.
- Biggest technical risk: 'No longer the SIP stack — because rutster no
  longer builds one.' Retires ADR-0003's named schedule risk; redirects
  to 'the reflex loop itself' (turn-taking, VAD-driven barge-in, jitter,
  pacing) — which is also the differentiator.

PORT_PLAN:
- Design rule 4: 'rent it, don't own it' replaces ADR-0003's 'Rust-native
  trunk SIP, no SBC shield.' ADR-0007 restores the rule's original instinct.
- SIP signaling (trunk) row: disposition flipped to 🔌 Rented / out-of-tree.
- Outbound registration row: disposition flipped to 🔌 Rented / out-of-tree
  (handled by the rented transport or out-of-tree SBC; rutster parses no SIP).
- Spend/abuse engine row: 'co-located with call origination + the tap inside
  the boundary' (was: 'co-located with trunk termination'). rutster mediates
  both the provider call-control API and the brain tap — the brain never
  holds the wire.
- Spearhead step 5: 'Add a real phone number via rented transport.'
- Open decisions: SIP line updated to 'Re-decided — ADR-0007.'

AGENTS.md:
- ADR list: adds 0007 and 0008 with annotations; marks 0003 as superseded
  by 0007.
- Key decisions to respect: prepends the FOB/green-zone doctrine as the
  *the* build-vs-reuse rule. 'When in doubt, default to green zone — the
  FOB earns its members, it doesn't collect them. This is why the trunk is
  rented (ADR-0007) and Valkey reused (ADR-0005), not rebuilt. Don't pull
  green-zone plumbing into the core.'
2026-06-29 20:26:51 -04:00

162 lines
9.9 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
The open-source engine for building the **AI-era contact center** — self-hostable, AI-native,
memory-safe Rust. A spiritual successor to Asterisk's *place in the world*, not its protocols
or its architecture.
> Not a port of Asterisk. rutster inherits the role Asterisk held — *the self-hostable engine a
> technical builder uses to stand up a contact center* — and re-aims it at a category AI is
> actively disrupting, instead of a PBX category UCaaS already ate.
## Quickstart
```bash
# Prereqs: Rust (rustup), libopus dev headers (libopus-dev / opus-devel / brew install opus)
cargo run
# listening on http://0.0.0.0:8080
```
Open <http://localhost:8080/> → click "Start call" → grant mic → hear yourself echo.
Full walkthrough + troubleshooting: **[`docs/QUICKSTART.md`](docs/QUICKSTART.md)**.
> **Status:** Slice 1 (WebRTC media loopback) is the active build target. The workspace is
> landing task-by-task on the `slice-1-webrtc-loopback` branch. Design:
> [`docs/superpowers/specs/2026-06-28-slice-1-webrtc-loopback-design.md`](docs/superpowers/specs/2026-06-28-slice-1-webrtc-loopback-design.md).
> Implementation plan:
> [`docs/superpowers/plans/2026-06-28-slice-1-webrtc-loopback.md`](docs/superpowers/plans/2026-06-28-slice-1-webrtc-loopback.md).
## Documentation
| Doc | For when you want to… |
|---|---|
| [`docs/QUICKSTART.md`](docs/QUICKSTART.md) | Run it in 5 minutes |
| [`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md) | Iterate on the codebase (workspace layout, per-crate testing, dev loop) |
| [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) | Understand the fused per-call vertical + composable platform + agent tap |
| [`docs/PORT_PLAN.md`](docs/PORT_PLAN.md) | See every Asterisk subsystem mapped to a disposition (capability checklist, not template) |
| [`docs/adr/`](docs/adr/) | Load-bearing architecture decisions |
| [`AGENTS.md`](AGENTS.md) | Project orientation for any agent (human/AI/hybrid) working in the repo |
| [`CONTRIBUTING.md`](CONTRIBUTING.md) | Trunk-based dev workflow, CI gates, commit style, review checklist |
| [`LEARNING.md`](LEARNING.md) | Index of "to learn concept X, read file Y" (learner-facing codebase) |
## Why it exists
Asterisk won because contact centers were **built on it** (Vicidial, GOautodial, a thousand
integrator builds) — it never tried to *be* Five9. rutster inherits that position: it is a
**framework / engine, not a turnkey product**.
The white space no incumbent fills:
| Competitor | What they are | rutster's edge |
|---|---|---|
| **LiveKit** | Horizontal real-time media infra (Go) | rutster owns the **contact-center domain** (ACD, IVR, queues, recording, CDR, dialer, supervisor) LiveKit will never ship |
| **Cloud CCaaS** (Five9, Genesys, NICE, Amazon Connect, Twilio Flex) | Proprietary, AI bolted on | Self-hostable, AI-**native**, no per-seat/minute lock-in |
| **Cloud AI-voice** (Vapi, Retell, Bland) | Cheap managed voice bots | You own your **calls and training data**; it's a *contact center* (escalation, queues), not a single bot |
| **Dated OSS** (Vicidial, FreePBX) | Self-hostable, Asterisk-era | Modern, AI-native, memory-safe Rust |
The wedge is a **coherent combination, not a silver bullet**:
1. **No-GC real-time determinism** — tight turn-taking / barge-in / jitter in a no-GC loop.
2. **One secure auditable boundary** — media + local reflexes + spend/abuse control + the tap +
audit in a single memory-safe trust domain. One thing to certify (strongest for PCI / HIPAA / TCPA).
The carrier trunk is rented (or out-of-tree); bringing PSTN media *inside* the boundary is the
on-prem **graduation** ([ADR-0007](docs/adr/0007-trunk-rented-transport.md)), not a day-one claim.
3. **Operational simplicity** — one binary, one bill, one deploy.
**Honest caveat:** the agent *brain* (STT/LLM/TTS) is necessarily external — audio leaves the box
to reach it, same as LiveKit. The real-time edge therefore lives in the **local reflexes that don't
need the brain** (VAD killing TTS the instant the caller speaks, barge-in, jitter, pacing, DTMF),
not the brain round-trip. The moat is the *whole*.
## Who it's for
The modern equivalent of the 2006 Linux-nerd-who-stood-up-Asterisk-for-an-SMB: the
**CLI/IDE/AI-comfortable self-hosting technical builder** — runs Claude Code in a terminal, lives
in an editor with an AI pair, versions everything in git, self-hosts on principle. *Not* the no-code
admin clicking a flow-designer canvas. The builder serves the non-technical operator downstream,
exactly as integrators did on top of Asterisk.
## What it is / isn't
- **Is:** a Rust media core owning the **per-call vertical** (RTP/SRTP media termination + local real-time reflexes + a clean
audio tap to an external brain + in-boundary spend/abuse control; the carrier trunk is *rented
transport*, not first-party — [ADR-0007](docs/adr/0007-trunk-rented-transport.md)); a programmable call model exposed as a REST/gRPC API + event stream; WebRTC-first human
ingress; a library of contact-center capabilities (ACD, IVR, queues, recording, CDR, dialer,
supervisor) delivered as services around the core.
- **Isn't:** a TDM/PSTN-hardware PBX — *and not a SIP stack.* No DAHDI, no Sangoma/Digium cards, no
ISDN/SS7, no IAX2/H.323/SCCP/MGCP/Unistim — and **no first-party SIP**
([ADR-0007](docs/adr/0007-trunk-rented-transport.md)). PSTN reach is **rented transport** (a CPaaS
raw-media fork, or an out-of-tree SBC for on-prem media). Inbound SIP *endpoint* registration (desk
phones) likewise stays out-of-tree — not the browser/SSO UX rutster targets.
## Core design pillars
1. **Memory-safe by construction** — Rust everywhere on the hot path; fuzzed sans-IO protocol
parsers. rutster **parses no SIP at all** ([ADR-0007](docs/adr/0007-trunk-rented-transport.md)): its
entire first-party wire surface is WebRTC/RTP/SRTP + the WebSocket tap/ingress protocol — all
memory-safe Rust. The carrier-SIP interop tail lives *outside* the trust boundary (rented transport
or an out-of-tree SBC), so the buffer-overflow/RCE CVE class is designed out of rutster's own surface.
2. **Security-as-product** — the single auditable boundary *is* the moat. TLS/SRTP mandatory,
deny-by-default routing, built-in toll-fraud controls, mTLS gRPC admin (no plaintext AMI), hard
multi-tenancy. Compliance is a buying criterion, not a row.
3. **In-boundary spend / abuse control** — spend caps and abuse/pacing control live **inside the
trust boundary**, co-located with trunk termination. A runaway brain can't exceed pacing or
spend because it doesn't hold the wire — structurally impossible for a 3-vendor stack.
4. **Data ownership** — calls and training data never leave the operator's infra. The self-host
wedge *and* the fuel for the ML self-improvement loop.
5. **Degradation, deterministic, observable** — no-GC real-time loop; OpenTelemetry traces that
follow a single call across the boundary; config-as-data, not `.conf` files edited on a box.
6. **Operational simplicity** — one binary, one bus, one deploy (`compose up`).
## Layout
- [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — the fused per-call vertical + composable
horizontal platform; the agent tap as the central interface.
- [`docs/PORT_PLAN.md`](docs/PORT_PLAN.md) — the capability *checklist* (what a telephony system
must handle), *not* an architecture template. Every Asterisk subsystem mapped to a disposition
with rationale.
- [`docs/adr/`](docs/adr/) — decisions. Highlights:
- [0002](docs/adr/0002-north-star-and-fused-core.md) — north star + fused vertical
- [0007](docs/adr/0007-trunk-rented-transport.md) — rent the trunk transport; no first-party SIP stack (supersedes [0003](docs/adr/0003-sip-rust-native-trunk.md))
- [0004](docs/adr/0004-license.md) — GPL-3.0-or-later
- [0005](docs/adr/0005-event-bus.md) — Valkey (bus + state store)
- [0006](docs/adr/0006-ingress-posture.md) — WebRTC-first ingress; SIP endpoint deferred
## Status
Slice 1 (WebRTC media loopback) implemented; spearhead steps 26 pending. The
[vision revision](docs/superpowers/specs/2026-06-26-vision-revision-design.md)
and ADRs define the architecture; the
[slice-1 design](docs/superpowers/specs/2026-06-28-slice-1-webrtc-loopback-design.md)
documents the active build.
## First proof (the spearhead)
The full thin slice, sequenced so each step is its own proof — never a big bang:
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
5. **Add a real phone number via rented transport** (a CPaaS raw-media fork, e.g. Twilio Media
Streams) — proves a PSTN call reaches the reflex loop, no first-party SIP ([ADR-0007](docs/adr/0007-trunk-rented-transport.md))
6. **Add the spend cap** (hard-stop at threshold) — proves the boundary
Steps 14 *are* the reflex loop — the hard, most-differentiating part proves itself before trunk
integration piles on. "I called my Rust box and an AI answered the phone" is the momentum fuel a
solo multi-year build needs.
## 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 |
| 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 |
## License
**GPL-3.0-or-later** ([ADR-0004](docs/adr/0004-license.md)). Strong copyleft in the Asterisk lineage,
modernized one notch. The license is the *floor*, not the moat — the wedge is.