Internet-Draft | QUIC-LY | August 2025 |
Briesemeister | Expires 22 February 2026 | [Page] |
QUIC-LY is an experimental variant of QUIC intended for controlled environments where both endpoints are managed by a single operator and no confidentiality is required. QUIC-LY removes TLS-based packet protection and replaces the QUIC/TLS handshake with a concise, 1-RTT transport setup using a small CONFIG/CONFIG-ACK exchange. QUIC-LY preserves QUIC’s transport benefits (multiplexed streams, flow control, loss recovery, connection migration) while operating entirely in cleartext. QUIC-LY is not intended for the public Internet.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 22 February 2026.¶
Copyright (c) 2025 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
QUIC [RFC9000] couples a transport protocol with TLS 1.3 [RFC9001] for key establishment and packet protection. QUIC-LY is a deliberately simplified variant intended for closed, trusted deployments where encryption is unnecessary and endpoints are under common control. QUIC-LY removes TLS, disables packet and header protection, and introduces a compact transport parameter exchange (CONFIG/CONFIG-ACK) to reach steady state in one round trip.¶
QUIC-LY retains QUIC’s invariants [RFC8999], stream and flow control mechanisms, loss recovery [RFC9002], and connection migration capability, unless otherwise stated. QUIC-LY is not suitable for deployment on the open Internet.¶
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in BCP 14 when, and only when, they appear in all capitals, as shown here.¶
QUIC-LY establishes a connection in one round trip, without TLS:¶
Client sends an Initial packet using QUIC-LY’s version with a CONFIG frame (optional) and padding to reach a configured size (Section Section 6).¶
Server responds with ACK (for the Initial) and CONFIG-ACK (if needed). Both endpoints then consider the connection established and proceed with Short Header packets.¶
The CONFIG/CONFIG-ACK exchange carries a compact set of transport parameters using TLVs. Operators MAY omit CONFIG entirely and rely on prearranged defaults.¶
QUIC-LY uses QUIC version 0x51554c59
(ASCII “QULY”). Endpoints that do not support this version MUST silently discard such packets. Version negotiation is not defined. QUIC-LY is intended only for environments where both endpoints are controlled and configured to support this version.¶
QUIC-LY does not use TLS and does not provide header protection or payload encryption. Packets are sent in cleartext. The QUIC-LY wire image adheres to QUIC invariants [RFC8999]. Implementations SHOULD use the same packet number spaces and encoding rules as [RFC9000], but packet contents are not cryptographically protected.¶
Operators MUST assume that on-path observers can read, modify, and inject QUIC-LY packets. QUIC-LY is therefore RECOMMENDED only in closed, trusted networks.¶
QUIC-LY uses the QUIC Long and Short Header formats from [RFC9000]. Fields are interpreted as in QUIC, with the following differences:¶
No Initial/Handshake/1-RTT keys are derived or used.¶
CRYPTO and HANDSHAKE_DONE frames are not used.¶
Endpoints MAY send Short Header packets immediately after the server’s first flight.¶
By default, a client Initial pads the UDP payload to at least 1200 bytes. QUIC-LY makes this configurable via the initial_padding_target
transport parameter (Section Section 7.4). Endpoints SHOULD pad the Initial to the agreed target. Any non-negative value is valid; 0 means no padding. Operators are advised to review amplification guidance in Section Section 9 before reducing the padding target.¶
Allowed: STREAM, RESET_STREAM, STOP_SENDING, MAX_DATA, MAX_STREAMS, MAX_STREAM_DATA, DATA_BLOCKED, STREAM_DATA_BLOCKED, STREAMS_BLOCKED, PADDING, ACK/ACK_ECN, CONNECTION_CLOSE, NEW_CONNECTION_ID, RETIRE_CONNECTION_ID, PATH_CHALLENGE, PATH_RESPONSE, DATAGRAM (if supported) [RFC9221].¶
Disallowed: CRYPTO, HANDSHAKE_DONE, NEW_TOKEN, early-data related frames.¶
Frame Type: 0x3a
(suggested; see IANA Considerations).¶
The CONFIG frame conveys proposed transport parameters from client to server. Its format is:¶
CONFIG = FRAME_TYPE (varint = 0x3a), LENGTH (varint), TLV-List (LENGTH bytes)¶
Each TLV is encoded as:¶
TLV = PARAM_ID (varint), VALUE_LEN (varint), VALUE (VALUE_LEN bytes)¶
Integers inside VALUE are QUIC varints unless otherwise stated. A parameter that is a boolean flag is encoded with VALUE_LEN = 0
to mean true; absence means false.¶
Frame Type: 0x3b
(suggested; see IANA Considerations).¶
The server replies with CONFIG-ACK to indicate the effective values that apply to the connection (accepted or clamped). Format is identical to CONFIG.¶
QUIC-LY defines the following minimal set. Unknown PARAM_ID
s MUST be ignored (skipped) by the receiver.¶
ID | Name | Type | Units |
---|---|---|---|
0x01 | initial_max_data | varint | bytes |
0x02 | initial_max_stream_data_bidi | varint | bytes |
0x03 | initial_max_streams_bidi | varint | count |
0x04 | max_udp_payload_size | varint | bytes |
0x05 | idle_timeout_ms | varint | ms |
0x06 | ack_delay_exponent | varint | — |
0x07 | max_ack_delay_ms | varint | ms |
0x08 | disable_active_migration | flag | — |
0x09 | initial_padding_target | varint | bytes |
RECOMMENDED defaults (if CONFIG/CONFIG-ACK are omitted):¶
initial_max_data = 1,048,576; initial_max_stream_data_bidi = 262,144; initial_max_streams_bidi = 8; max_udp_payload_size = 1350; idle_timeout_ms = 30000; ack_delay_exponent = 3; max_ack_delay_ms = 25; initial_padding_target = 1200; disable_active_migration = absent (false).¶
Client → Server (Initial):¶
Long Header, Version = 0x51554c59
.¶
Frames: CONFIG (optional), application STREAM data (optional), padding to reach the configured target.¶
Server → Client (First Response):¶
ACK for the client Initial.¶
CONFIG-ACK with effective parameters (optional if defaults used).¶
NEW_CONNECTION_ID (optional), application STREAM data (optional).¶
After the server’s first response, both endpoints consider the connection established and SHOULD use Short Header packets.¶
QUIC-LY deployments in controlled environments MAY disable Retry and tokens. Servers SHOULD apply an anti-amplification limit of at most three times (3x) the number of bytes received from the client address until that address is considered validated. When the negotiated or default initial_padding_target
is less than 1200 bytes (including 0, meaning no padding), this limit can substantially reduce the size of the server's first flight. Operators MAY configure policy to permit a small first response even with small client initials; a simple rule is to cap the first flight to max(3× bytes received, initial_padding_target)
. In tightly controlled labs, operators MAY disable amplification limits entirely.¶
QUIC-LY uses NEW_CONNECTION_ID and RETIRE_CONNECTION_ID as in [RFC9000]. PATH_CHALLENGE/PATH_RESPONSE MAY be used to validate a new path before migrating.¶
QUIC-LY uses connection- and stream-level flow control as in [RFC9000] with limits established by CONFIG/CONFIG-ACK or defaults. Limits MAY be raised during the connection using MAX_DATA and MAX_STREAM_DATA.¶
QUIC-LY adopts the algorithms and timers of [RFC9002]. ACK generation, ACK delay, probe timeouts, and packet number spaces follow QUIC norms, minus cryptographic key phases.¶
QUIC-LY reuses QUIC Transport error codes where applicable. The following additional error codes are defined for CONNECTION_CLOSE (and potentially application close), encoded as QUIC varints:¶
QUICLY_MALFORMED_CONFIG (0xface01)
: The CONFIG or CONFIG-ACK frame is not well-formed or violates encoding rules.¶
QUICLY_UNSUPPORTED_PARAM (0xface02)
: A required parameter is unsupported by the peer.¶
Endpoints MUST ignore unknown CONFIG parameters and only fail the connection when a required parameter is missing or invalid by local policy.¶
QUIC-LY provides no confidentiality or integrity. On-path adversaries can read, modify, inject, or drop packets. Operators MUST deploy QUIC-LY only in closed, trusted networks or testbeds where these risks are acceptable.¶
Optional lightweight integrity: An operator MAY define an application-level integrity check (e.g., a per-connection MAC carried in the first application message and echoed) to reduce trivial teardown or frame-injection attacks. Such mechanisms are out of scope here.¶
This document makes no requests of IANA. The frame type values 0x3a
and 0x3b
are suggested for private experiments and MUST NOT be used on the open Internet. The version number 0x51554c59
is for private use only.¶
QUIC-LY packets are plaintext. Standard operational tools (e.g., flow logs, packet captures) can observe transport fields directly. The QUIC spin bit behavior, if implemented, follows [RFC9000].¶
The following illustrates TLV serialization. QUIC varint encoding is as defined in [RFC9000]. Examples are illustrative, not normative.¶
encode_config(map): body = [] for (id, val) in map: emit varint(id) if val is flag_true: emit varint(0) else: tmp = encode_as_varint_bytes(val) emit varint(len(tmp)) emit tmp return varint(0x3a) || varint(len(body)) || body parse_config(bytes): ftype = read_varint() assert ftype in {0x3a, 0x3b} L = read_varint() end = pos + L out = {} while pos < end: pid = read_varint() vlen = read_varint() val = read(vlen) out[pid] = (flag_true if vlen == 0 else val) return out¶
Client states: START → WAIT_FIRST → ESTABLISHED → CLOSING → DRAINING
¶
START: Send Initial (ver=0x51554c59
) with optional CONFIG and padding to the configured target; enter WAIT_FIRST
.¶
WAIT_FIRST: On receiving server ACK of the Initial (and optional CONFIG-ACK), apply parameters and enter ESTABLISHED
. Ignore any Version Negotiation. On CONNECTION_CLOSE, enter DRAINING
.¶
ESTABLISHED: Use Short Header packets with STREAM/ACK/flow control.¶
Server states: LISTEN → ESTABLISHED → CLOSING → DRAINING
¶
LISTEN: On client Initial (ver=0x51554c59
), parse CONFIG (if any), respond with ACK and CONFIG-ACK (if needed); enter ESTABLISHED
.¶
ESTABLISHED: Normal operation.¶