On this page

DTLS

History
Source Code: lib/dtls.js
Stability: 1Experimental

The node:dtls module provides an implementation of the Datagram Transport Layer Security (DTLS) protocol over UDP. DTLS provides TLS-equivalent security guarantees for datagram-based communication, including confidentiality, integrity, and authentication.

To use this module, it must be enabled at build time with the --experimental-dtls configure flag and at runtime with the --experimental-dtls CLI flag.

When using the Permission Model, the --allow-net flag must be passed to allow DTLS network operations. Without it, calling dtls.connect() or dtls.listen() will throw an ERR_ACCESS_DENIED error.

node --permission --allow-fs-read=* --experimental-dtls index.mjs
Error: Access to this API has been restricted. Use --allow-net to manage permissions.
  code: 'ERR_ACCESS_DENIED',
  permission: 'Net',
}

Creating a DTLSEndpoint instance without connecting or listening is permitted even without --allow-net, since no network I/O occurs until dtls.connect() or dtls.listen() is called.

DTLS is designed for UDP transport and differs from TLS in several key ways:

  • No stream guarantees: Messages may arrive out of order or be lost. DTLS preserves datagram semantics.
  • One socket, many peers: A single UDP socket can serve multiple DTLS sessions. The DTLSEndpoint manages this multiplexing.
  • Cookie exchange: DTLS servers use a stateless cookie mechanism (HelloVerifyRequest) to prevent denial-of-service amplification attacks.
  • Retransmission: DTLS handles handshake retransmission internally since UDP does not guarantee delivery.
M

dtls.listen

History
dtls.listen(callback, options): DTLSEndpoint
Attributes
callback:Function
Called for each new DTLS session accepted by the server.
session:DTLSSession
The new session.
options:Object
Server certificate in PEM format. Required.
Server private key in PEM format. Required.
port:number
Port to bind to. Required.
host?:string
Address to bind to. Default: '0.0.0.0' .
CA certificates in PEM format.
ciphers:string
OpenSSL cipher list string.
alpn:string[]|Buffer
ALPN protocol names.
srtp:string
Colon-separated SRTP protection profile names (e.g., 'SRTP_AES128_CM_SHA1_80:SRTP_AEAD_AES_128_GCM' ).
requestCert?:boolean
Request client certificate. Default: false .
mtu?:number
Maximum transmission unit for DTLS records. Default: 1200 .
Returns:DTLSEndpoint

Creates a DTLS server bound to the specified address and port. The server uses automatic HMAC-based cookie exchange for DoS protection.

import { listen } from 'node:dtls';
import { readFileSync } from 'node:fs';

const endpoint = listen((session) => {
  session.onmessage = (data) => {
    console.log('Received:', data.toString());
    session.send('pong');
  };

  session.onhandshake = (protocol) => {
    console.log('Handshake complete:', protocol);
  };
}, {
  cert: readFileSync('server-cert.pem'),
  key: readFileSync('server-key.pem'),
  port: 4433,
});

console.log('DTLS server listening on', endpoint.address);
M

dtls.connect

History
dtls.connect(host, port, options?): DTLSSession
Attributes
host:string
Remote host to connect to.
port:number
Remote port to connect to.
options:Object
CA certificates in PEM format.
Client certificate in PEM format.
Client private key in PEM format.
rejectUnauthorized?:boolean
Reject connections with unverifiable certificates. Default: true .
bindHost?:string
Local bind address. Default: '0.0.0.0' .
bindPort?:number
Local bind port. Default: 0 (ephemeral).
alpn:string[]|Buffer
ALPN protocol names.
srtp:string
SRTP protection profile names.
mtu?:number
Maximum transmission unit. Default: 1200 .
Returns:DTLSSession

Connects to a DTLS server. Returns a DTLSSession whose opened property is a Promise that resolves when the handshake completes.

import { connect } from 'node:dtls';
import { readFileSync } from 'node:fs';

const session = connect('localhost', 4433, {
  ca: [readFileSync('ca-cert.pem')],
});

await session.opened;
session.send('hello');

session.onmessage = (data) => {
  console.log('Received:', data.toString());
};
C

DTLSEndpoint

History

Manages a UDP socket and multiplexes DTLS sessions.

Returns:Object
{ address, family, port }

The local address the endpoint is bound to.

Returns:DTLSEndpointState

Shared state object with properties:

Attributes
bound:boolean
listening:boolean
closing:boolean
destroyed:boolean
sessionCount:number
busy:boolean
P

endpoint.stats

History

The statistics collected for this endpoint. Read only. The stats object is live and updated by the C++ internals as data flows through the endpoint.

Attributes

When true, the endpoint rejects new incoming connections. Can be set to implement backpressure.

endpoint.close(): Promise
Returns:Promise
Resolves when the endpoint is fully closed.

Gracefully closes the endpoint. All active sessions are closed with close_notify alerts before the UDP socket is released.

endpoint.destroy(error?): void

Immediately destroys the endpoint without sending close_notify alerts.

Attributes
Resolves when the endpoint has fully closed.
endpoint[Symbol.asyncDispose](): void

Equivalent to calling endpoint.close().

C

DTLSEndpoint.Stats

History

A view of the collected statistics for an endpoint.

P

endpointStats.createdAt

History
Type:bigint
A timestamp indicating when the endpoint was created. Read only.
P

endpointStats.destroyedAt

History
Type:bigint
A timestamp indicating when the endpoint was destroyed. Read only.
P

