A standalone high-performance audio relay node for Discord Voice Gateway based on Rust and Tokio.
Allows for sending audio without it ever reaching any of your shards, with distributed routing and low-latency UDP relay. Being built with Rust for maximum performance and reliability. A basic example bot is available in this workspace.
Created with ❤️ by Nick Aidil 🌸
Note
SoundRelay v0.1.0 is now production-ready!
# Clone & Configure
git clone <repository-url> && cd SoundRelay-main
cp .env.example .env # Edit with your settings
# Run
cargo build --release && ./target/release/SoundRelay
# Verify
curl http://localhost:4300/health| Component | Tech | Purpose |
|---|---|---|
| Runtime | Tokio 1.40 | Async I/O |
| Storage | Redis 6.0+ | Routing table |
| Metrics | Prometheus | Observability |
| HTTP | Hyper 0.14 | Control API |
| Concurrency | DashMap 5.4 | Lock-free routing |
| Tracing | OpenTelemetry | Distributed traces |
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Discord Bot │───UDP───│ SoundRelay │───UDP───│ Discord │
│ (Source) │ │ Relay Node │ │ Gateway │
└──────────────┘ └──────────────┘ └──────────────┘
│
Redis/In-Memory
│
┌──────────────┐
│ Routing Table│
│ + Metrics │
└──────────────┘
Incoming UDP packet → Extract stream_id → Lookup route → Forward to target(s)
- Binds to
BIND_ADDR(default:0.0.0.0:4000) - Custom protocol header parsing
- Jitter buffer for packet reordering
- Metrics:
relay_packets_received_total, latency histogram
DashMap (lock-free) → Redis persistence → Route selection → Multi-target forwarding
| Operation | Latency | Persistence |
|---|---|---|
| Route lookup | <1μs | DashMap (memory) |
| Route create | ~1ms | Redis + DashMap |
| Route delete | ~1ms | Redis + DashMap |
| Fallback mode | <1μs | In-memory only |
Route Structure:
struct Route {
stream_id: u64, // Unique stream identifier
source_addr: SocketAddr, // Packet source
target_addrs: Vec<SocketAddr>, // Forwarding destinations
room_code: Option<String>, // Room grouping
last_seen: Instant, // TTL tracking
}Connection Pool (10 conns) → Async commands → Auto-reconnect → Fallback on failure
- Keys:
route:{stream_id},node:{node_id}:heartbeat - Ops: GET, SET, DEL, TTL (auto-expire inactive routes)
- Pool:
deadpool-redisfor connection management - Failure mode: Automatically switches to in-memory routing
HTTP Request → Auth middleware → Handler → Redis/DashMap → Response
| Endpoint | Method | Auth | Purpose |
|---|---|---|---|
/health |
GET | ❌ | Health status + metrics |
/route |
POST | ✅ | Create route |
/route/{id} |
GET | ✅ | Get route info |
/route/{id} |
DELETE | ✅ | Remove route |
/migrate |
POST | ✅ | Stream migration |
/metrics |
GET | ❌ | Prometheus metrics |
W3C Trace Context → Per-stream trace → Baggage metadata → Export to OTLP
Header Format:
traceparent: 00-{trace_id:32hex}-{span_id:16hex}-01
tracestate: soundrelay=stream_id:{id},node:{node_id}
Multi-source packets → Buffer per stream → Mix algorithm → Single output
- Enabled via
ENABLE_MIXER=true - PCM mixing with overflow protection
- Room-based isolation
┌────────────────────────────────────────────────────────────┐
│ Main Process │
├────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ UDP Listener │ │ Control API │ │ Metrics │ │
│ │ (Tokio) │ │ (Hyper) │ │ (Prometheus) │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ └──────────┬───────┴──────────────────┘ │
│ │ │
│ ┌──────────▼──────────┐ │
│ │ Routing Table │ │
│ │ (DashMap + Redis) │ │
│ └─────────────────────┘ │
└────────────────────────────────────────────────────────────┘
Creating a route:
1. POST /route → Auth middleware validates API key
2. Handler parses JSON body
3. DashMap.insert(stream_id, route) → O(1) memory write
4. Redis SET route:{stream_id} → Persistent storage
5. Return 201 Created
Forwarding audio:
1. UDP packet arrives → Parse header (stream_id + metadata)
2. DashMap.get(stream_id) → O(1) lookup
3. Jitter buffer insert (if enabled) → Reorder packets
4. Forward to target_addrs → UDP sendto
5. Increment metrics → relay_packets_sent_total++
| Variable | Default | Description |
|---|---|---|
BIND_ADDR |
0.0.0.0:4000 |
UDP relay listener |
CONTROL_API_ADDR |
0.0.0.0:4300 |
HTTP API |
METRICS_ADDR |
0.0.0.0:9100 |
Prometheus exporter |
REDIS_URL |
redis://127.0.0.1:6379 |
Redis connection |
REDIS_POOL_SIZE |
10 |
Connection pool |
AUTH_ENABLED |
true |
API auth toggle |
API_KEYS |
- | Comma-separated keys |
MAX_STREAMS_PER_NODE |
1000 |
Capacity limit |
NODE_ID |
relay-node-1 |
Node identifier |
ENABLE_MIXER |
false |
Audio mixing |
RUST_LOG |
info |
Log level |
Prometheus Metrics: http://localhost:9100/metrics
| Metric | Type | Description |
|---|---|---|
relay_packets_received_total |
Counter | Total UDP packets in |
relay_packets_sent_total |
Counter | Total UDP packets out |
relay_packet_errors_total |
Counter | Processing errors |
relay_active_streams |
Gauge | Current streams |
relay_active_routes |
Gauge | Current routes |
relay_packet_processing_duration_seconds |
Histogram | Latency (p50/p95/p99) |
http_requests_total |
Counter | API requests |
relay_redis_operations_total |
Counter | Redis ops |
Example Queries:
# Packet throughput (packets/sec)
rate(relay_packets_received_total[1m])
# p95 latency
histogram_quantile(0.95, rate(relay_packet_processing_duration_seconds_bucket[5m]))
# Error rate
rate(relay_packet_errors_total[5m]) / rate(relay_packets_received_total[5m])
| Method | Command | Use Case |
|---|---|---|
| Cargo | cargo build --release && ./target/release/SoundRelay |
Development |
| Docker | docker run -p 4000:4000/udp -p 4300:4300 soundrelay |
Containerized |
| K8s | kubectl apply -f k8s/deployment.yaml |
Production cluster |
| Systemd | systemctl start soundrelay |
Linux server |
Authentication: Authorization: Bearer <key> or X-API-Key: <key>
| Endpoint | Method | Body | Response |
|---|---|---|---|
/health |
GET | - | Health status |
/route |
POST | {"stream_id":u64,"source_addr":"ip:port","target_addrs":["ip:port"]} |
201 Created |
/route/{id} |
GET | - | Route details |
/route/{id} |
DELETE | - | 204 No Content |
/migrate |
POST | {"stream_id":u64,"new_node_addr":"ip:port"} |
200 OK |
Apache-2.0 License- See LICENSE
Maintainer: Nick Aidil 🌸 | Status: Production Ready ✅
