Proxeno.Keryx.Ice 0.1.9

dotnet add package Proxeno.Keryx.Ice --version 0.1.9
                    
NuGet\Install-Package Proxeno.Keryx.Ice -Version 0.1.9
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Proxeno.Keryx.Ice" Version="0.1.9" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Proxeno.Keryx.Ice" Version="0.1.9" />
                    
Directory.Packages.props
<PackageReference Include="Proxeno.Keryx.Ice" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Proxeno.Keryx.Ice --version 0.1.9
                    
#r "nuget: Proxeno.Keryx.Ice, 0.1.9"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Proxeno.Keryx.Ice@0.1.9
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Proxeno.Keryx.Ice&version=0.1.9
                    
Install as a Cake Addin
#tool nuget:?package=Proxeno.Keryx.Ice&version=0.1.9
                    
Install as a Cake Tool

Keryx

Keryx

CI NuGet version NuGet downloads License: Apache-2.0 .NET 10

A from-scratch WebRTC media stack for .NET. Pure managed C#, zero native dependencies, Apache-2.0. Stream real-time audio and video from a .NET server to any WebRTC client — the whole protocol suite (ICE, DTLS-SRTP, RTP/RTCP, SCTP) is implemented here, in this repository, against the RFCs. Keryx (κῆρυξ) is the Greek herald — the one who carries the message.

Status: pre-release (0.x). APIs will change. Every capability listed here is backed by a test in this repository — including a real-Chrome media interop test — and the Scope section says plainly what is and isn't here yet.

What you can build

Keryx is the sending side of WebRTC: one .NET process encodes once and streams to browsers and native clients over standard WebRTC — no plugin, no gateway, no native runtime on your server. It's built for low-latency, server-to-client media, such as:

  • Game & cloud-gaming streaming — render and encode on a server, play in a browser.
  • Camera, drone & robotics feeds — live video and telemetry to an operator's screen.
  • Screen share & remote desktop — a headless host streaming its own output.
  • Interactive media servers — anything that renders a frame and needs it on a remote display in well under a second.

The far end is ordinary WebRTC, so your client is a plain browser RTCPeerConnection and its <video> element — nothing Keryx-specific to install.

Goals

  • Pure managed. 100% C# on .NET 10 — no native library to cross-compile, ship, or keep patched. Cryptography comes only from System.Security.Cryptography; ciphers are never hand-rolled.
  • Genuinely open. Apache-2.0 from the first commit — embed it in commercial or proprietary products with no copyleft or source-available strings attached.
  • Faithful to the specs. Each protocol layer is implemented against its RFC, cites the clause in the source, and has a test behind every claim.
  • Strictly layered. Every protocol is its own package with no upward dependencies, testable in isolation over in-memory transports — including lossy and reordering ones.
  • Honest about maturity. A 0.x that tells you exactly what is proven, what is experimental, and what is not here.

Quickstart: offering H.264 to a browser

using Keryx;

var pc = new PeerConnection(new PeerConnectionConfig
{
    StunServers = { new IPEndPoint(IPAddress.Parse("74.125.250.129"), 19302) }, // optional
});

// Channels created before negotiation are DCEP-opened once SCTP associates.
var controller = pc.CreateDataChannel("controller", ordered: false, maxRetransmits: 0);
pc.OnPictureLossIndication += (_, e) => encoder.RequestKeyframe();

var offerSdp = await pc.CreateOfferAsync(ct);                       // -> to the browser
await pc.SetRemoteDescriptionAsync(answerSdp, SdpType.Answer, ct);  // <- browser's answer
pc.AddIceCandidate(candidate, sdpMid);                              // <- trickled candidates
await pc.WaitForConnectedAsync(TimeSpan.FromSeconds(15), ct);

pc.SendVideoFrame(annexBAccessUnit, rtpTimestamp90k);   // one H.264 access unit per call
pc.SendAudioFrame(opusPacket, rtpTimestamp48k);
(await controller).OnMessage += (isBinary, payload) => HandleInput(payload);

// Inbound NACKs are served from the send history as RTX automatically; read the link back:
var video = pc.GetStats().Video;
var lost = video?.Quality?.FractionLost;              // 0..1, from the peer's reception reports
var rtt = video?.Quality?.RoundTripTime;              // RFC 3550 LSR/DLSR arithmetic
var resent = video?.Retransmission?.PacketsRetransmitted;

That single surface runs ICE gathering, DTLS with fingerprint pinning, SRTP keying, SCTP data channels and the RTCP loops behind those calls.

What you get

