turna
A TURN/STUN server in Rust, built for a closed video-calling system that needed a networking stack under its own control rather than external infrastructure.
- Rust
- RFC 5389 · 5766 · 8656
- continuously fuzzed parsers
- Production GA 0.3.0
What it is
A TURN/STUN server written in Rust. When two devices cannot connect directly — the normal case behind NAT and a corporate firewall — call traffic goes through a relay. turna is that relay, deployed and controlled by you.
It implements STUN (RFC 5389) and TURN (RFC 5766, RFC 8656). Apache 2.0.
Why it exists
The project needed a closed video-calling system. In such a system the relay is not a minor detail: all media for calls that failed to connect directly passes through it. Which means access, limits and the physical path of the stream all pass through it too.
An external service means handing that perimeter to someone else. So the networking stack was written instead of plugging in third-party infrastructure.
What I did
The protocol layer, the architecture and the entire operational surface, solo. The workspace is split by domain: protocol (proto-stun, proto-turn, proto-rtp, packet), datapath (transport, relay, session, qos), auth and crypto, state and cluster, control plane and observability, binaries and tools.
What’s technically interesting
Batched UDP I/O. SO_REUSEPORT recv workers with recvmmsg/sendmmsg and per-batch arena buffers. The relay reads and writes in batches of syscalls rather than one packet at a time — on media traffic that is the main place throughput is lost.
Channel binding. The compact relay mode that drops the full STUN header from every packet. On a media stream the overhead difference is visible.
Memory safety kept in check. The STUN and TURN parsers are continuously fuzzed (cargo-fuzz), and every unsafe block is inventoried in a separate audit and confined to the transport and relay datapaths. A network protocol parser takes input from the outside world — valid frames are not enough to test it.
Operations as part of the product. A gRPC control plane and the turnactl CLI for managing a live server, Prometheus metrics and OpenTelemetry traces out of the box, graceful drain with a bounded wait, session migration per RFC 8016. Management commands are idempotent: each carries a target node, an expected version and an idempotency key, and the response comes from the node’s durable state rather than control-plane-local state.
Multi-tenancy. Realms with their own relay port pools and per-tenant limits, long-term credentials and JWT, credential rotation, per-subject limits. State lives in a pluggable backend — in-memory or Tarantool — with config and limit restore after a restart.
What’s ready and what’s preview
This is arguably the most important part.
The production path is the Tokio datapath: STUN binding, the TURN allocation lifecycle, relaying over UDP and TCP, auth, observability, management, graceful drain.
Behind Cargo features, and not considered verified for production: the io_uring and AF_XDP datapaths, and the DTLS, QUIC, WebTransport and TLS-over-TCP transports. RFC 6062 (TCP allocations) is implemented but less exercised. Clustering — gossip discovery, a hash ring, TURN redirects — is experimental: it does not guarantee transparent survival of active allocations or zero-gap rolling upgrades.
In the repository this is written down as a guarantees table that states plainly what the server does not promise: an allocation is not guaranteed to survive a process crash, an active media path does not migrate to another node, and transparent active-session failover is out of GA scope.
A table like that is more useful than a list of ticks. An infrastructure component is chosen on what it guarantees under load, not on how many protocols appear in its feature list.
Benchmarks
Reproducible benchmarks against coturn live in the repository, under bench/.
There are deliberately no numbers here: until a run is done with a fixed methodology, a stated coturn version and described hardware, publishing figures would mean passing off something that is not a measurement as one.