Builds out the user-facing docs tree alongside the slice-1 build target. Kept the implementer's planned Task 7 'Slice 1 dev loop' README section untouched — these docs are the canonical destination for that pointer. - docs/QUICKSTART.md: 5-min path to 'hear the echo' (libopus install, cargo run, browser steps, troubleshooting, what's happening under the hood). - docs/DEVELOPMENT.md: dev loop — workspace layout, per-crate iteration, running tests, the 20 ms loop / 'drop + observe' rule, slice-1 boundaries (what NOT to add yet). - CONTRIBUTING.md (at repo root, conventional): trunk-based dev, CI gates, commit message style, atomic commits, code style + learner-facing documentation policy, terminology policy, PR workflow + review checklist, GPL-3.0-or-later license. - README.md: add a Quickstart pointer at the top, a Documentation table linking to every doc, and the slice-1 build-target status block.
3.9 KiB
Quickstart
Get Rutster running and hear your own voice echoed back in under 5 minutes.
Status: Slice 1 (WebRTC media loopback) is the active build target. If the workspace isn't on
mainyet, check theslice-1-webrtc-loopbackbranch — that's where the implementation is landing task-by-task. Seedocs/superpowers/specs/2026-06-28-slice-1-webrtc-loopback-design.mdfor the full design.
Prerequisites
1. Rust toolchain
Install via rustup:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
The repo pins a specific stable channel in rust-toolchain.toml — rustup
will pick it up automatically on first cargo invocation. No manual
toolchain selection needed.
2. libopus (FFI dependency)
The opus crate links system libopus via FFI (per PORT_PLAN §7's
"🦀 Core (FFI)" disposition — Opus is the codec surface Rust doesn't need
to re-implement). Install the dev headers:
| Platform | Command |
|---|---|
| Debian/Ubuntu | sudo apt-get install -y libopus-dev |
| Fedora | sudo dnf install -y opus-devel |
| Arch | sudo pacman -S opus |
| macOS (Homebrew) | brew install opus |
Verify: pkg-config --cflags opus should print a path with no error.
That's the only system dependency in slice 1. Everything else is pure Rust from crates.io.
Run the server
cargo run
# listening on http://0.0.0.0:8080
First build takes ~2 minutes (str0m + axum + tokio compile fresh). Subsequent builds are incremental.
Hear the echo
- Open a browser to http://localhost:8080/.
- Click Start call.
- Grant microphone permission when the browser prompts.
- Speak — you should hear yourself back within ~200 ms (no perceptible delay).
- Click Hang up to tear down. The server logs
Closing → Closedfor the session.
Verbose tracing for debugging:
RUST_LOG=rutster=debug cargo run
Troubleshooting
| Symptom | Likely cause / fix |
|---|---|
error: linking with cc failed / could not find opus |
libopus dev headers not installed. Re-run the install command above. |
| Browser shows no mic prompt | Another tab/app holding the mic, or mic permissions disabled for localhost. Check browser settings. |
ICE connection failed in the browser |
Shouldn't happen on loopback (host candidates only). If it does, check the server console for the str0m error. |
| Click Start call, nothing happens | Open the browser console (F12). The page logs ICE state + connection state to a <pre> element. Look for the failure there. |
| Port 8080 already in use | Another process holding the port. Either stop it or edit crates/rutster/src/main.rs to bind a different port. |
The browser test page at GET / is a single self-contained HTML file
with inline JS — no build step. View source to see exactly what the
client side is doing.
What's happening
When you click "Start call":
- Browser captures microphone audio via
getUserMedia. - Browser creates an
RTCPeerConnectionand generates an SDP offer (audio-only, Opus codec). - Browser POSTs the offer to
POST /v1/sessions/:id/offer. - The Rutster core (built on
str0m, a sans-IO WebRTC implementation) accepts the offer, generates an SDP answer with its DTLS fingerprint + ICE credentials. - Browser sets the answer as remote description; ICE + DTLS handshake completes.
- RTP starts flowing: browser → core terminates DTLS-SRTP → decodes Opus to 16-bit PCM @ 24 kHz mono → echoes PCM back → re-encodes to Opus → DTLS-SRTP → browser plays it.
The "codec-to-PCM boundary" is the canonical point where, in a future slice, the audio tap for an external AI brain splices in. Slice 1 just echoes; step 2 of the spearhead swaps the echo for a real tap.
For the why, see ARCHITECTURE.md. For the dev loop,
see DEVELOPMENT.md.