The parts a real media server needs, exposed as first-class API instead of SDP string-splicing and raw RTCP parsing in your application:

  • Typed RTCP feedback. PLI, FIR, NACK and transport-cc arrive as dedicated events (OnPictureLossIndication, …), and a=rtcp-fb lines are emitted natively per configured codec. H.264 offers nack, nack pli and ccm fir by default.
  • NACK-driven retransmission that actually retransmits. Bare a=rtcp-fb nack is backed by a real RFC 4588 repair stream: an rtx codec, a dedicated SSRC published as a=ssrc-group:FID, and a ring of recently sent packets that inbound NACKs are served from — under a per-packet resend rate limit and a bandwidth budget, with counters on GetStats(). If the answer drops the rtx codec, retransmission is switched off rather than promised and not delivered.
  • Sender-side link quality. The reception-report blocks the browser sends are folded into GetStats() per track: fraction lost, cumulative loss, interarrival jitter and LSR/DLSR round-trip time — the signal a rate controller needs, without parsing RTCP yourself.
  • A codec-agnostic packetizer seam. H.264 (packetization-mode=1, STAP-A/FU-A) and Opus ship in-box; other codecs implement one interface (IRtpPayloadizer) plus one SdpCodec entry.

Architecture

Keryx architecture — the PeerConnection composition root built on Sdp, Rtp, Srtp, Sctp, Dtls, and Ice (over Stun), all resting on Keryx.Core

At runtime everything multiplexes over one UDP socket (BUNDLE + rtcp-mux): ICE consumes STUN, DTLS records (first byte 20–63) carry SCTP, and SRTP/SRTCP (128–191) carry media — see docs/architecture.md for the wire-path diagram and design rules, and docs/layers/ for per-layer design notes.

Packages

Package Contents
Keryx Composition root: the PeerConnection API. Reference this to get the whole stack.
Keryx.Core Binary readers/writers, the IDatagramTransport seam, logging abstraction.
Keryx.Stun STUN (RFC 5389) messages and client.
Keryx.Sdp Lossless SDP model, JSEP offer builder, answer negotiator.
Keryx.Rtp RTP, RTCP with typed feedback, H.264/Opus packetizers, IRtpPayloadizer.
Keryx.Dtls DTLS 1.2 handshake + record layer, DTLS-SRTP keying export.
Keryx.Srtp SRTP/SRTCP: AES-CM + HMAC-SHA1-80 and AEAD AES-GCM.
Keryx.Ice ICE agent (RFC 8445 subset, dual-stack IPv4/IPv6, aggressive nomination).
Keryx.Turn TURN client: relay allocations, permissions, channel binding (RFC 8656).
Keryx.Sctp SCTP over DTLS, DCEP data channels, partial reliability.

Packages are published as Proxeno.Keryx* on nuget.org; the assemblies and namespaces are Keryx.* (so using Keryx; is what you write). Reference Proxeno.Keryx to get everything.

Verification status

Every claim is backed by a test in this repository.

  • RFC test vectors — STUN: all four RFC 5769 vectors, byte-exact both directions · SRTP: RFC 3711 B.2 keystream + B.3 key derivation, RFC 7714 §16/§17 GCM · SCTP: CRC32c check vectors · RTP: header edge-case matrix, RFC 6184 golden packets.
  • Loopback integration — two PeerConnections over real UDP sockets: ICE connects, DTLS completes with mutual fingerprint pinning, 30 real H.264 access units arrive byte-identical, data channels round-trip (including 64 KB binary), PLI/FIR arrive as typed events, and a Generic NACK is answered with RFC 4588 RTX packets on the repair SSRC.
  • Loss sweeps — a seeded fault injector splices in under the sender's SRTP through PeerConnectionConfig.TransportInterceptor and drops, duplicates, reorders and delays media datagrams while STUN, DTLS and RTCP pass through untouched. At 1%, 5% and 15% uniform loss, and under ten-packet bursts, every packet the receiver could detect as missing comes back; with retransmission switched off at the same seeds, exactly those packets stay lost (2098-packet stream: 19 / 113 / 314 permanent holes).
  • Chrome interop — headless Chrome (tested with 151) answers a Keryx offer over HTTP signaling: ICE connects, DTLS completes (Keryx as server, SRTP_AES128_CM_HMAC_SHA1_80), Chrome decodes and renders the H.264 Keryx sends (60+ frames, 640×360, keyframes counted, video element playing) and both data channels echo. A second session runs the same fifteen seconds twice across a 5% lossy link: with RFC 4588 retransmission Chrome NACKs, accepts 79 of 80 destroyed packets back as repairs and decodes 433 frames at 29 fps with no freezes; without it, the same seed leaves 51 frames at 15 fps and five freezes. Run them locally with dotnet test tests/Keryx.IntegrationTests --filter "Category=ChromeInterop".
  • Soak — five minutes of 30 fps video across a 5% lossy, reordering link: 31,815 packets, 1,564 repaired, zero holes, zero history misses, and a managed heap that ends 0.9% below where it started (--filter "Category=Soak").

885 tests across ten projects (dotnet test Keryx.slnx), plus the trait-gated Chrome interop and soak suites.

Benchmarks

BenchmarkDotNet 0.15.8 (default job), Apple M5 Max / macOS 26.6 / .NET 10.0.11 arm64, run 2026-08-21. Measured, absolute numbers.

