AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
RTP media routing server in Rust
Translation pending
Developer toolsRust · TypeScriptGitHub @zyno-ioOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
High-performance RTP media routing server in Rust. Routes audio between WebRTC, RTP/SRTP, and file playback endpoints with codec transcoding (PCMU, G.722, Opus), DTMF detection/injection, PCAP recording, and voice activity detection. Controlled via WebSocket JSON API. Designed for telephony infrastructure and SIP integration.
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
A high-performance RTP media routing server in Rust, loosely inspired by rtpengine.
rtpbridge sits between VoIP endpoints, routing audio between them with support for WebRTC, plain RTP/SRTP, codec transcoding, DTMF, recording, voice activity detection, and file playback. It is controlled via a WebSocket JSON interface where each connection maps 1:1 to a media session.Read the full README ↗A high-performance RTP media routing server in Rust, loosely inspired by rtpengine.
rtpbridge sits between VoIP endpoints, routing audio between them with support for WebRTC, plain RTP/SRTP, codec transcoding, DTMF, recording, voice activity detection, and file playback. It is controlled via a WebSocket JSON interface where each connection maps 1:1 to a media session.
endpoint.transfer)session.bridge)session.attach), and empty session auto-destroy# Requires Rust 1.94+, libopus headers, a C compiler, make, Perl and curl
sh scripts/build-openssl.sh target/openssl
export OPENSSL_DIR="$PWD/target/openssl" OPENSSL_STATIC=1
cargo build --release
# Default: WebSocket on 127.0.0.1:9100, media on 127.0.0.1
./target/release/rtpbridge --listen 127.0.0.1:9100 --media-ip 203.0.113.5
# With config file
./target/release/rtpbridge --config rtpbridge.toml
Non-loopback control listeners require TLS and HMAC configuration. A trusted TLS proxy may use allow_plaintext_control = true on its protected upstream; unauthenticated remote control requires the separate explicit development exception allow_unauthenticated_control = true. The HMAC key authorizes all sessions.
Connect a WebSocket client and send JSON messages:
# Using websocat
websocat ws://localhost:9100
{"id":"1","method":"session.create","params":{}}
| Argument | Default | Description |
|---|---|---|
--listen | 127.0.0.1:9100 | WebSocket/HTTP control plane address(es), comma-separated |
--media-ip | 127.0.0.1 | IP for all media sockets (RTP/WebRTC) |
--config | — | Path to TOML config file |
--log-level | info | Log level (trace/debug/info/warn/error) |
listen = "127.0.0.1:9100"
media_ip = "203.0.113.5" # or "203.0.113.5, 2001:db8::5" for dual-stack
rtp_port_range = [30000, 39999]
# Optional WebRTC UDP range override (inclusive); defaults to rtp_port_range.
# webrtc_port_range = [49152, 65535]
disconnect_timeout_secs = 30
shutdown_max_wait_secs = 300
max_sessions = 10000
max_endpoints_per_session = 20
max_recordings_per_session = 100
session_idle_timeout_secs = 0 # 0 = disabled
recording_dir = "/var/lib/rtpbridge/recordings"
media_dir = "/var/lib/rtpbridge/media"
cache_dir = "/tmp/rtpbridge-cache"
cache_cleanup_interval_secs = 300
ws_ping_interval_secs = 30
log_level = "info"
# See rtpbridge.toml.example for all available options
Set [tls] to serve HTTPS/WSS on the existing listen port; rtpbridge does
not mix plaintext and TLS on that port. Set auth_hmac_secret_file to require
short-lived HMAC-SHA256 signatures for control WebSocket upgrades and sensitive
HTTP routes. /audio/<connect_token> remains a separate single-use audio
capability, so AI/media consumers do not receive the control signing key. See
the configuration guide
for the exact configuration and wire format.
All communication uses JSON over WebSocket. Each connection is bound to exactly one session.
The control API is intended for backend clients. Without HMAC, privileged requests carrying a browser Origin are rejected, and loopback clients must use a loopback IP or localhost in Host. This protects local development listeners from cross-site requests and DNS rebinding. Browser audio uses the separate /audio/<connect_token> capability.
| Method | Description |
|---|---|
session.create | Create a new session |
session.attach | Reattach to an orphaned session |
session.destroy | Destroy the current session |
session.info | Get session details |
session.list | List all sessions on the server |
server.info | Get server hostname and configured media IP |
endpoint.create_from_offer | Create endpoint from remote SDP offer (auto-detects WebRTC vs RTP) |
endpoint.create_offer | Create a new endpoint and generate SDP offer |
endpoint.accept_answer | Accept remote SDP answer |
endpoint.accept_offer | Accept remote SDP offer for an existing endpoint |
endpoint.create_with_file | Create file playback endpoint |
endpoint.create_tone | Create generated tone endpoint |
endpoint.create_websocket | Create raw PCM WebSocket audio endpoint |
endpoint.remove | Remove an endpoint |
endpoint.ice_restart | ICE restart (WebRTC only) |
endpoint.update_direction | Override or restore endpoint routing direction |
endpoint.update_remote_sdp | Update remote RTP address/SRTP state without codec changes |
endpoint.file.seek | Seek file playback position |
endpoint.file.pause | Pause file playback |
endpoint.file.resume | Resume file playback |
endpoint.dtmf.inject | Inject DTMF digit into endpoint |
endpoint.dtmf.set_sensitive | Redact DTMF logs, tag control events, and omit DTMF packets from recordings for an endpoint |
endpoint.srtp_rekey |
recording.start, recording.stop, endpoint removal, and the file-playback lifecycle events carry
media-host epoch milliseconds. These use the same host clock as PCAP packet records, so callers
can safely align explicitly injected media to a recording without comparing control-plane clocks.
endpoint.file.started is emitted only immediately before the first file RTP is routed and carries
that same epoch; ledger consumers must use it rather than endpoint creation, which can remain buffering.
pcap2audio --metadata <path> writes its earliest decodable-RTP sample-zero epoch and rendered WAV
duration as JSON for conversion services.
| Event | Description |
|---|---|
dtmf | DTMF digit detected from remote endpoint |
endpoint.state_changed | Endpoint state transition |
endpoint.ice_state_changed | WebRTC ICE state transition |
endpoint.file.started | First file RTP packet entered media routing |
endpoint.file.finished | File playback completed |
endpoint.tone.finished | Tone generation completed |
endpoint.ws.connected | WebSocket audio socket attached |
endpoint.ws.disconnected | WebSocket audio socket closed |
endpoint.ws.connect_timeout | WebSocket audio endpoint was not dialed in before timeout |
endpoint.media_timeout | No media received from remote endpoint |
endpoint.rtcp_bye | RTCP BYE received from remote endpoint |
endpoint.transferred_out | Endpoint transferred to another session |
endpoint.transferred_in | Endpoint transferred in from another session |
events.dropped | Events dropped due to client backpressure |
recording.stopped | Recording stopped externally |
session.idle_timeout | Session destroyed due to inactivity |
session.empty_timeout | Session destroyed due to zero endpoints timeout |
session.orphaned | Control connection dropped, timeout running |
stats | Periodic session statistics |
vad.speech_started | Speech detected after silence |
vad.silence | Periodic silence notification |
vad.error | VAD analysis decoder could not be created |
fax.cng_detected | Fax calling tone (CNG, 1100 Hz) detected |
See Events Reference for detailed payload schemas.
rtpbridge also serves HTTP endpoints on the same listen address(es). See Configuration — HTTP REST API for full details including status codes and pagination.
| Endpoint | Method | Description |
|---|---|---|
/health | GET | Health check — returns {"status":"ok"} |
/metrics | GET | Prometheus-format metrics |
/sessions | GET | List all active sessions |
/sessions/{id} | GET | Get session details |
/recordings | GET | List PCAP recording files |
/recordings/{path} | GET | Download a recording file |
/recordings/{path} | DELETE | Delete a recording file |
WebSocket Control (JSON)
|
+------v------+
| SessionMgr | DashMap<SessionId, Session>
+------+------+
|
+------------v------------+
| Session Task | (one tokio task per session)
| |
| +--------+ +--------+ |
| | WebRTC | | RTP/ | |
| | str0m | | SRTP | | +--------+ +----------+
| +---+----+ +---+----+ | | File | | Recording|
| | | | |Playback | | (PCAP) |
| +----+-----+ | +----+----+ +----------+
| | | |
| Routing Table | |
| (sendrecv/recvonly/ | |
| sendonly directions) | |
+-------------------------+-------+
|
+------------v------------+
| Per-Endpoint UDP |
| Sockets |
+-------------------------+
# Unit tests
cargo test
# Integration tests (serial — spawns server processes)
cargo test -- --test-threads=1
MIT
| Initiate SRTP rekey (plain RTP/SRTP only) |
endpoint.transfer | Transfer an endpoint to a different session |
session.bridge | Bidirectional audio bridge between two sessions |
recording.start | Start recording (full session or single leg) |
recording.stop | Stop recording |
vad.start | Start voice activity detection on endpoint |
vad.stop | Stop voice activity detection |
fax_detect.start | Start fax tone detection (CNG/CED) on endpoint |
fax_detect.stop | Stop fax tone detection |
stats.subscribe | Subscribe to periodic session statistics |
stats.unsubscribe | Unsubscribe from statistics |
fax.ced_detected | Fax/modem answer tone (CED, 2100 Hz) detected |
fax.error | Fax analysis decoder could not be created |