endpointStats.bytesReceived

History
Type:bigint
The total number of bytes received by this endpoint. Read only.
P

endpointStats.bytesSent

History
Type:bigint
The total number of bytes sent by this endpoint. Read only.
P

endpointStats.packetsReceived

History
Type:bigint
The total number of UDP packets received by this endpoint. Read only.
P

endpointStats.packetsSent

History
Type:bigint
The total number of UDP packets sent by this endpoint. Read only.
P

endpointStats.serverSessions

History
Type:bigint
The total number of peer-initiated sessions accepted by this endpoint. Read only.
P

endpointStats.clientSessions

History
Type:bigint
The total number of sessions initiated by this endpoint. Read only.
P

endpointStats.serverBusyCount

History
Type:bigint
The total number of incoming connections rejected because the endpoint was marked busy. Read only.
P

endpointStats.isConnected

History
Type:boolean

true if the stats object is still connected to the underlying endpoint. Once the endpoint is destroyed, the stats become a stale snapshot.

C

DTLSSession

History

Represents a DTLS association with a single remote peer.

session.send(data): number
Attributes
The data to send.
Returns:number
The number of bytes written to the DTLS layer.

Send application data to the peer. The data is encrypted by DTLS before being sent over UDP. Can only be called after the handshake completes (session.opened has resolved).

session.close(): Promise
Returns:Promise
Resolves when the session is closed.

Initiates a graceful DTLS shutdown by sending a close_notify alert.

session.destroy(error?): void

Immediately destroys the session without sending close_notify.

Attributes
Resolves with { protocol } when the DTLS handshake completes.
Attributes
Resolves when the session is fully closed.
Returns:Object
{ address, family, port }
Returns:string
The negotiated DTLS protocol version (e.g., 'DTLSv1.2' ).
Returns:Object
{ name, standardName, version }
The peer's certificate in PEM format.
The negotiated ALPN protocol.
The negotiated SRTP protection profile name.
P

session.stats

History

The statistics collected for this session. Read only. The stats object is live and updated as data flows through the session.

session.exportKeyingMaterial(length, label, context?): Buffer
Attributes
length:number
Number of bytes to export.
label:string
The label for the exported keying material.
context:Buffer
Optional context value.
Returns:Buffer

Exports keying material from the DTLS session, as defined in RFC 5705. This is commonly used with DTLS-SRTP to derive encryption keys for media streams.

C

DTLSSession.Stats

History

A view of the collected statistics for a session.

P

sessionStats.createdAt

History
Type:bigint
A timestamp indicating when the session was created. Read only.
P

sessionStats.destroyedAt

History
Type:bigint
A timestamp indicating when the session was destroyed. Read only.
P

sessionStats.closingAt

History
Type:bigint
A timestamp indicating when close() was called. Read only.
P

sessionStats.handshakeCompletedAt

History
Type:bigint
A timestamp indicating when the DTLS handshake completed. Read only.
P

sessionStats.bytesReceived

History
Type:bigint
The total number of application data bytes received. Read only.
P

sessionStats.bytesSent

History
Type:bigint
The total number of application data bytes sent. Read only.
P

sessionStats.messagesReceived

History
Type:bigint
The total number of application messages received. Read only.
P

sessionStats.messagesSent

History
Type:bigint
The total number of application messages sent. Read only.
P

sessionStats.retransmitCount

History
Type:bigint
The total number of DTLS handshake retransmissions. Read only.
P

sessionStats.isConnected

History
Type:boolean

true if the stats object is still connected to the underlying session. Once the session is destroyed, the stats become a stale snapshot.

Attributes

Set to receive application data from the peer.

Attributes

Set to receive error notifications.

Attributes

Set to receive handshake completion notifications.

Attributes

Set to receive TLS key log lines (for debugging with Wireshark).

session[Symbol.asyncDispose](): void

Equivalent to calling session.close().

DTLS-SRTP is used by WebRTC for media encryption. The DTLS handshake negotiates the SRTP protection profile and provides keying material.

import { listen, connect } from 'node:dtls';
import { readFileSync } from 'node:fs';

// Server with SRTP
const server = listen((session) => {
  session.onhandshake = () => {
    console.log('SRTP profile:', session.srtpProfile);
    const keys = session.exportKeyingMaterial(
      60,
      'EXTRACTOR-dtls_srtp',
    );
    console.log('SRTP keying material:', keys);
  };
}, {
  cert: readFileSync('server-cert.pem'),
  key: readFileSync('server-key.pem'),
  port: 5004,
  srtp: 'SRTP_AES128_CM_SHA1_80:SRTP_AEAD_AES_128_GCM',
});

// Client with SRTP
const session = connect('localhost', 5004, {
  rejectUnauthorized: false,
  srtp: 'SRTP_AEAD_AES_128_GCM:SRTP_AES128_CM_SHA1_80',
});

await session.opened;
console.log('Negotiated SRTP:', session.srtpProfile);
const keys = session.exportKeyingMaterial(60, 'EXTRACTOR-dtls_srtp');

Since libuv does not currently support path MTU discovery, the DTLS module uses a conservative default MTU of 1200 bytes. This value works across virtually all network paths but may be suboptimal for local networks.

The MTU can be configured via the mtu option:

// For a local network where you know the path MTU
const endpoint = listen(callback, {
  // ...
  mtu: 1400,
});

The minimum allowed MTU is 256 bytes. The maximum is 65535.