npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

node-rtc-connection

v2.1.1

Published

WebRTC DataChannel implementation for Node.js with STUN, TURN, NAT traversal, and encryption. Pure Node.js, no native dependencies.

Readme

node-rtc-connection

npm version npm downloads CI Node.js TypeScript License: MIT

A from-scratch, pure-Node.js WebRTC data-channel implementation that interoperates with browsers. No native dependencies — the entire ICE / DTLS / SCTP stack is built on Node's crypto and dgram. Written in TypeScript; ships type declarations.

Features

  • Browser interoperable: Verified end-to-end against Chromium (Playwright) and OpenSSL
  • Real protocols, not stubs: Genuine DTLS 1.2 handshake + SCTP association over UDP
  • ICE (RFC 8445): connectivity checks with MESSAGE-INTEGRITY, host/srflx/relay candidates
  • STUN/TURN (RFC 5389/5766): NAT traversal and relay for restrictive networks, including encrypted turns: (TURN-over-DTLS and TURN-over-TLS)
  • DTLS 1.2 (RFC 6347): ECDHE_ECDSA_AES128_GCM, mutual auth, self-signed ECDSA P-256 certs
  • SCTP + DCEP (RFC 8831/8832): ordered/unordered data channels, string + binary
  • W3C API: familiar RTCPeerConnection / RTCDataChannel surface, including EventTarget, on... handlers, addEventListener, and DOM-style errors
  • Pure Node.js, no native deps; CommonJS + ESM bundles with TypeScript types

Connection Flow

flowchart TD
  app["Application code"] --> offerer["Offerer RTCPeerConnection"]
  app --> answerer["Answerer RTCPeerConnection"]
  offerer -->|createDataChannel| localChannel["Local RTCDataChannel"]
  offerer -->|createOffer + setLocalDescription| gatherOffer["ICE gathering"]
  gatherOffer -->|offer SDP + candidates| signaling["Your signaling channel"]
  signaling -->|setRemoteDescription + addIceCandidate| answerer
  answerer -->|createAnswer + setLocalDescription| gatherAnswer["ICE gathering"]
  gatherAnswer -->|answer SDP + candidates| signaling
  signaling -->|setRemoteDescription + addIceCandidate| offerer
  offerer --> checks["ICE connectivity checks"]
  answerer --> checks
  checks --> dtls["DTLS 1.2 handshake<br/>fingerprint verified"]
  dtls --> sctp["SCTP association"]
  sctp --> dcep["DCEP channel open"]
  dcep --> open["RTCDataChannel open"]
  open --> messages["string / binary / Blob messages"]

Installation

npm install node-rtc-connection

Works from both CommonJS and ES modules, and bundles TypeScript declarations:

// CommonJS
const { RTCPeerConnection } = require('node-rtc-connection');

// ES modules / TypeScript
import { RTCPeerConnection } from 'node-rtc-connection';

Quick Start

Basic Local Connection (No STUN/TURN)

const { RTCPeerConnection } = require('node-rtc-connection');

// Create two peer connections
const pc1 = new RTCPeerConnection({ iceServers: [] });
const pc2 = new RTCPeerConnection({ iceServers: [] });

// Set up data channel on peer 1
const channel = pc1.createDataChannel('chat');

channel.on('open', () => {
  console.log('Channel opened!');
  channel.send('Hello from Peer 1!');
});

channel.on('message', (event) => {
  console.log('Received:', event.data);
});

// Peer 2 receives data channel
pc2.on('datachannel', (event) => {
  const channel = event.channel;
  
  channel.on('message', (event) => {
    console.log('Received:', event.data);
    channel.send('Hello from Peer 2!');
  });
});

// Exchange ICE candidates
pc1.on('icecandidate', (e) => {
  if (e.candidate) pc2.addIceCandidate(e.candidate).catch(() => {});
});

pc2.on('icecandidate', (e) => {
  if (e.candidate) pc1.addIceCandidate(e.candidate).catch(() => {});
});

