opencode controller 42060b68e3
Some checks failed
CI / fmt (pull_request) Failing after 58s
CI / clippy (pull_request) Failing after 33s
CI / test (1.85) (pull_request) Failing after 32s
CI / test (stable) (pull_request) Failing after 47s
CI / deny (pull_request) Failing after 41s
spec(ui): default operator console design (later-rung, not yet scheduled)
Design record for the operator console — the downstream non-technical
operator's surface. **Not scheduled for current build;** lands after the
spearhead (steps 1-6) and the ACD/escalation rungs (capability ladder
rungs 1-2). The current build target is slice-2; the next build target
after slice-2 is slice-3 (the OpenAI Realtime adapter). An agent that
proposes building the console now is wrong — point it at spec §0.

Records the visual-brainstorming session's resolved decisions so they
don't get re-litigated at implementation time:

§1 Principles (load-bearing):
- GUI is a pure API client — **green-zone, never an insider** (ADR-0008);
  holds no privilege a third party couldn't get from the public API.
- Config-as-code is the single source of truth; the UI is a reconciled
  *view* (the k8s model — declarative desired-state in git, the engine
  reconciles; console + CLI + git are views over it).
- Operator persona, not builder. The builder authors flows in **code +
  an AI pair** (ARCHITECTURE 'DX spine'); this console is NOT the
  authoring surface for the technical builder. **No drag-drop authoring
  canvas** — a deliberately-closed decision (vision-revision §8).
- Ships in the batteries-included distro and the all-in-one image
  (the reference GUI is bundled — 'boom' includes a usable console).

§2 IA — Ops Console shell: left nav + center panel + persistent live rail.
Dense single-screen; for an operator who lives in it. Live rail always
visible regardless of the selected section.

§3 Dashboards — Live Ops (default landing) + Quality (sibling view). Maps
the k8s split: Live Ops = live operational state; Quality = the data-owned
eval loop (capability ladder rungs 3→4).

§4 Flows editor — master/detail + a generated, navigable preview map.
The preview map is a *view* rendered from the config, not a drag-drop
authoring canvas. Earns its place by showing branching (the escalation
split) the linear stage-list flattens.

§5 Declarative flow schema (illustrative YAML; format is open). The config
the console edits via the API; the builder edits in git; the engine
reconciles. The map is generated from this structure.

§6 Other sections (conventional): Agents · Numbers · Queues · People ·
Recordings · Settings.

§7 Architecture: separate app, public API only; never reaches into the FOB.
Config writes go through the reconcile path. Live data rides the event
stream off Valkey (ADR-0005); never the 20 ms media loop.

§8 v1 vs. later scoping. §9 open decisions (flow config format, map
rendering depth, quality metrics, live transport, framework).

Cross-refs ADR-0002 (fused vertical + composable platform), ADR-0006
(WebRTC ingress / escalation UX), ADR-0007 (rented trunk / Numbers
config), ADR-0008 (FOB / green-zone — the UI is green-zone),
ARCHITECTURE.md 'DX spine' + 'GUI & extension architecture', PORT_PLAN §5
(contact-center domain).
2026-06-29 20:27:25 -04:00

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.

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. Implementation plan: docs/superpowers/plans/2026-06-28-slice-1-webrtc-loopback.md.

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
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), 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); 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

  1. 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.
  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 — 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

Status

Slice 1 (WebRTC media loopback) implemented; spearhead steps 26 pending. The vision revision and ADRs define the architecture; the slice-1 design 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)
  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). Strong copyleft in the Asterisk lineage, modernized one notch. The license is the floor, not the moat — the wedge is.

Description
Terribly-named "spiritual" ancestor to the niche Asterisk PBX filled in 2005, but for AI-first call centers in 2026
Readme 4.8 MiB
Languages
Rust 95.1%
Python 2.6%
Dockerfile 1.3%
Shell 0.6%
HTML 0.4%