Co-authored-by: Aaron D. Lee <himself@adlee.work> Co-committed-by: Aaron D. Lee <himself@adlee.work>
117 lines
4.0 KiB
Markdown
117 lines
4.0 KiB
Markdown
# Quickstart
|
||
|
||
Get Rutster running and hear your own voice echoed back in under 5 minutes.
|
||
|
||
> **Status:** Slices 1–3 are merged to `main`. Slice 4 (barge-in / VAD-driven playout
|
||
> kill) is the active build target, in flight on the `slice-4-dev-a-reflex` +
|
||
> `slice-4-dev-b-tap` branches. This quickstart exercises the slice-1 WebRTC media
|
||
> loopback, which remains the simplest end-to-end demo on `main`. See
|
||
> [`docs/superpowers/specs/2026-07-01-slice-4-barge-in-design.md`](superpowers/specs/2026-07-01-slice-4-barge-in-design.md)
|
||
> for the active build target's design.
|
||
|
||
---
|
||
|
||
## Prerequisites
|
||
|
||
### 1. Rust toolchain
|
||
|
||
Install via [rustup](https://rustup.rs/):
|
||
|
||
```bash
|
||
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
|
||
|
||
```bash
|
||
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
|
||
|
||
1. Open a browser to <http://localhost:8080/>.
|
||
2. Click **Start call**.
|
||
3. Grant microphone permission when the browser prompts.
|
||
4. Speak — you should hear yourself back within ~200 ms
|
||
(no perceptible delay).
|
||
5. Click **Hang up** to tear down. The server logs
|
||
`Closing → Closed` for the session.
|
||
|
||
Verbose tracing for debugging:
|
||
|
||
```bash
|
||
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 | Set `RUTSTER_HTTP_BIND`, e.g. `RUTSTER_HTTP_BIND=0.0.0.0:8090 cargo run -p rutster` |
|
||
|
||
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":
|
||
|
||
1. Browser captures microphone audio via `getUserMedia`.
|
||
2. Browser creates an `RTCPeerConnection` and generates an SDP offer
|
||
(audio-only, Opus codec).
|
||
3. Browser POSTs the offer to `POST /v1/sessions/:id/offer`.
|
||
4. The Rutster core (built on [`str0m`](https://docs.rs/str0m), a sans-IO
|
||
WebRTC implementation) accepts the offer, generates an SDP answer with
|
||
its DTLS fingerprint + ICE credentials.
|
||
5. Browser sets the answer as remote description; ICE + DTLS handshake
|
||
completes.
|
||
6. 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`](ARCHITECTURE.md). For the dev loop,
|
||
see [`DEVELOPMENT.md`](DEVELOPMENT.md).
|