// Signaling (offer/answer exchange)
async function connect() {
  const offer = await pc1.createOffer();
  await pc1.setLocalDescription(offer);
  
  await pc2.setRemoteDescription(pc1.localDescription);
  const answer = await pc2.createAnswer();
  await pc2.setLocalDescription(answer);
  
  await pc1.setRemoteDescription(pc2.localDescription);
}

connect();

With STUN Server (NAT Traversal)

const { RTCPeerConnection } = require('node-rtc-connection');

const config = {
  iceServers: [
    { urls: 'stun:stun.l.google.com:19302' }
  ]
};

const pc = new RTCPeerConnection(config);

// Listen for gathered ICE candidates
pc.on('icecandidate', (event) => {
  if (event.candidate) {
    console.log('ICE Candidate:', event.candidate.candidate);
    // Send to remote peer via your signaling channel
  }
});

// Create offer and start ICE gathering
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);

With TURN Server (Relay Support)

const { RTCPeerConnection } = require('node-rtc-connection');

const config = {
  iceServers: [
    { urls: 'stun:stun.l.google.com:19302' },
    {
      urls: 'turn:turn.example.com:3478',
      username: 'your-username',
      credential: 'your-password'
    }
  ]
};

const pc = new RTCPeerConnection(config);

pc.on('icecandidate', (event) => {
  if (event.candidate) {
    const candidate = event.candidate.candidate;
    
    // Check candidate type
    if (candidate.includes('typ relay')) {
      console.log('TURN relay candidate:', candidate);
    } else if (candidate.includes('typ srflx')) {
      console.log('STUN reflexive candidate:', candidate);
    } else if (candidate.includes('typ host')) {
      console.log('Host candidate:', candidate);
    }
  }
});

Configuration Options

const config = {
  // Array of ICE servers (STUN/TURN)
  iceServers: [
    { 
      urls: 'stun:stun.l.google.com:19302' 
    },
    {
      urls: [
        'turn:turn.example.com:3478?transport=udp',
        'turn:turn.example.com:3478?transport=tcp'
      ],
      username: 'user',
      credential: 'pass'
    }
  ],
  
  // ICE transport policy
  iceTransportPolicy: 'all', // 'all' or 'relay'
  
  // Bundle policy
  bundlePolicy: 'balanced', // 'balanced', 'max-bundle', or 'max-compat'
  
  // RTCP mux policy
  rtcpMuxPolicy: 'require', // 'negotiate' or 'require'
  
  // ICE candidate pool size
  iceCandidatePoolSize: 0
};

const pc = new RTCPeerConnection(config);

With iceTransportPolicy: 'relay', only relay candidates are gathered. Host UDP sockets are not opened just to be filtered later, which keeps relay-only deployments from consuming local ports unnecessarily.

ICE server URLs

ICE server URLs are parsed with query-string support:

const config = {
  iceServers: [
    { urls: 'stun:stun.l.google.com:19302' },
    {
      urls: [
        'turn:turn.example.com:3478?transport=udp',
        'turn:turn.example.com:53?transport=udp'
      ],
      username: 'user',
      credential: 'pass'
    }
  ]
};

URL format: stun:host[:port] and turn(s):host[:port][?transport=udp|tcp&...]. The default port is 3478 (5349 for the turns: scheme).

Transport support: the turns: scheme is encrypted end-to-end to the TURN server — DTLS over UDP (turns:host:5349) or TLS over TCP (turns:host:5349?transport=tcp). Plain turn: (and STUN srflx) use UDP. Unknown query parameters are preserved and ignored.

STUN/TURN gather requests use finite timeouts. Closing a connection while a STUN/TURN request is in flight settles the pending request and releases the underlying socket or stream.

TLS certificate validation (turns: over TCP)

For TURN-over-TLS, the server's certificate is validated by default. To accept a self-signed or otherwise unverifiable certificate (e.g. a local/test TURN server), set rejectUnauthorized: false on that ICE server entry — this is insecure and intended for development only:

const config = {
  iceServers: [
    {
      urls: 'turns:turn.example.com:5349?transport=tcp',
      username: 'user',
      credential: 'pass',
      rejectUnauthorized: false // accept self-signed cert (insecure)
    }
  ]
};

