Skip to content

Discord Audio Relay Service (DARS) • Audio voice relay node for Discord Voice Gateaway UDP

License

Notifications You must be signed in to change notification settings

nvckai/SoundRelay

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SoundRelay

SoundRelay logo

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!

Quick Start

# 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

Stack

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

Architecture

System Flow

┌──────────────┐         ┌──────────────┐         ┌──────────────┐
│ Discord Bot  │───UDP───│  SoundRelay  │───UDP───│   Discord    │
│   (Source)   │         │  Relay Node  │         │   Gateway    │
└──────────────┘         └──────────────┘         └──────────────┘
                                │
                         Redis/In-Memory
                                │
                         ┌──────────────┐
                         │ Routing Table│
                         │  + Metrics   │
                         └──────────────┘

How It Works

1. Packet Reception (src/events/udp_listener.rs)

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

2. Routing Logic (src/utils/routing.rs)

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
}

3. Redis Integration (src/database/redis.rs)

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-redis for connection management
  • Failure mode: Automatically switches to in-memory routing

4. Control API (src/handlers/control_api.rs)

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

5. Distributed Tracing (src/utils/tracing.rs)

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}

6. Audio Mixing (src/events/mixer_client.rs)

Multi-source packets → Buffer per stream → Mix algorithm → Single output
  • Enabled via ENABLE_MIXER=true
  • PCM mixing with overflow protection
  • Room-based isolation

Component Interaction

┌────────────────────────────────────────────────────────────┐
│                        Main Process                        │
├────────────────────────────────────────────────────────────┤
│                                                            │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐    │
│  │ UDP Listener │  │ Control API  │  │   Metrics    │    │
│  │   (Tokio)    │  │   (Hyper)    │  │ (Prometheus) │    │
│  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘    │
│         │                  │                  │            │
│         └──────────┬───────┴──────────────────┘            │
│                    │                                       │
│         ┌──────────▼──────────┐                           │
│         │   Routing Table     │                           │
│         │  (DashMap + Redis)  │                           │
│         └─────────────────────┘                           │
└────────────────────────────────────────────────────────────┘

Data Flow Example

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++

Configuration

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

Monitoring

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])

Deployment

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

API Reference

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

License

Apache-2.0 License- See LICENSE


Maintainer: Nick Aidil 🌸 | Status: Production Ready ✅

About

Discord Audio Relay Service (DARS) • Audio voice relay node for Discord Voice Gateaway UDP

Resources

License

Stars

Watchers

Forks

Packages

No packages published