SpawnDev.RTC.Server
1.0.7
dotnet add package SpawnDev.RTC.Server --version 1.0.7
NuGet\Install-Package SpawnDev.RTC.Server -Version 1.0.7
<PackageReference Include="SpawnDev.RTC.Server" Version="1.0.7" />
<PackageVersion Include="SpawnDev.RTC.Server" Version="1.0.7" />
<PackageReference Include="SpawnDev.RTC.Server" />
paket add SpawnDev.RTC.Server --version 1.0.7
#r "nuget: SpawnDev.RTC.Server, 1.0.7"
#:package SpawnDev.RTC.Server@1.0.7
#addin nuget:?package=SpawnDev.RTC.Server&version=1.0.7
#tool nuget:?package=SpawnDev.RTC.Server&version=1.0.7
SpawnDev.RTC.Server
WebRTC signaling server for SpawnDev.RTC consumers. Hosts the WebTorrent tracker wire protocol so any ASP.NET Core app can run a room-based signaling endpoint with a single app.UseRtcSignaling("/announce") call. Bundles an embedded RFC 5766 STUN/TURN server that can run alongside on the same host.
For the client library see SpawnDev.RTC.
What this gives you
Three composable pieces, all opt-in:
| Piece | Method | Purpose |
|---|---|---|
| Signaling tracker | app.UseRtcSignaling("/announce") |
WebSocket endpoint that brokers WebRTC offer/answer between peers. Bit-compatible with the public WebTorrent tracker fleet — your clients (using SpawnDev.RTC, JS WebTorrent, libtorrent v2 with WSS, etc.) just point at your URL instead of wss://tracker.openwebtorrent.com. |
| STUN/TURN server | builder.Services.AddRtcStunTurn(opts => ...) |
Full RFC 5766 TURN with classic long-term creds OR ephemeral HMAC creds (RFC 8489 §9.2 / TURN REST API pattern). Optionally tracker-gated so only currently-announced peers can allocate. Replaces coturn for SpawnDev-style deployments. |
| Origin allowlist | property on the signaling-server options | Browser-side abuse protection: only origins on the allowlist can open a signaling WebSocket. Non-browser clients (desktop C#, curl, Node.js ws) bypass the check automatically because they don't send an Origin header. |
Install
<PackageReference Include="SpawnDev.RTC.Server" Version="1.0.5" />
Or run the standalone host directly: SpawnDev.RTC.ServerApp — single executable, Docker image, env-var configurable. Both the library and the app share the same internals; pick whichever suits your deployment.
Quickstart — embedded signaling
using SpawnDev.RTC.Server.Extensions;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseWebSockets();
app.UseRtcSignaling("/announce"); // your tracker is now live
app.Run();
Every WebTorrent-compatible client can now use wss://your-host/announce as a tracker URL. Peers in the same infohash room get paired automatically.
Quickstart — embedded STUN/TURN
Add it during service registration, separate from signaling. Both can run in the same host process:
using SpawnDev.RTC.Server.Extensions;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRtcStunTurn(opts =>
{
opts.Enabled = true;
opts.ListenAddress = "0.0.0.0";
opts.Port = 3478; // standard STUN/TURN port
opts.Realm = "your-domain.example";
// Long-term credentials (classic TURN auth)
opts.LongTermUsername = "alice";
opts.LongTermPassword = "alice-secret";
// OR ephemeral REST API credentials (preferred for production)
opts.EphemeralSharedSecret = "your-32-byte-shared-secret-here";
opts.EphemeralLifetime = TimeSpan.FromHours(1);
// OR tracker-gated ephemeral: only peers currently announced to your
// tracker can allocate. Combine with the signaling layer above.
opts.TrackerGated = true;
opts.RelayPortRangeStart = 49152; // for NAT port forwarding
opts.RelayPortRangeEnd = 65535;
});
var app = builder.Build();
app.UseWebSockets();
app.UseRtcSignaling("/announce");
app.Run();
Or bind from appsettings.json:
builder.Services.AddRtcStunTurn(builder.Configuration.GetSection("Turn"));
{
"Turn": {
"Enabled": true,
"Port": 3478,
"Realm": "your-domain.example",
"EphemeralSharedSecret": "your-32-byte-shared-secret-here"
}
}
Full options + walkthrough at Docs/stun-turn-server.md in the parent repo.
Origin allowlist
Browsers send an Origin header on every WebSocket connection. Setting AllowedOrigins rejects browser connections from origins not on the list — useful for keeping a tracker private to your own apps without firewalling the port:
app.UseRtcSignaling("/announce", opts =>
{
opts.AllowedOrigins = new[] { "https://app.example.com", "https://staging.example.com" };
});
Desktop / CLI clients (SpawnDev.RTC desktop, JS WebTorrent on Node, curl) don't send Origin; the allowlist auto-bypasses them so non-browser clients keep working. Locked by OriginAllowlist_E2E_MissingOriginBypassesList in the RTC test suite.
What's in the package
TrackerSignalingServer— the signaling endpoint. Speaks the same wire protocol astracker.openwebtorrent.com(JSONannounce/offer/answer/signalmessages over WebSocket). Per-room pairing keyed on info_hash.StunTurnServerHostedService— the embedded TURN server, registered as anIHostedServiceso the lifetime is managed by ASP.NET Core's host. Built on the bundled SipSorcery fork.EphemeralTurnCredentials— utility for issuing/validating ephemeral usernames + HMAC passwords. Pair withTurnServerConfig.ResolveHmacKeyfor tenant-aware credential rotation.UseRtcSignaling/AddRtcStunTurnextension methods onIApplicationBuilder/IServiceCollection.
Production deployments
hub.spawndev.com:44365 runs this exact stack. Real-world load: dozens of concurrent rooms, simultaneous TURN allocations, browser + desktop clients pairing through it daily.
For the standalone-executable + Docker route, see the SpawnDev.RTC.ServerApp project — same library, env-var-driven configuration, single deployable artifact.
Dependencies
SpawnDev.RTC1.1.6 (signaling client primitives, room key types)- ASP.NET Core 10 (
Microsoft.AspNetCore.Appframework reference) - SipSorcery fork (bundled inline via SpawnDev.RTC) — for the TURN server's underlying RFC 5766 implementation
Documentation
| Topic | Doc |
|---|---|
| Signaling protocol overview | Docs/signaling-overview.md |
| Running a tracker (this library) | Docs/run-a-tracker.md |
| STUN/TURN server reference | Docs/stun-turn-server.md |
License
MIT — see LICENSE.txt in the parent repository.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- SpawnDev.RTC (>= 1.1.8)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
v1.0.7 stable: rolls up rc.1's tracker-parity fixes plus 1.0.6's `peers`-field-omission fix into a single stable cut. Two parity fixes against the bittorrent-tracker JS reference (webtorrent/bittorrent-tracker server.js), found by `tracker-debug/verify-tracker-parity.mjs` head-to-head test against the local bittorrent-tracker npm package. (1) Answer-relay path: when an announce frame carries `answer + to_peer_id + offer_id` (a reply to a forwarded offer), the JS reference forwards the answer to the targeted peer and returns no announce response to the sender. The C# server now matches - was previously sending an extra announce-response frame in this case. (2) Stopped event: when an announce carries `event=stopped`, the JS reference removes the peer from the room AND sends back an announce-response with the updated counts (so `incomplete=0` etc. reflect post-stop state). The C# server now matches - was previously returning early without sending any response. Plus the `peers`-field-omission fix from 1.0.6: omit the `peers` field entirely (instead of `peers=[]`) when there are no peers to return, matching the JS reference. All three fixes only affect the announce-response framing; the data-channel WebRTC flow itself is unchanged. Verified end-to-end against the live hub at `wss://hub.spawndev.com:44365/announce`: all 6 scenarios PARITY OK against JS reference. Transitively pulls in SpawnDev.RTC 1.1.8 stable (Desktop OnBufferedAmountLow + browser connection-state polling fallback + opt-in DiagnosticsEnabled).