Transport Stability and Security

The transport stack is designed to fail closed around peer identity:

  • Remote SDP must include a supported a=fingerprint:sha-256 ... line. SDP without a supported DTLS fingerprint is rejected by setRemoteDescription().
  • DTLS peers are verified against the SDP fingerprint before application data is accepted.
  • DTLS server mode requires the peer to prove possession of its certificate with CertificateVerify.

Reliable data-channel messages are carried over SCTP DATA chunks. If a DATA chunk is not acknowledged by SACK, the association retransmits it with exponential backoff and aborts after repeated failure instead of keeping an undeliverable message queued forever.

Data Channel API

// Create data channel with options
const channel = pc.createDataChannel('myChannel', {
  ordered: true,           // Guarantee message order
  protocol: 'custom',      // Sub-protocol
  negotiated: false        // Set true only for out-of-band negotiation
});

channel.binaryType = 'arraybuffer'; // or 'blob' for received binary frames

// Events
channel.on('open', () => {
  console.log('Channel opened');
});

channel.on('close', () => {
  console.log('Channel closed');
});

channel.on('error', (error) => {
  console.error('Channel error:', error);
});

channel.on('message', (event) => {
  console.log('Message received:', event.data);
});

// Send data
channel.send('Hello World');
channel.send(new Uint8Array([1, 2, 3, 4]));
channel.send(Buffer.from([1, 2, 3, 4])); // Binary data
channel.send(new Blob([Uint8Array.from([1, 2, 3, 4])]));

// Close channel
channel.close();

Partial reliability options (maxRetransmits and maxPacketLifeTime) are currently rejected because PR-SCTP abandon / FORWARD-TSN behavior is not implemented. Reliable channels use SCTP retransmission until the peer acknowledges DATA chunks.

W3C Compatibility Notes

This package targets browser-compatible WebRTC data channels. It does not implement media tracks, transceivers, RTP senders/receivers, or the WebRTC stats API.

Implemented W3C-facing behavior includes:

  • RTCPeerConnection and RTCDataChannel extend native EventTarget. Node-style on(), once(), off(), removeListener(), listenerCount(), and emit() are retained for existing users.
  • addEventListener() supports once, capture, and signal options for public peer-connection and data-channel events.
  • setLocalDescription() and setRemoteDescription() maintain separate current and pending local/remote descriptions. localDescription and remoteDescription return the pending description when one exists, matching browser behavior.
  • Offer/answer/pranswer/rollback signaling transitions are validated, and a remote offer applied during local-offer glare performs the W3C implicit rollback path.
  • canTrickleIceCandidates is null until a remote description is applied, then reflects whether the remote SDP advertises trickle ICE.
  • addIceCandidate() accepts null or an empty candidate as end-of-candidates, validates candidate targets against the active remote SDP (including pending remote SDP), gives sdpMid precedence over sdpMLineIndex, checks usernameFragment against the current/pending remote generation, and appends valid candidates to the matching remote SDP.
  • RTCSessionDescription requires type, defaults missing sdp to '', and serializes with non-null type and sdp fields.
  • RTCIceCandidate accepts missing or null sdpMid/sdpMLineIndex at construction time. Non-empty targetless candidates are rejected by addIceCandidate(), where the W3C validation order applies.
  • RTCDataChannel supports text, ArrayBuffer, typed arrays, Node Buffer, and Blob sends. Received binary data is delivered as ArrayBuffer or Blob according to binaryType.
  • RTCError extends DOMException with name === 'OperationError' and the W3C errorDetail values.
  • RTCPeerConnection.generateCertificate() accepts the W3C-required ECDSA P-256 and RSA-2048 algorithm identifiers. The DTLS transport currently negotiates only the ECDSA P-256 cipher suite, so configured RSA certificates are rejected for transport use.

RTCPeerConnection Events

const pc = new RTCPeerConnection(config);

// ICE candidate discovered
pc.on('icecandidate', (event) => {
  // event.candidate contains the ICE candidate
});

