- Connection establishment with TLS 1.3 handshake
- Connection close (immediate and draining states)
- Idle timeout enforcement (configurable via
idle_timeoutoption) - Lazy idle and keep-alive timers: armed once and re-armed only when they
fire, using the
last_activitytimestamp, so steady-state traffic does not cancel and reschedule a timer on every packet - Version negotiation: server emits a Version Negotiation packet for an unknown version; a client that receives one and shares no version closes with
{version_negotiation, Versions}(RFC 9000 §6.2) - Retry packets for address validation
- IPv6 client connections: hostname, IP-literal (bracketed or bare), or
inet:ip_address()tuple host - Happy Eyeballs v2 (RFC 8305): dual-stack hostnames race IPv6-first, with
happy_eyeballs,family,connection_attempt_delayandconnect_timeoutoptions onquic:connect/4 - Latency spin bit (RFC 9000 §17.4) with
spin_bit => true | false - NEW_TOKEN frame dispatch (server rejects peer-received tokens per §8.1.3); client caches received tokens keyed by
{Host, Port}and reuses them in the Initial of the next connect to the same endpoint - Stateless reset (RFC 9000 §10.3): listener emits resets for orphan packets; per-connection
NEW_CONNECTION_IDtokens share the listener's HMAC secret so they match orphan-path tokens - Server-side address validation (RFC 9000 §8.1): opt in with
address_validation => alwaysonquic:start_server/3. Listener emits a Retry packet with an HMAC-signed retry token when a client Initial arrives without one; subsequent Initials carrying a valid token skip retry and spawn a connection that echoesretry_source_connection_id. Server issues a NEW_TOKEN after handshake so the next reconnect skips retry entirely
- Bidirectional streams (client and server initiated)
- Unidirectional streams
- Stream prioritization (RFC 9218) with 8 urgency levels
- Incremental delivery flag support
- RESET_STREAM_AT extension (draft-ietf-quic-reliable-stream-reset-07)
- Connection-level flow control (MAX_DATA)
- Stream-level flow control (MAX_STREAM_DATA)
- MAX_STREAMS limits (bidirectional and unidirectional)
- Initial, Handshake, and 1-RTT packet types
- Short header (1-RTT) packets
- Packet number encoding (1-4 bytes)
- Packet number reconstruction per RFC 9000 Appendix A
- Coalesced packets
- Frame coalescing (ACK + small stream data in single packet)
- HTTP/3 response HEADERS coalesced with the first DATA frame so the response headers and first body bytes ride in one 1-RTT packet (a large body still fragments; only the standalone HEADERS packet is removed)
- PATH_CHALLENGE / PATH_RESPONSE validation
- Active connection migration (
quic:migrate/1,quic:migrate/2) - Preferred address handling (RFC 9000 Section 9.6)
- Server-side address change detection (NAT rebinding and active migration)
- Congestion control reset on path change (RFC 9002 Section 9.4)
- CID rotation on migration for path unlinkability (RFC 9000 Section 9.5)
-
disable_active_migrationtransport parameter support - Path validation timeout with retry (3 * PTO, up to 3 attempts)
- Multiple connection IDs
- NEW_CONNECTION_ID frames
- RETIRE_CONNECTION_ID frames
- Active connection ID limit
- Packet loss detection
- Probe timeout (PTO)
- RTT measurement (smoothed RTT, RTT variance)
- Pluggable congestion control behavior
- NewReno (default, RFC 9002)
- CUBIC (RFC 9438)
- BBR (Bottleneck Bandwidth and RTT)
- HyStart++ slow start (RFC 9406) for all algorithms
- Slow start with improved exit detection
- Congestion avoidance
- Recovery on packet loss
- Persistent congestion detection (resets cwnd after PTO * 3)
- ECN support (ECN-CE triggers congestion response)
- Packet pacing (RFC 9002 Section 7.7) to prevent bursts
- RTT-based flow control auto-tuning
- Binary search probing for optimal MTU
- Integration with peer's
max_udp_payload_sizetransport parameter - Black hole detection and recovery
- Automatic MTU reset on connection migration
- Periodic re-probing for MTU increases
- Congestion control integration (updates cwnd-related parameters)
- Full TLS 1.3 handshake
- ALPN negotiation
- Transport parameters exchange
- Certificate verification
- HelloRetryRequest (RFC 8446 §4.1.4)
- x25519 (default)
- secp256r1, secp384r1 (opt-in via
groups) - Multi-group
key_share+supported_groupsnegotiation - secp521r1, x448 (constants only; see Roadmap)
- ECDSA secp256r1-SHA256, secp384r1-SHA384
- RSA-PSS-RSAE SHA256/384/512
- Ed25519
- Per-handshake
signature_algorithmsnegotiation (signature_algs) - Ed448, ECDSA secp521r1-SHA512 (constants only; see Roadmap)
- AES-128-GCM cipher suite
- AES-256-GCM cipher suite
- ChaCha20-Poly1305 cipher suite
- Header protection
- Key derivation (HKDF)
- Initial secrets derivation
- Handshake secrets
- Application secrets
- Key updates (RFC 9001 Section 6)
- Session tickets (NewSessionTicket)
- PSK-based resumption
- 0-RTT early data
-
psk_dhe_kemode (forward-secret) -
psk_kemode (no DHE) - Server-side binder verification (constant-time)
- Identity lookup via callback and/or static map
- Cert + PSK coexistence on the same listener
- Client downgrade protection
See docs/PSK.md. Pending follow-ups tracked in the Roadmap below.
- Version 2 (0x6b3343cf) support
- Updated initial salt
- Updated retry integrity tag key
- Server ID encoding in Connection IDs for LB routing
- Config rotation bits for LB coordination
- Variable CID length support (1-20 bytes)
- Three encoding algorithms:
- Plaintext: Server ID visible in CID (no encryption)
- Stream Cipher: AES-128-CTR encryption
- Block Cipher: Feistel network for variable lengths
- LB-aware CID generation in listener and connection
RESET_STREAM_AT allows resetting a stream while ensuring data up to a specified offset is reliably delivered. Required for WebTransport where stream headers must be received even if the stream is immediately reset.
- Frame type 0x24 (RESET_STREAM_AT) encode/decode
- Transport parameter negotiation (0x17f7586d2cb571)
- Reliable delivery guarantee up to ReliableSize
- Retransmission filtering (data beyond ReliableSize not retransmitted)
- Validation: ReliableSize cannot exceed FinalSize
- Validation: ReliableSize cannot be increased after initial reset
- Validation: ErrorCode cannot change after initial reset
%% Enable in connection options (both client and server)
Opts = #{reset_stream_at => true, alpn => [<<"webtransport">>]},
{ok, Conn} = quic:connect(Host, Port, Opts, self()),
%% Send stream header (e.g., WebTransport session ID)
{ok, StreamId} = quic:open_stream(Conn),
ok = quic:send_data(Conn, StreamId, Header, false),
%% Reset stream but ensure header is delivered
ok = quic:reset_stream_at(Conn, StreamId, ErrorCode, byte_size(Header)).quic:connect/3,4- Connect to serverquic:close/1,2,3- Close connection (with optional app error code)quic:peername/1- Get peer addressquic:sockname/1- Get local addressquic:peercert/1- Get peer certificatequic:migrate/1,2- Trigger connection migration (with optional timeout)quic:has_early_keys/1- Whether 0-RTT (early data) keys are availablequic:early_data_accepted/1- Whether the server accepted 0-RTT early data
quic:send_datagram/2- Send unreliable datagramquic:datagram_max_size/1- Get max datagram size (0 if unsupported)quic:datagram_stats/1- Delivered / dropped / sent counters (backpressure)
quic_h3:send_datagram/3- Send an HTTP datagram bound to a request streamquic_h3:h3_datagrams_enabled/1- Whether both sides negotiated supportquic_h3:max_datagram_size/2- Max payload per datagram for a given stream- Owner event:
{quic_h3, Conn, {datagram, StreamId, Payload}} - Set
h3_datagram_enabled => trueonconnect/3/start_server/3to enable. CONNECT-UDP (RFC 9298) builds on this in a separate library.
quic_h3:open_bidi_stream/1,2- Open a client-initiated bidi stream; with a non-negativeSignalTypevarint the stream is pre-claimed and inbound bytes route as{stream_type_data, bidi, ...}owner messages instead of HTTP/3 request frames (e.g. WebTransport's0x41)stream_type_handleroption onstart_server/3claims peer-initiated uni / bidi streams whose first varint matches a caller-supplied filter- Owner events:
{stream_type_open, Direction, StreamId, VarintType},{stream_type_data, Direction, StreamId, Data, Fin},{stream_type_closed, Direction, StreamId},{stream_type_reset, Direction, StreamId, ErrorCode},{stream_type_stop_sending, Direction, StreamId, ErrorCode} - Per-connection owner override via
connection_handlercallback onstart_server/3for hosting many sessions on one listener
quic:open_stream/1- Open bidirectional streamquic:open_unidirectional_stream/1- Open unidirectional streamquic:send/3,4- Send data on streamquic:close_stream/2,3- Close streamquic:reset_stream/3- Reset stream with error codequic:reset_stream_at/4- Reset stream with reliable delivery up to specified sizequic:set_stream_priority/4- Set stream priority (urgency, incremental)quic:get_stream_priority/2- Get stream priority
quic:start_server/3- Start named server poolquic:stop_server/1- Stop named serverquic:get_server_info/1- Get server informationquic:get_server_port/1- Get server listening portquic:get_server_sockname/1- Get server bound address ({IP, Port})quic:get_server_connections/1- Get server connection PIDsquic:which_servers/0- List all running servers
quic_lb:new_config/1- Create LB configuration from options mapquic_lb:new_cid_config/1- Create CID generation configurationquic_lb:generate_cid/1- Generate CID with encoded server_idquic_lb:decode_server_id/2- Extract server_id from CIDquic_lb:is_lb_routable/1- Check if CID has valid LB routing bitsquic_lb:get_config_rotation/1- Get config rotation bits from CIDquic_lb:expected_cid_len/1- Calculate expected CID length from config
idle_timeout- Connection idle timeout in milliseconds (0 to disable)max_data- Connection-level flow control limitmax_stream_data- Stream-level flow control limitmax_datagram_frame_size- Max datagram size to accept (0 = disabled, default: 0)datagram_recv_queue_len- Bounded receive queue for inbound datagrams (default:infinity; drops oldest on overflow, tracked viadatagram_stats/1)reset_stream_at- Enable RESET_STREAM_AT extension (default: false)alpn- ALPN protocols listverify- Server certificate verification on the client (default:true; verifies the CertificateVerify signature, the chain, and the hostname). The hostname check follows the RFC 6125 HTTPS rules, so a leftmost-label wildcard SAN such as*.example.commatcheshost.example.com. Setfalseto accept any certificate, e.g. a self-signed test server.cacerts- Trust anchors for client chain validation, as a list of DER-encoded certificates (default: the operating system trust store)preferred_ipv4- Server preferred IPv4 addresspreferred_ipv6- Server preferred IPv6 addresspool_size- Number of listener processes for server pools (default: 1)connection_handler- Callback for handling new connectionslb_config- QUIC-LB configuration map for load balancer routingkeep_alive_interval- Keep-alive PING interval (disabled,auto, or milliseconds)pmtu_enabled- Enable Path MTU Discovery (default: true)pmtu_max_mtu- Maximum MTU to probe (default: 1500)max_udp_payload_size- Largest UDP payload this endpoint is willing to receive, advertised as the RFC 9000 §18.2 transport parameter (minimum 1200; default: what a 1500-byte path carries for the address family, 1472 over IPv4 and 1452 over IPv6)recbuf- UDP receive buffer size in bytes (default: 7MB)sndbuf- UDP send buffer size in bytes (default: 7MB)server_send_batching- Per-connection send batching on the server (default: true). On Linux +socket_backend => socketwith UDP_SEGMENT, outgoing packets are coalesced into GSO super-datagrams viasendmsgcmsg; neutral on macOS / gen_udp. Set tofalseto fall back to directgen_udp:send/4
quic:get_mtu/1- Get current effective MTU for a connection
QUIC-based Erlang distribution protocol implementation.
- Full distribution protocol over QUIC transport
- TLS 1.3 encryption built-in (no separate SSL setup)
- 0-RTT session resumption for fast reconnection
- Multiple streams: control (urgency 0) + data (urgency 4-6)
- Stream prioritization for tick/control messages
- QUIC-level liveness detection (packet counts, not blocked by flow control)
- Keep-alive PING frames for transport liveness
- Backpressure mechanism for congestion control
- Session ticket storage for 0-RTT
quic_dist- Distribution protocol callbacksquic_dist_controller- Per-connection state machinequic_dist_sup- Distribution supervisorquic_dist_tickets- Session ticket storagequic_epmd- EPMD replacement modulequic_dist_auth- Optional auth-handshake behaviour
quic_discovery_static- Static node configurationquic_discovery_dns- DNS SRV-based discovery- Custom backends via
quic_discoverybehaviour
quic:get_stats/1- Get packet counts for liveness detectionquic:send_ping/1- Send transport-level PING frame
auth_callback(defaultundefined):{Mod, Fun}orfun/3invoked on both sides after the QUIC handshake but before the dist_util handshake. Returning{error, _}closes the connection without ever starting the dist controller. Seequic_dist_authand the Configuration Reference indocs/QUIC_DIST.md.register_with_epmd(defaultfalse): whentrue, the listener registers its port via the configuredepmd_moduleso external tooling (e.g.epmd -names) can resolve the node.
Tracked future work. Items here are not committed deliverables; they mark known gaps that may land in a later release.
- 0-RTT on external PSK. The server currently ignores
early_dataon PSK handshakes; full EndOfEarlyData state-machine support is deferred. -
NewSessionTicketon PSK-authenticated handshakes. Suppressed in v1 to avoid binding-identity ambiguity; revisit when a concrete use case appears. - RFC 9258 PSK Importer. The current API consumes the secret as
raw IKM; an importer layer would derive an
epsk -> pskmapping bound to a target protocol/KDF.
- secp521r1 / x448 key-exchange groups (constants defined; key generation and key_share wiring deferred).
- Ed448 / ECDSA secp521r1-SHA512 signature schemes (constants defined; sign/verify branches deferred).
- PSK + HelloRetryRequest in one handshake. Currently the client aborts if HRR follows a PSK ClientHello (binder recompute over the synthetic transcript is not implemented).
All 10 QUIC Interop Runner test cases pass:
| Test Case | Status |
|---|---|
| handshake | Pass |
| transfer | Pass |
| retry | Pass |
| keyupdate | Pass |
| chacha20 | Pass |
| multiconnect | Pass |
| v2 | Pass |
| resumption | Pass |
| zerortt | Pass |
| connectionmigration | Pass |