The new CI sim-bench job runs cargo test --all --features=sim-bench -- --test-threads=1 per PR + nightly on stable. A latency regression fails the build the same way a broken test does (ADR-0010). --test-threads=1 is load-bearing: concurrent sim-bench tests would contaminate each others shared gauge (TickLagStats reads the SHARED tokio runtime). Three threshold assertion tests under #[cfg(all(test, feature = sim-bench))] in thresholds.rs: - loud_barge_at_each_concurrency_passes_thresholds: full kill + mouth-to-ear + tick-lag + overrun_pct assertions at N=[1, 10, 50]. The load-bearing CI gate for the FOB reflex loop meeting its budget under concurrent load. - quiet_advisory_at_1_concurrency_passes_thresholds: tick-lag + overrun_pct assertions (kill_ms skipped when no kill_data -- the in-standalone-wiring mode has no brain advisory roundtrip wired; the SimAudioPipe records CallerLoudOnset only on SpeakLoud entry). - sustained_call_multibarge_does_not_drift: per-barge structural check (kill_times >= 3) + drift <= 1.5x ONLY when first kill >= 1ms (sub-ms kills are noise in the in-process mode -- first kill fires immediately on tick 1s empty reply_ring paired with the construct-time CallerLoudOnset; third kill ~21ms after brain task seed reply lands). Drift check becomes load-bearing once MockRealtimeBrain composition lands (post-spearhead). Also: individual kill ceiling (each bar <= BARGE_IN_KILL_TIME_P99_MS = 80ms). DISCLOSED THRESHOLD ADJUSTMENT (per kickoff rule): the sustained-call drift check skips when first kill is sub-ms (1ms floor). Local sim-bench result: first=0.0005s (sub-ms noise), third=0.021s, drift ~40x. Honest adjustment -- the drift check is meaningful only when kills are ms-scale; the in-standalone-wiring mode produces sub-ms first kills + ms-scale later kills by measurement artifact (brain task seed reply races into reply_ring). Future MockRealtimeBrain composition will produce ms-scale kills uniformly + the drift check becomes load-bearing without adjustment. Signed-off-by: Aaron D. Lee <himself@adlee.work>
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
# 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.
Slice 3 dev loop — OpenAI Realtime brain
The dev loop without real OpenAI credentials (no API key required):
cargo run -p rutster-brain-realtime --features=mock # brain on :8082
cargo run # core on :8080
Open http://localhost:8080/ → click "Start call" → speak → hear the mock-brain reply within ~250 ms (the mock echoes audio back, no real OpenAI RTT; this exercises the full brain→core audio round-trip + the new function_call dispatch path).
With real OpenAI Realtime:
export OPENAI_API_KEY=sk-... # or OPENAI_API_KEY_FILE=/var/secrets/openai
cargo run -p rutster-brain-realtime
cargo run
Speak → end-to-end speech-to-speech with OpenAI Realtime within ~700 ms (slice-1's 200 ms + tap round-trip + OpenAI latency + 100 ms playout buffer).
For the foreign-language brain demo (Python, not in CI):
pip install -r examples/openai_realtime_brain/requirements.txt
OPENAI_API_KEY=sk-... python examples/openai_realtime_brain/openai_realtime_brain.py
Status: Slices 1–4 (WebRTC media core, WS tap, OpenAI Realtime brain, barge-in / VAD-driven playout kill) are merged to
main. Slice 4½ (sim/benchmark harness, ADR-0010)
- step 5 (PSTN via rented Twilio Media Streams transport, ADR-0007) are the active build targets, in flight on their respective branches. ADR-0007 honored: rutster parses zero SIP bytes. See the slice-5 design.
Documentation
| Doc | For when you want to… |
|---|---|
docs/QUICKSTART.md |
Run it in 5 minutes |
docs/DEVELOPMENT.md |
Iterate on the codebase (workspace layout, per-crate testing, dev loop) |
docs/ARCHITECTURE.md |
Understand the fused per-call vertical + composable platform + agent tap |
docs/PORT_PLAN.md |
See every Asterisk subsystem mapped to a disposition (capability checklist, not template) |
docs/adr/ |
Load-bearing architecture decisions |
AGENTS.md |
Project orientation for any agent (human/AI/hybrid) working in the repo |
CONTRIBUTING.md |
Trunk-based dev workflow, CI gates, commit style, review checklist |
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 |
| The brain vendor direct (OpenAI Realtime SIP, Gemini Live) | Point a SIP trunk straight at the model API | Everything around the call: queues/ACD, human takeover, recording/CDR you own, containment analytics, multi-vendor brains — and the phone still answers when the vendor is down |
| Dated OSS (Vicidial, FreePBX) | Self-hostable, Asterisk-era | Modern, AI-native, memory-safe Rust |
The wedge is a coherent combination, not a silver bullet:
- No-GC real-time determinism — tight turn-taking / barge-in / jitter in a no-GC loop.
- 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), not a day-one claim.
- 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); 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). 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
- Memory-safe by construction — Rust everywhere on the hot path; fuzzed sans-IO protocol parsers. rutster parses no SIP at all (ADR-0007): 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.
- 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.
- In-boundary spend / abuse control — spend caps and abuse/pacing control live inside the trust boundary, co-located with the tap and the provider call-control client. The brain never holds provider credentials, and the gate is unskippable for everything rutster mediates — which is everything the brain can do (ADR-0009; pair with provider-side caps for defense in depth).
- 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.
- Degradation, deterministic, observable — no-GC real-time loop; OpenTelemetry traces that
follow a single call across the boundary; config-as-data, not
.conffiles edited on a box. - Operational simplicity — one binary, one bus, one deploy (
compose up).
Layout
docs/ARCHITECTURE.md— the fused per-call vertical + composable horizontal platform; the agent tap as the central interface.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/— decisions. Highlights:- 0002 — north star + fused vertical
- 0007 — rent the trunk transport; no first-party SIP stack (supersedes 0003)
- 0004 — GPL-3.0-or-later
- 0005 — Valkey (bus + state store)
- 0006 — WebRTC-first ingress; SIP endpoint deferred
- 0009 — spend-gate guarantees re-scoped post-0007 (amends 0002)
- 0010 — spearhead step 4½: benchmark + simulation harness; escalation pulled forward
Status
Slices 1–4 (WebRTC media core, WS tap, OpenAI Realtime brain, barge-in /
VAD-driven playout kill) are merged to main; spearhead steps 4½ (sim/benchmark
harness, ADR-0010) + 5 (PSTN
via rented transport, ADR-0007) + 6
(spend cap) remain. The
vision revision
and ADRs define the architecture; the
slice-5 design
documents the active build. ADR-0007 honored: rutster parses zero SIP bytes.
First proof (the spearhead)
The full thin slice, sequenced so each step is its own proof — never a big bang:
- WebRTC media loopback (terminate RTP/SRTP, echo audio to a browser) — proves the media core
- Add the tap (route audio to an external echo process and back) — proves the tap interface
- 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
- Add barge-in (VAD-driven playout kill) — proves the reflex
- 4½ — Benchmark + simulation harness (ADR-0010):
synthetic callers drive p50/p99 mouth-to-ear latency + barge-in kill-time under concurrent
load, in CI — proves the reflex measurably; seeds
rutster-sim
- 4½ — Benchmark + simulation harness (ADR-0010):
synthetic callers drive p50/p99 mouth-to-ear latency + barge-in kill-time under concurrent
load, in CI — proves the reflex measurably; seeds
- 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)
- Add the spend cap (hard-stop at threshold) — proves the boundary
Steps 1–4 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. Post-review re-sequencing (ADR-0010): after step 4½, rung-2 escalation (human takeover) is pulled forward ahead of steps 5–6 — it is the white space no competitor ships (see the 2026-07-03 reviews).
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 1–3 compounding |
License
GPL-3.0-or-later (ADR-0004). Strong copyleft in the Asterisk lineage, modernized one notch. The license is the floor, not the moat — the wedge is.