// ICE gathering state changed
pc.on('icegatheringstatechange', () => {
  console.log('Gathering state:', pc.iceGatheringState);
  // 'new', 'gathering', or 'complete'
});

// ICE connection state changed
pc.on('iceconnectionstatechange', () => {
  console.log('ICE state:', pc.iceConnectionState);
  // 'new', 'checking', 'connected', 'completed', 'failed', 'disconnected', 'closed'
});

// Connection state changed
pc.on('connectionstatechange', () => {
  console.log('Connection state:', pc.connectionState);
  // 'new', 'connecting', 'connected', 'disconnected', 'failed', 'closed'
});

// Signaling state changed
pc.on('signalingstatechange', () => {
  console.log('Signaling state:', pc.signalingState);
  // 'stable', 'have-local-offer', 'have-remote-offer', 'have-local-pranswer', 'have-remote-pranswer', 'closed'
});

// Data channel received (for answerer)
pc.on('datachannel', (event) => {
  const channel = event.channel;
  console.log('Received data channel:', channel.label);
});

// Negotiation needed
pc.on('negotiationneeded', () => {
  console.log('Negotiation needed');
});

EventEmitter listeners, W3C property handlers, and addEventListener are all supported for public peer-connection and data-channel events:

pc.onicecandidate = (event) => {
  if (event.candidate) signaling.send(event.candidate);
};

pc.addEventListener('datachannel', (event) => {
  event.channel.onmessage = (messageEvent) => {
    console.log(messageEvent.data);
  };
}, { once: true });

Complete Example: Two-Peer Communication

const { RTCPeerConnection } = require('node-rtc-connection');

async function createPeerConnection() {
  const config = {
    iceServers: [
      { urls: 'stun:stun.l.google.com:19302' }
    ]
  };

  // Create peer connections
  const offerer = new RTCPeerConnection(config);
  const answerer = new RTCPeerConnection(config);

  // Exchange ICE candidates
  offerer.on('icecandidate', (e) => {
    if (e.candidate) answerer.addIceCandidate(e.candidate).catch(() => {});
  });

  answerer.on('icecandidate', (e) => {
    if (e.candidate) offerer.addIceCandidate(e.candidate).catch(() => {});
  });

  // Set up data channel on offerer
  const channel = offerer.createDataChannel('chat');

  channel.on('open', () => {
    console.log('Offerer: Channel opened');
    channel.send('Hello from offerer!');
  });

  channel.on('message', (event) => {
    console.log('Offerer received:', event.data);
  });

  // Answerer receives data channel
  answerer.on('datachannel', (event) => {
    const channel = event.channel;

    channel.on('open', () => {
      console.log('Answerer: Channel opened');
    });

    channel.on('message', (event) => {
      console.log('Answerer received:', event.data);
      channel.send('Hello from answerer!');
    });
  });

  // Perform signaling
  const offer = await offerer.createOffer();
  await offerer.setLocalDescription(offer);

  await answerer.setRemoteDescription(offerer.localDescription);
  const answer = await answerer.createAnswer();
  await answerer.setLocalDescription(answer);

  await offerer.setRemoteDescription(answerer.localDescription);

  // Wait for connection
  await new Promise(resolve => setTimeout(resolve, 2000));

  // Clean up
  channel.close();
  offerer.close();
  answerer.close();
}

createPeerConnection().catch(console.error);

Example Files

The package includes runnable examples in examples/:

  • examples/node-to-node.ts — Two node-rtc-connection peers in one process establish a real data channel through a TURN server and exchange string + binary messages. The quickest way to see the full ICE/DTLS/SCTP stack work. Configure the server via TURN_URL/TURN_USER/TURN_PASS (defaults match the test-suite coturn), and set RELAY_ONLY=1 to force traffic through the relay.
  • examples/browser-server.ts + examples/browser-client.html — A Node.js HTTP server that runs a node-rtc-connection peer (the offerer) and serves a chat page. A browser opens the page, runs its native RTCPeerConnection as the answerer, and the two establish a genuine WebRTC data channel over UDP.

Run them (the examples are TypeScript, run via tsx):

