videocall.rs is an open-source, low-latency video conferencing platform and streaming API written entirely in Rust. It is a WebRTC alternative built on WebTransport (HTTP/3 and QUIC) with an automatic WebSocket fallback, and it runs in the browser, on native devices through a command-line client, and on mobile through iOS and Android SDKs. It is self-hostable, dual-licensed under MIT and Apache-2.0, and built for real-time video streaming from robots, drones, and embedded Linux boards (Raspberry Pi, Jetson) as well as conventional browser-to-browser calls.
Website | Documentation | Discord | Crates.io
Project status: Beta. Actively developed and used in production for non-critical workloads.
- What videocall.rs is for
- Features
- FAQ
- Why WebTransport instead of WebRTC?
- How it compares
- Browser compatibility
- Architecture
- Getting Started
- Runtime configuration (config.js)
- Usage
- Meeting management
- Performance
- Security
- Feature flags
- Testing
- Release history
- Contributing
- Demos and media
- Contributors
- Sponsors
- License
videocall.rs gives you the building blocks for real-time video communication without the WebRTC stack. It is aimed at three audiences:
- Software professionals building custom video applications on a type-safe Rust API, from web apps to autonomous-vehicle feeds.
- Robotics and IoT engineers streaming low-latency video from drones, robots, and embedded devices using the lightweight
videocall-cliand the mobile SDKs. - Teams that want to self-host their own conferencing infrastructure with JWT authentication, SSO/OAuth, and transport encryption (TLS 1.3 / QUIC), deployed with the provided Helm charts.
The same core powers browser calls at videocall.rs.
- Low latency by design. Media travels over WebTransport (QUIC/HTTP3), which avoids head-of-line blocking and recovers from packet loss faster than TCP-based transports.
- WebTransport with WebSocket fallback. Clients negotiate WebTransport where available and fall back to WebSockets automatically for compatibility.
- Encrypted in transit. WebSocket connections use TLS 1.3 and WebTransport uses QUIC's built-in TLS 1.3, so media is encrypted between each client and the relay. See Security.
- Browser, native, and mobile clients. A Dioxus/WebAssembly web UI, a native
videocall-clifor headless streaming, andvideocall-sdkbindings for iOS and Android. - Horizontally scalable. A NATS pub/sub backbone lets WebSocket and WebTransport servers scale independently behind a load balancer.
- Self-hostable. Kubernetes Helm charts, a fully native dev stack, and reproducible container images built with Nix.
- Pure-Rust media pipeline. VP9 encode/decode and Opus audio are pure Rust with no C libvpx or libopus dependency.
- Open source. Dual-licensed under MIT and Apache-2.0.
For most server-mediated video use cases, yes. videocall.rs does not use WebRTC's peer-connection stack. Media flows over WebTransport (QUIC/HTTP3), or a WebSocket fallback, to a Rust server that forwards packets to other participants through NATS. You give up WebRTC's mature browser SFU ecosystem, but you also drop ICE, STUN/TURN, and SDP negotiation entirely.
Yes. The repository ships Helm charts for Kubernetes and a fully native local dev stack. Production container images are built reproducibly with Nix. See Getting Started and the architecture document.
Yes, on Chromium-based browsers (Chrome, Edge, Brave) and Safari (macOS and iOS). Firefox is not currently supported. See Browser compatibility.
Those stacks are WebRTC selective-forwarding units. videocall.rs forwards media over WebTransport/QUIC instead of WebRTC, is written entirely in Rust (server, browser client, and native client), and requires no STUN/TURN/ICE infrastructure. See How it compares.
Yes. videocall-cli is a headless native client that streams from a camera on Raspberry Pi, Jetson Nano, and other embedded Linux devices. See CLI-based streaming.
Media is encrypted in transit. WebSocket connections use TLS 1.3, and WebTransport uses QUIC's built-in TLS 1.3 encryption, so media is protected between each client and the relay. The relay server forwards media between participants; it is not end-to-end encrypted.
WebTransport is the core technology that sets videocall.rs apart. Built on HTTP/3 and QUIC, it provides multiplexed, bidirectional streams and datagrams with modern congestion control. Choosing it over WebRTC has concrete consequences:
- No ICE, STUN, or TURN. Clients connect to a server over QUIC. There is no NAT-traversal negotiation, no candidate gathering, and no relay-server fleet to operate for connectivity.
- No SDP signaling dance. Session setup is a single QUIC handshake rather than an offer/answer exchange over a separate signaling channel.
- Better loss recovery. QUIC's per-stream flow control avoids the head-of-line blocking that degrades TCP-based transports on lossy links, which matters on mobile and long-haul networks.
- Faster connection establishment. QUIC's 0-RTT and 1-RTT handshakes reduce setup round-trips compared with WebRTC's multi-step negotiation.
- A standards-track platform. WebTransport is developed at the IETF and W3C with active browser-vendor support.
videocall.rs still runs a forwarding server, so it is architecturally closer to an SFU than to peer-to-peer WebRTC. The difference is that the transport and signaling are radically simpler: no ICE, no TURN, no SDP. For a deep dive, see the architecture document.
An honest, verifiable comparison with widely used WebRTC-based stacks. All four are open source and server-mediated (SFU-style); the differences are in transport and implementation language.
| Project | Media transport | Signaling / NAT traversal | Core language | License |
|---|---|---|---|---|
| videocall.rs | WebTransport (QUIC/HTTP3), WebSocket fallback | QUIC handshake, no ICE/STUN/TURN | Rust (server, browser, native) | MIT / Apache-2.0 |
| LiveKit | WebRTC | ICE / STUN / TURN | Go | Apache-2.0 |
| Jitsi Videobridge | WebRTC | ICE / STUN / TURN | Java | Apache-2.0 |
| mediasoup | WebRTC | ICE / STUN / TURN | C++ core, Node.js / Rust API | ISC |
videocall.rs is the youngest of these and does not yet match their breadth of client SDKs, recording, and ecosystem integrations. Its advantage is a simpler transport and a single-language (Rust) codebase from server to browser.
| Browser | Support |
|---|---|
| Chrome | Yes |
| Brave | Yes |
| Edge | Yes |
| Safari (macOS, iOS) | Yes |
| Firefox | No |
videocall.rs follows a microservices architecture in which clients connect over WebSocket or WebTransport and servers exchange media through a NATS pub/sub backbone.
graph TD
Clients[Clients<br>Browsers, Mobile, CLI] -->|WebSocket| ActixAPI[Actix API<br>WebSocket]
Clients -->|WebTransport| WebTransportServer[WebTransport<br>Server]
ActixAPI --> NATS[NATS<br>Messaging]
WebTransportServer --> NATS
Primary components:
- actix-api streaming servers (
websocket_server,webtransport_server,metrics) built on Actix Web. - meeting-api REST/auth API (login, meetings, host controls) on Axum with dbmate migrations.
- dioxus-ui web frontend built with Dioxus and compiled to WebAssembly.
- videocall-types shared protobuf data types and protocol definitions.
- videocall-client client library for native and WebAssembly integration.
- videocall-cli command-line client for headless streaming.
- videocall-sdk iOS and Android bindings (UniFFI).
- videocall-codecs / neteq pure-Rust codec wrappers and adaptive audio jitter buffer.
For details, including connection flows, message routing, and the encryption model, see the architecture document.
Development is fully native: the entire dev stack (postgres, NATS, prometheus, grafana, and the Rust services) runs as host processes supervised by process-compose, with hot reload everywhere. Docker only appears when you want to run the images, which are built by Nix. There are no Dockerfiles in the dev workflow. See docs/nix-architecture.md.
- Modern Linux distribution, macOS, or Windows 10/11 (WSL2).
- Nix, which provides the dev stack, toolchains, and image builds.
- One-time on multi-user installs: add yourself to
trusted-usersso builds can pull the project's public binary cache instead of compiling everything locally. Until then Nix printsignoring untrusted substituter 'https://videocall-rs.cachix.org'(harmless, but slow):echo "trusted-users = root $USER" | sudo tee -a /etc/nix/nix.conf sudo pkill nix-daemon
- One-time on multi-user installs: add yourself to
- Docker is not needed. It is optional, only if you want to
docker runa published image locally. - A Chromium-based browser (Chrome, Edge, Brave) or Safari (macOS, iOS). Firefox is not supported.
git clone https://github.com/security-union/videocall-rs.git
cd videocall-rs
make devThat's it. make dev opens the process-compose TUI running the whole stack as native processes:
| Process | What | Port |
|---|---|---|
postgres |
postgresql from nixpkgs, state in .data/postgres |
5432 |
nats |
nats-server with JetStream, state in .data/nats |
4222 / 8222 |
meeting-api |
dbmate migrations, then cargo watch |
8081 |
websocket / webtransport |
cargo watch |
8080 / 4433 udp |
metrics / server-stats |
cargo watch |
9091 / 9092 |
dioxus-ui |
tailwind watch + trunk serve |
3001 |
prometheus / grafana |
native, same dashboards/config as the container stack | 9090 / 3000 |
Services wait for middleware health (compose-style depends_on), recompile and restart instantly on save, and the TUI can restart individual processes on demand. Open http://localhost:3001 and navigate to a meeting at http://localhost:3001/meeting/<username>/<meeting-id>.
To stop: quit the TUI (F10 / Ctrl-C), or run make dev-down from another terminal. The TUI needs an interactive terminal; for scripted or headless use run nix-shell default.nix -A shells.dev --run "dev-stack -t=false".
Prefer one service per terminal? make dev-middleware starts just postgres/nats/prometheus/grafana; then make dev-websocket, make dev-meeting-api, make dev-ui, and so on run a single watcher each (these also work with plain rustup plus cargo-watch).
Overriding environment variables: every process in make dev sees env layered as built-in defaults < .env < .env.local, both untracked. Copy .env-sample to .env for documented knobs (OAuth, service URLs, JWT secret); put personal or secret overrides in .env.local, which is gitignored and never checked in.
Nothing local needs Docker: development, tests, and e2e all run native. The container images exist for Kubernetes. CI builds and publishes them from release.nix on every merge. To build one locally (for example, to debug an image's entrypoint with docker run):
make image-websocket-server # nix-build one image and docker-load it
make images # all of themOn Linux the images build natively; on macOS the service binaries are cross-compiled to aarch64-unknown-linux-musl with no VM and no remote builder. The first macOS build compiles a musl cross-toolchain (one-time, tens of minutes); after that it is cached in the Nix store.
Both stacks read the same files: make dev layers .env then .env.local into every process. To enable Google login, create a .env from the sample and fill in credentials (or keep secrets in .env.local):
cp .env-sample .env- Go to Google Cloud Console → APIs & Credentials.
- Create an OAuth 2.0 Client ID (Web application type).
- Add
http://localhost:8081/login/callbackas an authorized redirect URI. - Copy the Client ID and Secret into your
.env.
Without OAuth credentials the app runs with auth bypassed for local development.
Platform notes:
- Rancher Desktop (Windows/WSL2) with Traefik Ingress on port 3001: Rancher Desktop runs Traefik, which may conflict with the dioxus-ui frontend. Override the port in your local
.env(not.env-sample):Then access the app atDIOXUS_SERVE_PORT=8088 AFTER_LOGIN_URL=http://localhost:8088 ALLOWED_REDIRECT_URLS=http://localhost:8088http://localhost:8088. - Shell environment variables: if you have
API_BASE_URL,OAUTH_REDIRECT_URL, or similar variables exported in your shell profile (~/.bashrc,~/.zshrc), they override.envvalues. Remove them from your profile before runningmake dev.
The build system is native Nix, no flakes: default.nix (entry point), shell.nix (dev shells), release.nix (CI artifacts: packages and Docker images). Dependency pins live in nix/tamal/ and are managed by nixtamal; evaluating them needs plain Nix only. Full design: docs/nix-architecture.md. Coming from the old Docker dev environment? See docs/migrating-from-docker.md.
nix-shell # default dev shell (frontend toolchain)
nix-shell default.nix -A shells.backend-dev # backend toolchain + cargo-watch + dbmate
nix-build release.nix -A packages.websocket-server # a single service binary (static musl)
make image-websocket-server # its Docker image, loaded into the daemon
make pins-update # refresh nixtamal pins (nix/tamal/)The frontend is configured at runtime via a window.__APP_CONFIG object provided by a config.js file. The file is copied by Trunk and loaded at /config.js by dioxus-ui/index.html.
- Local:
make devandmake dev-uirunscripts/start-dioxus.sh, which writesdioxus-ui/scripts/config.jsfrom the environment before Trunk starts. To point the UI somewhere custom, override the relevant env vars in.env.local(seescripts/start-dioxus.shfor the keys) rather than editing the generated file. The Nix-builtvideocall/dioxus-uiimage does the same at container startup via its entrypoint. - Kubernetes / Helm:
helm/videocall-ui/templates/configmap-configjs.yamlrendersconfig.jsfrom.Values.runtimeConfig. DefineruntimeConfigin your values file and deploy or upgrade.
The vadThreshold config parameter controls how sensitive speaking detection is. It sets the minimum RMS audio level that counts as "speaking," used for tile border glow, peer-list mic glow, and self-video glow indicators.
window.__APP_CONFIG = Object.freeze({
// ... other config ...
vadThreshold: 0.02 // default
});| Value | Sensitivity | Use case |
|---|---|---|
0.01 |
High: picks up quiet speech and background noise | Quiet environments, soft speakers |
0.02 |
Medium (default): good balance for most setups | General use |
0.05 |
Low: only triggers on louder speech | Noisy environments, reduces false positives |
0.10 |
Very low: requires loud or close speech | Very noisy environments |
The threshold can also be set via the VAD_THRESHOLD environment variable (see scripts/start-dioxus.sh) or via runtimeConfig.vadThreshold in Helm values.
- Navigate to your deployed instance or local setup:
http://<server-address>/meeting/<username>/<meeting-id> - Grant camera and microphone permissions when prompted.
- Click "Connect" to join the meeting.
For headless devices like a Raspberry Pi:
# Install the CLI tool
cargo install videocall-cli
# Stream from a camera
videocall-cli stream \
--user-id <your-user-id> \
--video-device-index 0 \
--meeting-id <meeting-id> \
--resolution 1280x720 \
--fps 30 \
--frame-format NV12 \
--bitrate-kbps 500The CLI joins an existing meeting; create the call from a browser first. For all options, see the videocall-cli README.
videocall.rs includes a meeting management system with ownership, waiting rooms, and host controls.
- Meeting ownership: each meeting has an owner (the creator) identified by their email.
- My Meetings: users can view and manage all meetings they own from the home page.
- Waiting room: non-owners enter a waiting room and must be admitted by an existing participant.
- Host identification: the meeting owner is marked with "(Host)" in the UI.
- Soft delete: owners can delete their meetings; deleted meeting IDs can be reused.
Workflow: navigating to /meeting/{meeting-id} creates the meeting and makes you the owner if it does not exist. Owners see "Start Meeting," others see "Join Meeting." Non-owners wait for admission; admitted participants are auto-joined and can then manage the waiting room.
Meeting management requires the FEATURE_MEETING_MANAGEMENT flag (see Feature flags):
export FEATURE_MEETING_MANAGEMENT=trueDetails: Meeting Ownership & Workflow and Meeting API Reference.
videocall.rs is engineered for real-time streaming across a range of hardware and networks:
- One-on-one and small groups: a mesh-style topology with adaptive quality driven by per-receiver diagnostics (packet loss, latency, jitter, estimated bandwidth).
- Large conferences: the NATS-backed forwarding architecture scales out horizontally — add relay servers for capacity, with WebSocket and WebTransport relays scaling independently behind a load balancer.
Implementation choices that support this:
- Asynchronous core. Built on Rust's async/await ecosystem with the Tokio runtime.
- SIMD-accelerated VP9 encoding. The pure-Rust VP9 encoder uses AVX2/SSE2 (x86-64) and NEON (aarch64) intrinsics for motion estimation, with scalar fallbacks.
- Binary protocol. All messages use Protocol Buffer serialization, keeping wire size small.
- Adaptive streaming. Senders adjust bitrate, resolution, and frame rate to the most constrained receiver based on diagnostics feedback.
Design goals still in progress (minimizing data copies on the media path, further congestion-control tuning) are tracked in the architecture document. Detailed benchmarking guidelines are a work in progress.
- Transport security. WebSocket connections use TLS 1.3; WebTransport inherits QUIC's built-in TLS 1.3 encryption. Media is encrypted between each client and the relay, but not end-to-end: the relay forwards decrypted media between participants.
- Authentication. JWT-based access control with SSO/OAuth integration.
- Access controls. Meeting ownership, waiting rooms, and host controls (see Meeting management).
For the full model, see the security architecture and the security documentation.
videocall.rs uses environment-based feature flags to enable optional or experimental functionality at runtime. Flags are loaded lazily on first access and can be overridden for testing.
Flags are set via environment variables with the FEATURE_ prefix:
export FEATURE_MEETING_MANAGEMENT=true
# or, with Docker:
docker run -e FEATURE_MEETING_MANAGEMENT=true ...| Flag | Environment variable | Description | Default |
|---|---|---|---|
| Meeting Management | FEATURE_MEETING_MANAGEMENT |
Meeting lifecycle management: creation, tracking, host controls | false |
Truthy values (case-insensitive) are true, 1, and yes. Any other value, or an unset variable, is treated as false.
The Dioxus frontend uses a three-layer testing pyramid, all running in a real browser via wasm-bindgen-test:
| Layer | What it covers | Example |
|---|---|---|
| Unit | MediaDeviceList logic: hot-plug, fallback, device switching |
videocall-client/src/media_devices/media_device_list.rs |
| Component | Isolated Dioxus components with mock MediaDeviceInfo objects |
dioxus-ui/tests/device_selector.rs, dioxus-ui/tests/video_control_buttons.rs |
| Integration | Real Chrome fake devices rendered through the full pipeline | dioxus-ui/tests/device_integration.rs |
# Run UI component tests natively (requires Chrome + chromedriver)
cd dioxus-ui
CHROMEDRIVER=$(which chromedriver) cargo test --target wasm32-unknown-unknownCI runs these via .github/workflows/wasm-test.yaml. For the full guide, see dioxus-ui/README.md.
The actix-api crate has unit and integration tests that run against real PostgreSQL and NATS instances, started natively by make check-backend. Coverage includes session management, WebSocket and WebTransport meeting lifecycles, packet classification, the metrics server, and feature-flag behavior.
Tests use #[serial_test::serial] because they share a database, and each cleans up its own data. Everything runs natively, no Docker: make check-backend starts postgres 18 and NATS (JetStream) from nixpkgs under process-compose in a throwaway data dir, runs dbmate migrations, then clippy, fmt, and cargo test in the pinned nix-shell.
make check-backend # all backend tests (native postgres/NATS, fresh state per run)
make check-backend-sqlite # same, against the SQLite meeting-api backend
make check-backend-down # stop the test middleware and delete its stateCI runs these via .github/workflows/cargo-test.yaml on PRs touching actix-api/, videocall-types/, or protobuf/. Full guide: actix-api/TESTING.md.
Full browser-based end-to-end tests using Playwright. Tests run against the Dioxus UI, verifying meeting flows in real browsers; authentication is bypassed via JWT cookie injection, so no OAuth setup is needed.
The E2E stack runs natively, no Docker (e2eStack in nix/dev-stack.nix): postgres and NATS from nixpkgs, the meeting-api and websocket servers, and the Nix-built release UI dist served by caddy, which is the same payload the production images ship. Tests run automatically on pushes to main and can be triggered manually from the GitHub Actions page. See the e2e-* targets in the Makefile.
Milestones from 2023 to 2026
| Quarter | Release | Deliverable |
|---|---|---|
| Q2 2023 | 0.5.0 | JWT authentication & SSO |
| Q3 2023 | 0.6.0 | Safari browser support |
| Q4 2023 | 0.7.0 | Native mobile SDKs |
| Q3 2024 | — | videocall.rs website launched, with Matomo analytics for self-hosted usage insight (#169, #170) |
| Q3 2024 | — | Postgres made optional and a DigitalOcean deployment path documented, plus WebTransport connection fixes (#163, #165) |
| Q4 2024 | — | videocall-daemon released: headless streaming for robotics and embedded targets (#176) |
| Q4 2024 | — | macOS builds for the daemon and Ubuntu 24 base images (#177, #173) |
| Q1 2025 | videocall-client 1.0.0 |
Whole workspace published to crates.io at 1.0.0 with release-plz automation; daemon renamed to videocall-cli (#222, #212, #185) |
| Q1 2025 | videocall-client 1.1.6 |
In-call diagnostics panel, Yew UI redesign, and multi-peer bitrate control (#206, #196, #242) |
| Q2 2025 | videocall-sdk 0.1.0 |
iOS and Android bindings shipped as videocall-sdk (#253) |
| Q2 2025 | videocall-codecs 0.1.1 |
Full Safari support via a WASM Opus encoder, plus the videocall-codecs crate with a jitter-buffered decoder (#266, #282, #285) |
| Q3 2025 | neteq 0.1.0 |
NetEQ adaptive audio jitter buffer ported to Rust/WASM and rolled out to every browser through an AudioWorklet (#305, #310, #315) |
| Q3 2025 | videocall-cli 3.0.0 |
Prometheus + Grafana diagnostics and multi-region HA; CLI drops raw QUIC in favor of WebTransport (#365, #325, #410) |
| Q4 2025 | videocall-client 1.1.28 |
End-to-end OAuth / SSO sign-in with configurable cookie domain (#471, #485) |
| Q4 2025 | videocall-client 1.1.29 |
Meeting ownership behind a feature flag, and a NetEQ overhaul with WebCodecs support (#503, #466) |
| Q1 2026 | videocall-client 4.0.5 |
Dioxus UI became the sole frontend and Yew was removed; WebTransport server consolidated onto an actor model (#646, #788, #551) |
| Q1 2026 | videocall-types 5.0.0 |
Nix-based builds across backend, UI, and website; per-PR preview environments; Playwright E2E suite (#639, #672, #714) |
| Q1 2026 | videocall-client 4.0.5 |
Adaptive quality stack: PID-driven encoder adaptation, PLI keyframe requests, and decoder visibility skipping (#758, #761, #762) |
| Q2 2026 | videocall-cli 4.0.0 |
Pure-Rust audio: audiopus-sys/libopus removed workspace-wide in favor of ropus (#872) |
| Q2 2026 | videocall-types 6.0.0 |
Dioxus UI Helm chart with relay Prometheus annotations, reworked screen sharing, and audio-level re-render fixes (#757, #817, #816) |
| Q3 2026 | unreleased | Pure-Rust VP9 encoder and decoder: C libvpx removed from videocall-cli and the bot (#884) |
| Q3 2026 | unreleased | SQLite available as an optional database backend for meeting-api, alongside Postgres (#802) |
Contributions are welcome.
- Issues: report bugs or suggest features via GitHub Issues.
- Pull requests: submit PRs for bug fixes or enhancements.
- Community: join the Discord server to discuss development.
See the Contributing Guidelines for details.
- Backend: Rust + Actix Web + PostgreSQL (or SQLite) + NATS.
- Frontend: Rust + Dioxus + WebAssembly + Tailwind CSS.
- Transport: WebTransport (QUIC/HTTP3) + WebSocket fallback.
- Media: pure-Rust VP9 and Opus; NetEQ adaptive audio jitter buffer.
- Build system: Cargo + Trunk + Nix + Helm.
- Testing:
cargo test+wasm-bindgen-test(browser UI tests) + Playwright (E2E).
The repo ships its hooks in githooks/ (currently pre-push: cargo clippy -D warnings and cargo fmt --check). Enable them once per clone:
git config core.hooksPath githooksDario Lencina |
Seth Reid |
Griffin Obeid |
Ronen Barzel |
Leone |
Victor Martínez |
Hosting for the public videocall.rs instance is supported by DigitalOcean. Using this referral link helps fund the project:
Dual-licensed under the MIT License and the Apache License 2.0. See LICENSE-MIT and LICENSE-APACHE for details.