AppRTC is a WebRTC reference application and signaling server in the webrtc-rs ecosystem. The current Rust
implementation provides the complete AppRTC-compatible P2P V1 room and signaling flow: HTTP join/message/leave APIs,
initiator election, two-member rooms, queued signaling messages, tokenless browser WebSocket registration, reconnect
grace, fallback POST/DELETE signaling, HTML templates, static web assets, ICE configuration, and HTTP/HTTPS plus WS/WSS
serving.
The current executable supports P2P V1. The repository also contains the Sans-I/O SFU implementation and the
architecture for P2P/SFU call modes, but V2 mode transitions and SFU worker integration are not yet enabled in the
apprtc binary.
The Rust implementation replaces the previous unified Go Collider. The legacy implementation is retained only on the
repository's go branch.
Architecture
The workspace has three Rust crates:
| Crate | Responsibility |
|---|---|
apprtc |
Executable composition, CLI parsing, logging, HTTP or TLS listener, and graceful shutdown. |
appweb |
AppRTC HTTP room API, configuration parameters, Jinja templates, static web assets, and the in-process client for the signaling authority. |
signaling |
Authoritative room/client state, V1 browser protocol, message queueing and relay, reconnect deadlines, Sans-I/O protocol layers, and the Axum WebSocket driver. |
The initial deployment is an all-in-one process. appweb does not maintain a second room table: its HTTP handlers
submit admit, remove, occupancy, inject, and status operations to the single serialized Collider owner task.
Browser WebSocket traffic terminates directly in signaling and never passes through the web handlers.
The signaling state is composed from Sans-I/O protocols:
Collider
└── RoomTable
└── Room
└── Client
Tokio tasks own sockets and timers, while Collider owns deterministic signaling state. Successful V1 registration is
intentionally silent, and a disconnected registered client remains eligible to reconnect for 10 seconds before its
membership is removed.
Current P2P V1 behavior
- Room and client IDs are opaque non-empty strings.
- A room contains at most two clients; a third join returns
FULL. - The first client is the initiator and the second is the callee.
- Removing a client promotes the survivor to initiator.
- Offers and trickle ICE candidates sent before the peer joins are queued.
- The second
/joinresponse returns queued messages inparams.messages. - The stock AppRTC asymmetric signaling flow is preserved: the initiator sends early signaling through
/message, while the callee normally sends through WebSocket. - A WebSocket disconnect starts a 10-second reconnect grace period instead of removing the client immediately.
- Root-path and
/_internalPOST/DELETE fallback routes are both supported. - V1 identifiers are not restricted to numeric values. Numeric
u64validation belongs to the future V2 protocol.
Build and test
Use a Rust toolchain with Edition 2024 support.
The integration tests are black-box clients of a real AppRTC TLS server. Run them locally in three steps from the repository root:
# 1. Start AppRTC on loopback in the background.
&
# 2. Run the integration tests against that server.
# 3. Stop the background server.
||
CI performs the same sequence with a release build in .github/workflows/tests.yml and uploads the server log when the
job finishes.
Run over HTTP and WebSocket
Run this command from the repository root:
The server prints:
AppRTC listening on http://127.0.0.1:8080
Open http://127.0.0.1:8080 in a browser.
--host is used both as the local listening host and in generated HTTP/WebSocket URLs. Use an address that browsers can
reach; the default is 127.0.0.1. The generated URLs include --port.
Run over HTTPS and secure WebSocket
Add --tls to serve real HTTPS and WSS from the same listener:
Without certificate options, AppRTC uses the bundled development certificate at
apprtc/cert/cert.pem. Its subject alternative names include localhost, 127.0.0.1, and
::1, but it is self-signed. Trust that certificate in the browser or operating-system trust store before opening the
page; otherwise HTTPS and WSS clients will reject it with CertificateUnknown or an equivalent certificate-authority
error.
For a deployment, supply a certificate issued by a trusted authority. Both options must be supplied together:
Command-line options
Run cargo run -p apprtc -- --help for the authoritative list.
| Option | Default | Description |
|---|---|---|
--host <HOST> |
127.0.0.1 |
Host used by the listener and generated HTTP/WebSocket URLs. |
-p, --port <PORT> |
8080 |
HTTP/HTTPS and WS/WSS listening port. |
--web-root <PATH> |
appweb |
Directory containing the HTML, JavaScript, CSS, and image assets. |
--tls |
off | Serve HTTPS and WSS instead of HTTP and WS. |
--certificate <PATH> |
bundled development certificate | PEM certificate chain used with --tls. |
--private-key <PATH> |
bundled development key | PEM private key used with --tls. |
--ice-server-url <URLS> |
empty | ICE server URL; repeat the option or provide comma-separated URLs. |
--ice-server-base-url <URL> |
same AppRTC origin | External ICE credential service origin. |
--ice-server-api-key <KEY> |
empty | API key appended to the ICE credential service URL. |
--header-message <TEXT> |
empty | Banner displayed by the web application. |
--bypass-join-confirmation |
off | Skip the browser's ready-to-join prompt. |
-d, --debug |
off | Enable application logging. |
-l, --level <LEVEL> |
info |
Log filter: error, warn, info, debug, or trace. |
-o, --output-log-file <PATH> |
stdout | Truncate and write formatted logs to a file. |
Example ICE configuration:
HTTP API
| Method and path | Behavior |
|---|---|
GET / |
Render the room-selection page. |
GET /r/{roomid} |
Render the call page or the full-room page when occupancy is two. |
POST /join/{roomid} |
Generate an eight-digit client ID, admit it, and return legacy AppRTC room parameters. |
POST /message/{roomid}/{clientid} |
Queue or relay the raw signaling message and return { "result": "SUCCESS" }. |
POST /leave/{roomid}/{clientid} |
Remove the client and return the legacy empty success response. |
GET /params |
Return room-independent AppRTC parameters. |
GET or POST /v1alpha/iceconfig |
Return the configured ICE server list. |
GET /status |
Return uptime and WebSocket/HTTP counters. |
POST /{roomid}/{clientid} |
V1 wss_post_url fallback: inject a raw signaling message. |
DELETE /{roomid}/{clientid} |
V1 wss_post_url fallback: remove the client. |
POST or DELETE /_internal/{roomid}/{clientid} |
Compatibility alias for the fallback bridge. |
Static files under appweb/js, appweb/css, appweb/images, and appweb/html are served by the same process.
Join response
A successful join returns the legacy shape consumed by appweb/js/call.js:
The actual params object also contains peer-connection constraints, ICE configuration, media constraints, room links,
loopback settings, and UI configuration.
V1 WebSocket protocol
Browsers connect to /ws and send a registration frame first:
A successful V1 registration has no acknowledgement. The registered client sends an opaque signaling payload with:
The peer receives the payload without the signaling authority parsing or modifying the inner JSON:
Protocol errors are sent once before the socket is closed:
The inner msg string may contain an SDP offer, SDP answer, trickle ICE candidate, end-of-candidates marker, or bye
object. V1 signaling treats it as an opaque UTF-8 string.
Status endpoint
GET /status preserves the Collider-compatible response:
Repository layout
apprtc/ Rust executable crate and development TLS certificate
appweb/ Rust web/API crate plus AppRTC browser assets
signaling/ Rust Sans-I/O signaling authority and WebSocket driver
sfu/ Rust Sans-I/O selective-forwarding media server
docs/design/ Signaling architecture and protocol design
License
This project is dual-licensed under the MIT and Apache-2.0 licenses.
Contributing
Contributors and pull requests are welcome.