# Node ↔ Node
npm run example:node

# Node ↔ Browser — then open http://localhost:3000
npm run example:browser

The browser example uses plain HTTP for signaling and folds ICE candidates into the SDP (non-trickle) to keep it simple. A production app would typically use WebSockets with trickle ICE.

API Reference

RTCPeerConnection

Constructor

new RTCPeerConnection(configuration?)

Methods

  • createOffer(options?) - Create an SDP offer without changing signaling state
  • createAnswer(options?) - Create an SDP answer after a remote offer is pending
  • setLocalDescription(description?) - Set local SDP. If omitted, an offer or answer is created implicitly from the current signaling state. ICE gathering starts immediately; while candidates are trickled through icecandidate, gathered candidates are also folded into localDescription.sdp once gathering completes for non-trickle signaling flows.
  • setRemoteDescription(description) - Set remote SDP, validate DTLS fingerprint presence, update current/pending descriptions, and apply offer-glare rollback when required.
  • addIceCandidate(candidate) - Add a remote ICE candidate or end-of-candidates (null or { candidate: '' }). A remote description must already be set.
  • createDataChannel(label, options?) - Create a reliable data channel. ordered: false is supported. maxRetransmits and maxPacketLifeTime are rejected until PR-SCTP abandon semantics are implemented.
  • getConfiguration() / setConfiguration(configuration) - Read or replace the peer-connection configuration.
  • addEventListener(type, listener, options?) / removeEventListener(type, listener, options?) - Browser-compatible event listener methods.
  • close() - Close the connection and release its transport stack (ICE/DTLS/ SCTP sockets, timers, and buffers). Closes any open data channels first.
  • RTCPeerConnection.generateCertificate(algorithm) - Generate an RTCCertificate from an ECDSA P-256 or RSA-2048 algorithm identifier.

Properties

  • localDescription - Local SDP description
  • remoteDescription - Remote SDP description
  • currentLocalDescription / currentRemoteDescription - Stable negotiated descriptions, or null
  • pendingLocalDescription / pendingRemoteDescription - Descriptions being negotiated, or null
  • canTrickleIceCandidates - null before remote SDP, then true or false
  • signalingState - Current signaling state
  • iceGatheringState - ICE gathering state
  • iceConnectionState - ICE connection state
  • connectionState - Overall connection state
  • sctp - Underlying SCTP transport object when the stack exists, otherwise null

RTCDataChannel

Methods

  • send(data) - Send string, ArrayBuffer, a typed array / ArrayBufferView, Node Buffer, or Blob. Throws InvalidStateError if the channel is not open, TypeError if the message exceeds the negotiated SCTP max-message-size, and OperationError if the transport is unavailable.
  • addEventListener(type, listener, options?) / removeEventListener(type, listener, options?) - Browser-compatible event listener methods.
  • close() - Close the channel. The transport drops its reference and the channel detaches its internal listeners, so closed channels are reclaimed (detach your own 'message'/'open' handlers if you keep the reference).

Properties

  • label - Channel label
  • ordered - Whether messages are ordered
  • maxRetransmits - Maximum retransmissions. Non-null partial reliability is currently rejected by createDataChannel().
  • maxPacketLifeTime - Maximum packet lifetime. Non-null partial reliability is currently rejected by createDataChannel().
  • protocol - Sub-protocol
  • negotiated - Whether manually negotiated
  • id - Channel ID
  • readyState - Current state ('connecting', 'open', 'closing', 'closed')
  • bufferedAmount - Bytes queued for outbound SCTP DATA that have not yet been acknowledged by the peer
  • bufferedAmountLowThreshold - Threshold for bufferedamountlow; the event fires when bufferedAmount crosses from above the threshold to at or below it
  • binaryType - 'arraybuffer' (default) or 'blob'; controls how received binary frames are delivered

RTCSessionDescription

  • new RTCSessionDescription({ type, sdp }) - Construct a description. type is required and must be one of 'offer', 'pranswer', 'answer', or 'rollback'; missing sdp defaults to ''.
  • type / sdp - Read-only getters.
  • toJSON() - Returns { type, sdp }.

