opencode controller f83bca98db
Some checks failed
CI / fmt (push) Failing after 41s
CI / clippy (push) Failing after 45s
CI / test (1.85) (push) Failing after 36s
CI / test (stable) (push) Failing after 39s
CI / deny (push) Failing after 31s
spec(slice-2): adversarial review patches
Patches the agent-tap design with seven findings from PM-side review
before the implementation plan gets written. Resolves invariant-breakers
and deferred-decision debt that would compound across steps 3-6.

- P1 (high): §8.5 #6 'byte-identical loop_driver.rs' was the wrong bar
  for the seam test. Reworded: the trait-method *call sites* are
  unchanged; impl bodies are free to differ. That's what 'the seam
  held' actually means — the contract is the test.
- P2 (high): tap: Option<TapHandle> and ChannelState were parallel
  state machines without an invariant tying them. Added an explicit
  state-pair table: tap is Some iff state == Connected. Lookups tolerate
  transient inconsistency; the source-of-truth is the pair, not either
  field alone.
- P3 (medium): v1 wire format was host-endian (silent big-endian
  hazard on paper). Nailed LE explicitly via i16::to_le_bytes /
  i16::from_le_bytes. v2 can negotiate endianness if a big-endian
  brain ever materializes; v1 contract is LE-only. Added a §3.4
  byte-order invariant alongside the sample-count invariant.
- P4 (medium): wss:// URLs were accepted by schema but returned
  501 at connect time (UX: 2 s into a call). Now rejected at
  POST /v1/sessions with 400 + clear message — fail fast at
  session-create. Updated §4.4, §7.1, §7.3, §9, and the decisions
  table for consistency; removed all stale '501 at connect' claims.
- P5 (low): TapEngine spawn owner was unspecified. Added explicit
  ownership: session_map::drive_all_sessions (the binary's poll
  task) observes the Connected transition from run_poll_once and
  spawns the engine. Keeps loop_driver.rs behaviorally unchanged
  (the §8.5 #6 seam test still holds).
- P6 (low): 'two new deps' in §1.1 undercounted. Updated list:
  tokio-tungstenite, futures-util, url, serde_json (if not already
  pulled).
- P7 (low): ADR-0007 was 'post-implementation follow-up, not a
  slice-2 deliverable.' Tightened: strongly recommended to land
  alongside or immediately after slice-2 implementation — once
  step 3 ships against an unpinned protocol, the wire shape will
  silently drift. The spec lists the decisions ADR-0007 should
  capture (LE byte order added to the list).
2026-06-28 13:41:54 -04:00
2026-06-28 09:34:04 -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 — trunk termination + media + spend/abuse control + audit in a single memory-safe trust domain. One thing to certify (strongest for PCI / HIPAA / TCPA).
  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 (trunk SIP termination + RTP/SRTP media
    • local real-time reflexes + a clean audio tap to an external brain + in-boundary spend/abuse control); 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. No DAHDI, no Sangoma/Digium cards, no ISDN/SS7, no IAX2/H.323/SCCP/MGCP/Unistim. PSTN reach is via SIP trunks only. Inbound SIP endpoint registration (desk phones) is deferred to an out-of-tree edge adapter — 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. Hostile bytes hit a fuzzed Rust parser first. Eliminates the buffer-overflow/RCE CVE class — and, with the C SBC edge dropped, this is literally true at the wire.
  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
    • 0003 — Rust-native trunk SIP (no SBC shield)
    • 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. Replace WebRTC ingress with a real PSTN trunk call — proves the trunk client
  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 3.8 MiB
Languages
Rust 99.5%
HTML 0.5%