[](https://webrtc.rs)
## Overview
WebRTC.rs is an async-friendly WebRTC implementation in Rust, originally inspired by and largely rewriting the Pion
stack. The async `webrtc` crate is a clean, ergonomic, runtime-agnostic rewrite on top of a Sans-I/O core; it ships with
Tokio and smol runtime backends, and any other runtime can be plugged in by implementing one trait.
**Architecture:**
- **[rtc](https://github.com/webrtc-rs/rtc)**: Sans-I/O protocol core with complete WebRTC stack (95%+ W3C API
compliance)
- **webrtc** (this crate): a thin async layer over `rtc`:
- **`PeerConnection`** — the user-facing async API handle; all operations (create offers/answers, add tracks,
create data channels) are `async`
- **`PeerConnectionDriver`** — an internal background event loop, spawned automatically, that owns the sockets,
drives the Sans-I/O `rtc` core, handles timeouts, and dispatches events
- **`Runtime`** — a trait abstracting timers, task spawning, and sockets, so the crate is runtime-agnostic
**📖 Learn more:** Read
our [architecture blog post](https://webrtc.rs/blog/2026/01/31/async-friendly-webrtc-architecture.html) for design
details and roadmap.
## Getting Started
**Trying the `0.21` pre-release?** Cargo does not select pre-release versions from a plain
`"0.21"` requirement, so name it in full:
```toml
[dependencies]
webrtc = "0.21.0-alpha.1"
```
**Feature flags:**
| Feature | Default | Description |
|--------------------|---------|-------------------------------------------------------------------------|
| `runtime-tokio` | ✅ | Timers, task spawning and sockets via Tokio |
| `runtime-smol` | | The same, via smol |
| `runtime-mock` | | `MockRuntime`, a deterministic virtual-clock runtime for tests (no I/O) |
| `crypto-ring` | ✅ | The `ring`-based crypto provider |
| `crypto-aws-lc-rs` | | The `aws-lc-rs`-based crypto provider |
The runtime features are **additive**: each one only makes a built-in runtime *available*, so enabling several is safe
and a single process can drive different connections on different runtimes.
The crypto features work the same way. Enabling both compiles both providers, and `ring` stays the default selection —
so a dependency that turns on `crypto-aws-lc-rs` cannot silently change which one your application runs. Building with
neither compiles no provider, and you supply your own.
**Bringing your own runtime.** The built-ins are not privileged — implement `webrtc::runtime::Runtime` and pass it per
connection with `with_runtime`, with no `#[cfg]` edits and no fork. See
the [custom-runtime example](examples/custom-runtime), which runs the full stack on `async-executor` + `async-io` with
`--no-default-features` (neither Tokio nor smol compiled in).
**Choosing a crypto provider.** Same story: pass one per connection through `SettingEngine`, which also means two
connections in one process can use different providers.
```rust
use std::sync::Arc;
use webrtc::peer_connection::crypto;
use webrtc::peer_connection::SettingEngineBuilder;
let setting_engine = SettingEngineBuilder::new().with_crypto_provider(Arc::new(crypto::providers::AwsLcRsProvider::new()));
```
Applications needing a FIPS-validated module, an HSM, or a platform backend implement
`crypto::RTCCryptoProvider` and pass it the same way; `rtc-crypto`'s conformance suite validates
an implementation against the same RFC vectors the built-ins pass. No cryptography happens in this
crate — it forwards the provider to `rtc`.
Build a peer connection and create an offer:
```
/* Detailed source-code truncated for AI context efficiency. */
```
`build()` returns an opaque `impl PeerConnection`. `PeerConnection` is an object-safe trait, so when you need to store
the connection in a struct or share it across tasks, wrap it:
```rust,ignore
let pc: Arc = Arc::new(pc);
```
Either way no runtime or interceptor type parameters leak into your own types.
**Next steps:** browse the [API docs](https://docs.rs/webrtc) or the
[37 runnable examples](https://github.com/webrtc-rs/webrtc/tree/master/examples) — data channels, media playback,
simulcast, ICE restart, insertable streams, and more.
### The architecture
✅ **Runtime independence**
- Runtime-agnostic via a Quinn-style `Runtime` abstraction (timers, task spawning, sockets, DNS)
- Feature flags: **`runtime-tokio`** (default) and **`runtime-smol`**, additive rather than mutually exclusive
- Any third-party runtime works today: implement `Runtime`, inject it per connection with `with_runtime`.
The [custom-runtime example](examples/custom-runtime) does exactly that on `async-executor` + `async-io`, with neither
built-in runtime compiled in
- **`runtime-mock`** gives tests a deterministic virtual clock, so timing-dependent behaviour is testable instantly and
without sockets
✅ **Clean event handling**
- One trait-based event handler (`PeerConnectionEventHandler`) for every event, with default no-op methods so you
implement only what you need — no per-event callback registration
- Centralized state: the handler is one shared `Arc`. Methods take `&self`, so mutable handler state
goes behind a single lock rather than being captured per closure
✅ **Sans-I/O foundation**
- Protocol logic completely separate from I/O (via the [rtc](https://github.com/webrtc-rs/rtc) core)
- Deterministic testing without real network I/O
- A thin async driver (`PeerConnection` handle + background `PeerConnectionDriver`) over the core
### Release lines
- **`v0.21.x`** — in development, currently `v0.21.0-alpha.1`. The pre-1.0 line, working towards a stable public
API; see [what's in 0.21](#whats-in-021) below. APIs may change between alphas.
- **`v0.20.x`** — the current stable line, and the recommended choice for production today.
While the version is `0.x`, a minor bump may carry breaking changes
(see [Semantic Versioning](#semantic-versioning)). Once `1.0` ships, that stops being true — which is the whole
point of the `0.21` work.
### What's in 0.21
`v0.21` is the run-up to `1.0`, whose goal is a **stable public API** — not every feature, but an API that will
not break under you. Work so far:
✅ **Extensibility, before the freeze** — public enums are `#[non_exhaustive]` and the traits this crate alone
implements are sealed, so variants and methods can be added later without a major bump. This cannot be done
*after* `1.0`, which is why it came first.
✅ **Crypto provider selection** — `crypto-ring` (default) and `crypto-aws-lc-rs` features, chosen per peer
connection, so two connections in one process can use different providers. Build with neither and supply your
own. No cryptography happens in this crate; a CI check enforces that.
✅ **Deterministic time** — the Sans-I/O core no longer reads a clock. Time is an input, threaded from
`Runtime::now()`, so `runtime-mock`'s virtual clock genuinely drives ICE timeouts, DTLS retransmits and SCTP
RTO. Advancing a mock clock by 30 s now produces real protocol transitions instead of nothing.
✅ **No silent drops on data channels** — a slow consumer used to lose messages on a *reliable* channel once
the internal hand-off queue filled. The driver now keeps them and stops pulling from the core, so back-pressure
reaches SCTP's receive window and the peer is throttled instead. Media is unaffected by a stalled data channel.
Track the remaining work in [The path to webrtc 1.0](https://github.com/webrtc-rs/webrtc/issues/836).
### How to Provide Feedback
We welcome your input as `v0.20.x` and `v0.21.x` grow:
- Review the [architecture blog post](https://webrtc.rs/blog/2026/01/31/async-friendly-webrtc-architecture.html)
- Join discussions on [GitHub Issues](https://github.com/webrtc-rs/webrtc/issues)
- Chat with us on [Discord](https://discord.gg/4Ju8UHdXMs)
**New projects:** start on `v0.20`, or on `v0.21.0-alpha.1` if you want the pre-1.0 API and can absorb changes
between alphas.
**Hit a gap?** Open an issue — reports of what is missing directly shape what we prioritise before `1.0`.
## Building and Testing
```bash
# Update rtc submodule first
git submodule update --init --recursive
# Build the library
cargo build
# Run tests
cargo test
# Build documentation
cargo doc --open
# Run examples
cargo run --example data-channels
```
## Semantic Versioning
This project follows [Semantic Versioning](https://semver.org/):
- **Patch** (`0.x.Y`): Bug fixes and internal improvements with no public API changes.
- **Minor** (`0.X.0`): Backwards-compatible additions or deprecations to the public API.
- **Major** (`X.0.0`): Breaking changes to the public API.
While the version is `0.x`, the minor version acts as the major — i.e., a minor bump may include breaking changes. Once
`1.0.0` is released, full semver stability guarantees apply.
Pre-release versions are published with the following suffixes, in order of increasing stability:
- **`-alpha.N`**: Early preview. API is unstable and may change significantly.
- **`-beta.N`**: Feature-complete for the release. API may still have minor changes.
- **`-rc.N`**: Release candidate. No further API changes are expected unless critical issues are found.
For example: `1.0.0-alpha.1` → `1.0.0-beta.1` → `1.0.0-rc.1` → `1.0.0`.
## Open Source License
Dual licensing under both MIT and Apache-2.0 is the currently accepted standard by the Rust language community and has
been used for both the compiler and many public libraries since (
see ). In order to match the
community standards, webrtc-rs is using the dual MIT+Apache-2.0 license.
## Contributing
Contributors or Pull Requests are Welcome!!!