RTCIceCandidate

  • new RTCIceCandidate(init?) - Construct an ICE candidate object. Missing candidate defaults to ''; missing sdpMid, sdpMLineIndex, and usernameFragment default to null.
  • sdpMLineIndex is validated as an unsigned short.
  • Parsed candidate attributes include foundation, component ('rtp' or 'rtcp'), protocol, priority, address, port, type, tcpType, relatedAddress, relatedPort, relayProtocol, and url.
  • toJSON() returns the W3C candidate fields, including usernameFragment: null when no fragment is present.

RTCCertificate

const ecdsa = await RTCPeerConnection.generateCertificate({
  name: 'ECDSA',
  namedCurve: 'P-256'
});

const rsa = await RTCPeerConnection.generateCertificate({
  name: 'RSASSA-PKCS1-v1_5',
  modulusLength: 2048,
  publicExponent: new Uint8Array([1, 0, 1])
});
  • ECDSA P-256 and RSA-2048 certificate generation are supported.
  • expires on generation algorithms is a validity duration in milliseconds and is capped at 365 days.
  • RTCCertificate.generateCertificate() also accepts package convenience options such as { keyType: 'ECDSA' } or { keyType: 'RSA', rsaModulusLength: 2048 }.
  • getFingerprints() returns SDP-ready fingerprints; toPEM() and fromPEM() support serialization.

Configured certificates used for actual DTLS transport must currently be ECDSA P-256. RSA certificate objects can be generated for W3C API compatibility, but RSA transport negotiation is not implemented.

RTCError

  • new RTCError({ errorDetail }, message?) creates a DOMException subclass with name === 'OperationError'.
  • errorDetail is required and must be one of the W3C detail strings exposed on RTCError.DetailType.
  • Optional sdpLineNumber, sctpCauseCode, receivedAlert, and sentAlert fields are exposed as read-only getters and included by toJSON() when set.

Requirements

  • Node.js 18 or higher
  • UDP network access for ICE connectivity (and to a TURN server, if used)

Setting Up Your Own TURN Server

For production use, it's recommended to run your own TURN server using coturn:

# Install coturn
apt-get install coturn

# Basic configuration (plain TURN over UDP)
turnserver -v -L 0.0.0.0 -a -u user:password -r realm

For the encrypted turns: scheme, give coturn an ECDSA certificate (required for the ECDHE_ECDSA cipher suite this library negotiates) and enable the TLS/DTLS listener:

turnserver -v -L 0.0.0.0 -a -u user:password -r realm \
  --tls-listening-port=5349 \
  --cert=/path/to/cert.pem --pkey=/path/to/key.pem

Then connect with turns:host:5349 (DTLS) or turns:host:5349?transport=tcp (TLS). For a self-signed cert, set rejectUnauthorized: false on the ICE server entry (see TLS certificate validation).

Development

The project is written in strict TypeScript. Sources live in src/; tests in test/ run directly through tsx (no precompile step).

npm run build          # rollup → minified dist/ bundles + dist/types/ declarations
npm run typecheck      # strict tsc --noEmit over src + tests
npm test               # full suite (auto-starts a coturn container for the TURN test)
npm run test:unit      # SKIP_INTEGRATION=1 — no Docker / browser / external servers
npm run test:coverage  # full suite under c8

The full test suite proves interoperability against external references: DTLS handshakes against openssl, an end-to-end data channel against real Chromium (via Playwright), and a relay path against a real coturn server. Integration tests skip gracefully when their dependency (Docker, openssl, Chromium) is unavailable or when SKIP_INTEGRATION=1.

License

MIT

Contributing

Contributions are welcome! Please read CONTRIBUTING.md for the development workflow and conventions, and our Code of Conduct. Security issues should be reported privately — see SECURITY.md. Release notes live in CHANGELOG.md.

Acknowledgments

This is a from-scratch, pure-Node.js implementation that follows the relevant IETF RFCs (8445 ICE, 6347 DTLS 1.2, 8831 SCTP-over-DTLS, 8832 DCEP) and the W3C WebRTC specification.