Benchmark Keryx Allocated
H.264 packetization, 25 KB access unit @ MTU 1200 0.85 μs 0 B
RTP header write + parse (12-byte header) 2.6 ns 0 B
SDP: generate full offer 1.9 μs 19.4 KB
SDP: parse Chrome answer 2.6 μs 25.7 KB
SRTP protect 1200-byte packet (AES-CM/SHA1-80) 1.1 μs 0 B

Zero allocation on the hot RTP / SRTP / packetization paths is a design goal; the SDP paths allocate the document they build or parse and are not on the per-packet path.

RFC coverage

RFC What Where tested
5389 (+5769 vectors) STUN, MESSAGE-INTEGRITY/FINGERPRINT dummy-length rule Keryx.Stun.Tests
8445 ICE: pair priority, checks, role conflict, prflx, nomination Keryx.Ice.Tests
4566 / 8829 (subset) SDP model, JSEP offer/answer Keryx.Sdp.Tests
3550 RTP header, SR/RR/SDES/BYE, NTP mapping Keryx.Rtp.Tests
4585 / 5104 PLI, FIR, GenericNack (PID/BLP), REMB parse Keryx.Rtp.Tests
draft-holmer-rmcat-transport-wide-cc-01 transport-cc feedback parse/build Keryx.Rtp.Tests
6184 / 7587 / 8285 H.264 STAP-A/FU-A, Opus, header extensions Keryx.Rtp.Tests
4588 / 5576 RTX packet format (OSN, own seq space), apt, ssrc-group:FID Keryx.Rtp.Tests, Keryx.Sdp.Tests, Keryx.IntegrationTests
3711 (B.2, B.3) / 7714 (§16, §17) SRTP/SRTCP AES-CM + GCM, ROC, replay Keryx.Srtp.Tests
5764 §4.2 DTLS-SRTP key split, use_srtp Keryx.Srtp.Tests, Keryx.Dtls.Tests
6347 / 5246 / 5288 / 7627 / 5705 DTLS 1.2 records, PRF vectors, AES-GCM, EMS, exporter Keryx.Dtls.Tests
9260 / 3758 / 8831 / 8832 SCTP, FORWARD-TSN, data channels, DCEP Keryx.Sctp.Tests
7983 / 5761 Demux on the bundled transport Keryx.Rtp.Tests, Keryx.IntegrationTests
8656 TURN allocations, permissions, channel binding Keryx.Turn.Tests

Scope

Implemented: the offerer-side media server path end to end — sendonly H.264 + Opus with BUNDLE/rtcp-mux, trickle ICE (in and out), DTLS 1.2 both roles with fingerprint pinning, SRTP AES-CM and AES-GCM, bidirectional data channels with partial reliability, typed RTCP feedback in both directions, RFC 4588 RTX retransmission driven by inbound NACKs, sender-side loss/jitter/RTT statistics from reception reports, a minimal recvonly answerer, and a raw RTP receive surface. Recent additions: a=extmap with outbound transport-wide-cc (TWCC) sequence numbers; TURN relay-candidate gathering (allocations, permissions, channel binding); and dual-stack IPv6 host / server-reflexive gathering with address-family-correct pairing.

Experimental (compiles and is tested, but not yet a production path): a send-side GCC bandwidth estimator and pacer (in Keryx.Rtp, not yet wired into the send loop), and simulcast transport primitives (RID / a=simulcast negotiation and ingest demux are complete; per-subscriber forwarding is scaffolded).

Not implemented (yet, honestly): ULPFEC and RED, REMB generation, regular (non-aggressive) nomination, renegotiation and ICE restart, IPv6 relay allocation, SCTP stream reset (RE-CONFIG), jitter buffering on the receive surface, audio receive processing. Per-layer simplifications are documented in docs/layers/.

Security

The DTLS and SRTP implementations verify everything the RFCs require — peer CertificateVerify over the transcript, Finished verify_data, fingerprint pinning against the SDP a=fingerprint, auth tags, anti-replay windows — and those checks are covered by tamper/replay tests. They have not yet received an independent security review; do not protect sensitive production traffic with them until they have. Primitives are the platform's, never hand-rolled. Details and reporting: SECURITY.md.

Contributing

Contributions are welcome — see CONTRIBUTING.md. The short version: layering is law, zero NuGet dependencies in src/, warnings are errors, wire parsers never throw on hostile input, and protocol claims cite their RFC section. Sign off commits with git commit -s (DCO).

License

Apache-2.0. See LICENSE. © Keryx contributors.

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Proxeno.Keryx.Ice:

Package Downloads
Proxeno.Keryx

Keryx: a from-scratch WebRTC stack for .NET. This package is the composition root - an RTCPeerConnection-style API over the Keryx STUN/ICE/SDP/DTLS/SRTP/RTP/SCTP layers.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.9 0 8/23/2026
0.1.8 23 8/22/2026
0.1.7 25 8/22/2026
0.1.6 38 8/22/2026