| Internet-Draft | PRF Protocol | May 2026 |
| QwQ | Expires 11 November 2026 | [Page] |
This document describes the PRF protocol ("Prf is Reversed Frp"), a lightweight, binary-length-prefixed TCP relay messaging protocol. PRF enables peer-to-peer tunneling of TCP connections through a public middle relay node, originally designed for Minecraft multiplayer sessions behind NAT or firewall environments. The protocol defines three roles - Server, Client, and Middle - and a set of four wireframe message types for room registration, connection requests, worker handoff, and direct Minecraft client handshake parsing.¶
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 11 November 2026.¶
Copyright (c) 2026 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.¶
PRF is a custom application-layer protocol that operates over raw TCP sockets. It is designed to facilitate TCP port forwarding between peers that cannot directly connect to each other due to network constraints, such as Network Address Translation (NAT) or firewall restrictions. A publicly accessible middle relay node brokers connections between a Server (which hosts a service) and a Client (which wishes to access that service).¶
The protocol does not provide encryption, authentication, or integrity protection. It is intended for use in trusted or low-risk environments where the primary concern is connectivity rather than confidentiality.¶
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 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
The PRF protocol defines three distinct roles:¶
PRF is a request-response protocol in which all communication flows through the Middle node. The Server registers with the Middle and then polls for pending Client connections. The Client sends a connection request to the Middle. When a Client is matched with a Server, the Middle assigns a Client Worker Identifier (CW-ID) and notifies the Server. The Server then initiates a worker connection back to the Middle to claim the Client, after which a bidirectional TCP tunnel is established.¶
The protocol also defines a null-type mode in which no type identifier is sent. In this mode, the Middle node inspects the raw initial bytes of the TCP stream to determine routing. One recognized payload format in this mode is the Minecraft client handshake packet, making the protocol compatible with native Minecraft clients without requiring the PRF Client tool.¶
All multi-byte numeric fields are transmitted in network byte order (big-endian).¶
A 32-bit signed integer encoded in 4 bytes, big-endian. The maximum value is 2,147,483,647.¶
A string is encoded as a length-prefixed sequence of UTF-8 bytes:¶
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Length (int32) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Data (UTF-8 bytes) ... | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+¶
The Length field specifies the number of UTF-8 bytes that follow.¶
A byte array is encoded identically to a String, with a 4-byte length prefix followed by the raw bytes. Unlike String, the payload of a byte array is not assumed to be valid UTF-8.¶
Every message sent to the Middle node begins with a type identifier field, which is a length-prefixed string containing one of the following four values. The type identifier determines how the remainder of the message is parsed.¶
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type Length (= 1) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type ("S") | Room ID Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | Room ID (UTF-8 bytes) ... |
~ ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
¶
The Type field is the UTF-8 string "S". The Room ID is a UTF-8
string of at most 30 bytes identifying the room.¶
Upon receiving a Type "S" message, the Middle node sends a response string.¶
Success vs. Failure: The first character of the response string
determines the outcome. A response beginning with > (U+003E) indicates
success; any other response indicates failure. The remainder of the
string is a server-defined, internationalized (i18n) message that MAY
be displayed to the user but MUST NOT be used for protocol-level
decision making.¶
If the Room ID ends with the character v (U+0076, case-insensitive)
and the room ID is already registered, the Middle node evicts the
existing Server registration and replaces it with the new connection.
The evicted Server receives the string "EXIT" (without a leading
type identifier) on its poll channel, indicating that it should
terminate.¶
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type Length (= 1) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type ("C") | Room ID (String, max 30) |
~ ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
¶
The Type field is the UTF-8 string "C". The Room ID is the
identifier of the target room, at most 30 bytes.¶
Upon receiving a Type "C" message, the Middle node sends a response string.¶
Success vs. Failure: The first character of the response string
determines the outcome. A response beginning with > (U+003E) indicates
success; any other response indicates failure. The remainder of the
string is a server-defined, internationalized (i18n) message that MAY
be displayed to the user but MUST NOT be used for protocol-level
decision making.¶
If the response indicates success, the Client SHOULD proceed to establish a bidirectional tunnel.¶
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type Length (= 1) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type ("W") | CW-ID (String, max 30) |
~ ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
¶
The Type field is the UTF-8 string "W". The CW-ID field is the
Client Worker Identifier previously received from the Middle during
polling.¶
Upon receiving a Type "W" message, the Middle node sends a response string.¶
Success vs. Failure: The first character of the response string
determines the outcome. A response beginning with > (U+003E) indicates
success; any other response indicates failure. The remainder of the
string is a server-defined, internationalized (i18n) message that MAY
be displayed to the user but MUST NOT be used for protocol-level
decision making.¶
On success, a bidirectional tunnel is established between this streamer's socket and the corresponding Client's socket. The response message MAY contain the IP address of the connected Client as informational text.¶
If the room identifier starts with the character V (U+0056,
case-insensitive), the Middle node prepends a HAProxy PROXY protocol
version 1 header [PROXY] to the Client-to-Server direction of the
tunnel before bridging. The header has the following format:¶
PROXY TCP4 <client-ip> 192.0.2.1 10000 10000\r\n¶
Where <client-ip> is the actual IP address of the Client. The
destination address and port are placeholders (192.0.2.1:10000). This
mechanism allows the target service to obtain the real Client IP
address.¶
When a connection arrives at the Middle node without a leading type identifier string, the Middle enters an untyped raw stream inspection mode. Rather than rejecting the connection, the Middle reads the initial bytes from the TCP stream and attempts to identify a recognized payload format. If a format is recognized, the Middle extracts routing information (the room identifier) and bridges the connection; if not, the connection is terminated with an error.¶
This is not a general-purpose passthrough - it is a protocol-defined extension point that allows PRF to interoperate with existing application protocols without requiring a PRF-specific client. The specific format described below is the Minecraft client handshake, which PRF happens to be compatible with by design.¶
The Minecraft client handshake packet is one payload format recognized in the null-type mode. When the Middle identifies this format, it extracts a room identifier from the Server Name Indication (SNI) hostname carried in the packet.¶
The packet format is defined by the Minecraft protocol. Within PRF, the relevant fields are:¶
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Total Packet Length (VarInt) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Packet ID = 0x00 (Byte) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Protocol Version (VarInt) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Server Name Length (VarInt) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Server Name (UTF-8 bytes) ... | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Port (Unsigned Short) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Next State (VarInt) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+¶
The Total Packet Length MUST be at least 10 bytes and at most 100 bytes. The Packet ID MUST be 0x00 (Handshake). The Server Name Length MUST be at least 5.¶
The Middle node extracts the subdomain from the Server Name (SNI)
hostname by taking all characters before the first . (U+002E)
character. For example, if the SNI is myroom.relay.example.com,
the extracted room ID is myroom.¶
The full handshake packet is buffered and forwarded unmodified to the target Server after the tunnel is established.¶
If the room ID cannot be found, the Middle node sends a Minecraft Disconnect/Login packet (Packet ID 0x00) to the Client with a JSON reason string in the format:¶
{"text":"<reason>"}
¶
This terminates the Client connection with an appropriate error message displayed in the Minecraft client.¶
The following constants are defined by the PRF protocol:¶
| Constant | Value | Description |
|---|---|---|
| Maximum Room ID Length | 30 bytes | Maximum UTF-8 byte length of a room/server identifier |
| Maximum Response Length | 100 bytes | Maximum UTF-8 byte length of a Middle response string |
| Minimum MC Packet Length | 10 bytes | Minimum Minecraft handshake packet total length |
| Maximum MC Packet Length | 100 bytes | Maximum Minecraft handshake packet total length |
| Minimum Server Name Length | 5 bytes | Minimum SNI hostname length |
| Maximum VarInt Bytes | 5 | Maximum bytes for a single VarInt encoding |
| Poll Interval | 5 seconds | Interval at which the Server polls for new CW-IDs |
| Initial Read Timeout | 5 seconds | Middle socket read timeout for type detection |
| Reconnect Delay | 10 seconds | Delay before a Server reconnects after disconnect |
| Streamer Bridge Delay | 10 seconds | Delay before a MiddleStreamer begins bridging data |
| CW-ID Prefix | "CW" | Prefix for Client Worker Identifiers |
| PROXY destination address | 192.0.2.1 | Placeholder destination IP in PROXY header |
| PROXY destination port | 10000 | Placeholder destination port in PROXY header |
The following diagram illustrates the normal protocol flow for a Server registering and a Client connecting:¶
Server Middle Client | | | |--- "S" + roomId ------------>| | |<-- ">success" ---------------| | | | | | |<-- "C" + roomId -------------| | |--- ">success" -------------->| | | | |<-- "CW0" (poll) -------------| | |--- "W" + "CW0" ------------->| | |<-- ">success (<ip>)" ---------| | | | | |<================== Bidirectional Tunnel ===================>| | (raw TCP relay) | (raw TCP relay) |¶
Native Client Middle Server | | | |--- MC Handshake ------------>| | | (SNI: myroom.relay.example.com) | | | |--- extracts roomId="myroom" | | |--- assigns CW-ID | | | | | |--- "CW0" (poll) ------------>| | |<-- "W" + "CW0" --------------| | | | |<================== Bidirectional Tunnel ===================>| | (handshake forwarded) | (handshake forwarded) |¶
The PRF protocol as defined in this document does not provide any form of encryption, authentication, or integrity protection. All data, including room identifiers, is transmitted in cleartext over TCP. The following security implications should be considered:¶
PRF is designed to provide no additional security guarantees beyond those offered by the underlying transport. The protocol itself does not perform encryption, authentication, content inspection, or access control. Consequently, the overall security of a PRF-tunneled connection depends entirely on the security properties of the application-layer protocol carried within the tunnel. Implementations and deployments SHOULD ensure that the tunneled protocol provides its own encryption and authentication if confidentiality and integrity are required.¶
This document has no IANA actions.¶
--- back¶
The PRF protocol name is a playful backronym: "Prf is Reversed Frp," referencing the frp (Fast Reverse Proxy) tool that inspired its design.¶