Wire Compatibility
This page documents the exact wire formats needed for interoperability with existing Xray-core instances. Your implementation must produce and consume bytes in exactly these formats.
VLESS Wire Format
Request (Client → Server)
Offset Size Field
0 1 Version (0x00)
1 16 UUID (raw bytes, NOT hex string)
17 1 Addons length (N)
18 N Addons (protobuf-encoded Addons message, or empty if N=0)
18+N 1 Command: 0x01=TCP, 0x02=UDP, 0x03=Mux, 0x04=Reverse
19+N ... Address (omitted for Mux/Reverse):
Port: 2 bytes big-endian
AddrType: 0x01=IPv4, 0x02=Domain, 0x03=IPv6
Address:
IPv4: 4 bytes
Domain: 1 byte length + domain string
IPv6: 16 bytesAddons protobuf:
message Addons {
string Flow = 1; // "xtls-rprx-vision" or empty
bytes Seed = 2;
}When Flow is empty: addons length = 0 (single 0x00 byte). When Flow is set: protobuf-encode and write length + data.
Response (Server → Client)
Offset Size Field
0 1 Version (echoes request version, 0x00)
1 1 Addons length (N)
2 N Addons (protobuf-encoded, usually empty)VLESS Body
| Command | Body Format |
|---|---|
| TCP (no Vision) | Raw stream bytes |
| UDP | [2B length BE][payload] repeated |
| Mux | Mux frame format (below) |
| Mux port=666 | XUDP frame format (below) |
VMess Wire Format (AEAD)
Auth Info (16 bytes)
authID = HMAC-MD5(key=MD5(UUID + timestamp/30s), data=timestamp + random)Request Header
[16B AuthID]
[2B EncryptedLength] (AES-128-GCM, key from authID)
[NB EncryptedCommand] (AES-128-GCM, key from authID)Decrypted command:
1B Version (1)
16B Request Body IV
16B Request Body Key
1B Response Auth V
1B Option (0x01=ChunkStream, 0x02=ConnectionReuse, 0x04=ChunkMasking, 0x08=GlobalPadding, 0x10=AuthenticatedLength)
1B Padding Length + Security
1B Reserved
1B Command (0x01=TCP, 0x02=UDP, 0x03=Mux)
2B Port (BE)
1B Address Type
NB Address
NB Random Padding
4B FNV1a32 ChecksumResponse Header
1B Response Auth V (must match request)
1B Option
1B Command
1B Command LengthBody (AES-128-GCM)
[2B Length (encrypted with AES-GCM)]
[NB Payload (encrypted with AES-GCM)]
[2B Length]
[NB Payload]
...
[2B 0x0000] (end marker)Nonce: counter-based, incremented per chunk.
Trojan Wire Format
Request
[56B Hex(SHA224(password))] (lowercase hex string, NOT binary)
[2B CRLF (0x0D 0x0A)]
[1B Command: 0x01=TCP, 0x03=UDP]
[1B Address Type: 0x01=IPv4, 0x03=Domain, 0x04=IPv6]
[NB Address]
[2B Port (BE)]
[2B CRLF (0x0D 0x0A)]
[... Payload]UDP Framing (Command = 0x03)
[1B Address Type]
[NB Address]
[2B Port (BE)]
[2B Payload Length (BE)]
[2B CRLF]
[NB Payload]Repeated for each UDP packet.
Mux Frame Format
Frame Header
[2B Meta Length (BE)] — length of following metadata
[2B Session ID (BE)]
[1B Status: 0x01=New, 0x02=Keep, 0x03=End, 0x04=KeepAlive]
[1B Option: bit 0=Data, bit 1=Error]New Session (Status=0x01)
[... Frame Header]
[1B Network: 0x01=TCP, 0x02=UDP]
[2B Port (BE)]
[1B Address Type: 0x01=IPv4, 0x02=Domain, 0x03=IPv6]
[NB Address]Data Payload (when Option has Data bit)
After the frame header/metadata:
[2B Data Length (BE)]
[NB Data]Session End (Status=0x03)
Just the frame header, no additional fields.
XUDP Frame Format
Same structure as Mux, but:
- Session ID is always
0x0000 - First frame includes 8-byte GlobalID after the address
- Keep frames include per-packet destination address
XUDP New Session
[2B Meta Length]
[00 00] Session ID = 0
[01] Status = New
[01] Option = Data
[02] Network = UDP
[2B Port (BE)]
[1B Addr Type]
[NB Address]
[8B GlobalID] BLAKE3 hash of client source
[2B Data Length (BE)]
[NB UDP Payload]XUDP Keep (subsequent packets)
[2B Meta Length]
[00 00] Session ID = 0
[02] Status = Keep
[01] Option = Data
[02] Network = UDP (if per-packet dest present)
[2B Port (BE)] (destination of this packet)
[1B Addr Type]
[NB Address]
[2B Data Length (BE)]
[NB UDP Payload]Vision Padding Format
[16B UserUUID] (only in first frame, omitted after)
[1B Command] 0x00=Continue, 0x01=End, 0x02=Direct
[2B Content Length (BE)]
[2B Padding Length (BE)]
[NB Content]
[NB Padding (random bytes)]Multiple padding blocks can be concatenated in one write.
Address Encoding
All protocols use the same address encoding:
IPv4: Type=0x01, followed by 4 bytes
Domain: Type=0x02, followed by 1 byte length + domain string
IPv6: Type=0x03, followed by 16 bytesPort is always 2 bytes big-endian, written before the address (port-then-address order).
Key Compatibility Notes
UUID encoding: Raw 16 bytes, NOT the hyphenated string form.
Protobuf wire format: VLESS addons use standard protobuf encoding. The length prefix is a single byte (not varint), limiting addons to 255 bytes.
XUDP session ID 0: This is how the server distinguishes XUDP from regular mux. Regular mux session IDs start from 1.
XUDP magic port 666: When the VLESS outbound converts UDP to XUDP, it uses address
v1.mux.coolwith port666. This is a convention, not a wire format detail (the address is sent as part of the VLESS header).Vision UserUUID once: The 16-byte UUID appears only in the first padding frame. If the first frame doesn't start with the expected UUID, the server falls back to non-Vision mode.
Trojan password is hex: SHA224(password) is encoded as 56 lowercase hex characters (ASCII), not binary.
VMess time window: The timestamp in the auth ID is
time.Now().Unix() / 30. The server accepts ±120 seconds (8 time periods).Endianness: All multi-byte integers are big-endian unless otherwise noted.
VLESS fallback detection: If the first 17 bytes (version + UUID) don't match any user, and fallback is enabled, the entire buffered data is forwarded to the fallback destination.
Cone mode flag: The
conecontext value determines whether UDP uses XUDP (cone=true) or individual mux sessions (cone=false). This affects the wire format used.