# Reliable Large Datagram Protocol (RLDP) (https://docs.ton.org/llms/foundations/network/rldp/content.md)



<Callout type="note">
  With the introduction of the [sub-second upgrade](https://docs.ton.org/llms/ecosystem/subsecond/content.md), blockchain nodes now use [QUIC](https://en.wikipedia.org/wiki/QUIC) instead of RLDP for internal node communication.
</Callout>

The Reliable Large Datagram Protocol (RLDP) runs on top of [ADNL UDP](https://docs.ton.org/llms/foundations/network/adnl-udp/content.md) and transfers data that does not fit in one ADNL datagram. It uses [Forward Error Correction (FEC)](https://en.wikipedia.org/wiki/Error_correction_code) to recover from packet loss. Its newer version, RLDP2, also uses acknowledgment packets, loss detection, pacing, and congestion control.

RLDP has been used throughout TON infrastructure for downloading blocks from other nodes, transferring data, and accessing [TON Sites](https://docs.ton.org/llms/foundations/web3/ton-sites/content.md) and [TON Storage](https://docs.ton.org/llms/foundations/web3/ton-storage/content.md).

<Callout type="note">
  The [`rldp-http-proxy` in the TON monorepo](https://github.com/ton-blockchain/ton/tree/v2026.06/rldp-http-proxy) uses RLDP2 for accessing TON Sites.
</Callout>

## Protocol [#protocol]

The [TL](https://core.telegram.org/mtproto/TL) schema retains structures for RLDP and RLDP2:

```tl
fec.raptorQ data_size:int symbol_size:int symbols_count:int = fec.Type;
fec.roundRobin data_size:int symbol_size:int symbols_count:int = fec.Type;
fec.online data_size:int symbol_size:int symbols_count:int = fec.Type;

rldp2.messagePart transfer_id:int256 fec_type:fec.Type part:int total_size:long seqno:int data:bytes = rldp2.MessagePart;
rldp2.confirm transfer_id:int256 part:int max_seqno:int received_mask:int received_count:int = rldp2.MessagePart;
rldp2.complete transfer_id:int256 part:int = rldp2.MessagePart;

rldp.messagePart transfer_id:int256 fec_type:fec.Type part:int total_size:long seqno:int data:bytes = rldp.MessagePart;
rldp.confirm transfer_id:int256 part:int seqno:int = rldp.MessagePart;
rldp.complete transfer_id:int256 part:int = rldp.MessagePart;

rldp.message id:int256 data:bytes = rldp.Message;
rldp.query query_id:int256 max_answer_size:long timeout:int data:bytes = rldp.Message;
rldp.answer query_id:int256 data:bytes = rldp.Message;
```

The message-part, confirmation, and completion structures are serialized as custom ADNL messages and transmitted over [ADNL UDP](https://docs.ton.org/llms/foundations/network/adnl-udp/content.md). RLDP2 reuses `rldp.message`, `rldp.query`, and `rldp.answer` for the decoded payload.

* `rldp.message` carries a one-way application payload.
* `rldp.query` carries a request, a `query_id`, a maximum answer size, and a UNIX timeout.
* `rldp.answer` carries the application response and the request's `query_id`. The querying endpoint rejects response data larger than `max_answer_size`.

The RLDP2 connection defaults to a 7,680-byte inbound limit. The `send_query_ex` registers `max_answer_size` as the limit for the answer transfer.

## Transfer mechanism [#transfer-mechanism]

1. The sender generates a random `transfer_id`.
2. The sender splits the payload into parts and creates a [RaptorQ](https://en.wikipedia.org/wiki/Raptor_code#RaptorQ_code) encoder for each part. The reference RLDP2 implementation uses parts of up to 2,000,000 bytes and 768-byte symbols.
3. The sender transmits encoded symbols in `rldp2.messagePart`.
4. The receiver reports the greatest received sequence number, a receive mask, and a total receive count in `rldp2.confirm`.
5. The sender uses this feedback to estimate round-trip time, bandwidth, and loss. It paces symbols and adjusts its congestion window with a [BBR](https://en.wikipedia.org/wiki/TCP_congestion_control#TCP_BBR)-style controller.
6. The sender stops transmitting a part after `rldp2.complete` arrives or the transfer times out.

The receiver decodes each part after collecting enough independent symbols, concatenates the parts in order, and deserializes the result as `rldp.message`, `rldp.query`, or `rldp.answer`. An answer uses the request's `transfer_id` with every bit inverted.

## Forward Error Correction [#forward-error-correction]

The TL schema defines RoundRobin, Online, and RaptorQ FEC types. The reference RLDP2 implementation accepts only RaptorQ — its FEC parser rejects the other constructors.

### RaptorQ [#raptorq]

RaptorQ divides data into fixed-size source symbols. An encoder can derive additional repair symbols from the same source data.

After receiving enough independent source and repair symbols, the decoder reconstructs the original part without requesting specific lost symbols.

RLDP2 sends an initial set of source and repair symbols, then uses acknowledgments and loss estimates to decide how many additional symbols to send. The receiver sends `rldp2.complete` after decoding a part.

<Callout type="note">
  [RaptorQ encoder setup in the reference RLDP2 implementation](https://github.com/ton-blockchain/ton/blob/v2026.06/rldp2/OutboundTransfer.cpp#L27-L40).
</Callout>

## RLDP-HTTP [#rldp-http]

[TON Sites](https://docs.ton.org/llms/foundations/web3/ton-sites/content.md) use HTTP over RLDP. The host runs a standard HTTP web server with `rldp-http-proxy` alongside it. Incoming requests from the TON network arrive through RLDP2, and the proxy forwards them to the local HTTP server. On the client side, a local proxy, such as [Tonutils Proxy](https://github.com/xssnick/Tonutils-Proxy), converts outgoing HTTP requests into RLDP.

HTTP over RLDP uses these TL structures:

```tl
http.header name:string value:string = http.Header;
http.payloadPart data:bytes trailer:(vector http.header) last:Bool = http.PayloadPart;
http.response http_version:string status_code:int reason:string headers:(vector http.header) no_payload:Bool = http.Response;
http.proxy.capabilities capabilities:long = http.proxy.Capabilities;

http.request id:int256 method:string url:string http_version:string headers:(vector http.header) = http.Response;
http.getNextPayloadPart id:int256 seqno:int max_chunk_size:int = http.PayloadPart;
http.proxy.getCapabilities capabilities:long = http.proxy.Capabilities;
```

The request-response flow:

1. The client sends `http.request` via `rldp.query`.
2. If the request has a payload, the server sends `http.getNextPayloadPart` queries to the client.
   * The client responds with `http.payloadPart` chunks and increments `seqno` until `last` is true.
3. The server sends `http.response`.
4. If `no_payload` is false, the client sends `http.getNextPayloadPart` queries to the server using the same chunked mechanism.

The proxy uses bit 0 (value 1) in the `http.proxy.getCapabilities` and `http.proxy.capabilities` exchange to advertise RLDP2 support. It requests payload chunks of up to 2,095,104 bytes and caps larger `max_chunk_size` values at that size.

## TON Site request example [#ton-site-request-example]

This example demonstrates requesting `foundation.ton`, assuming the ADNL address has been resolved via DNS and a connection has been established over [ADNL UDP](https://docs.ton.org/llms/foundations/network/adnl-udp/content.md). The proxy requires the peer to advertise RLDP2 support.

### GET request [#get-request]

The client fills the following structure:

```tl
http.request id:int256 method:string url:string http_version:string headers:(vector http.header) = http.Response;
```

The client serializes `http.request`:

```text
e191b161                                                           -- TL ID http.request
116505dac8a9a3cdb464f9b5dd9af78594f23f1c295099a9b50c8245de471194   -- id (random)
03 474554                                                          -- method = "GET"
16 687474703a2f2f666f756e646174696f6e2e746f6e2f 00                 -- url = "http://foundation.ton/"
08 485454502f312e31 000000                                         -- http_version = "HTTP/1.1"
01000000                                                           -- headers (1 entry)
   04 486f7374 000000                                              -- name = "Host"
   0e 666f756e646174696f6e2e746f6e 00                              -- value = "foundation.ton"
```

The client then wraps the serialized `http.request` in `rldp.query` and serializes it. The following partial snippet shows the field layout. The placeholders represent values that the client supplies before serialization:

```text
694d798a                    -- TL ID rldp.query
<QUERY_ID>                  -- 32 random bytes
0040000000000000            -- max_answer_size = 16 KiB (16,384 bytes)
<TIMEOUT>                   -- future UNIX timestamp encoded as a TL int
<SERIALIZED_HTTP_REQUEST>   -- TL bytes value
```

### RaptorQ encoding [#raptorq-encoding]

1. The sender uses the 768-byte symbol size used by the reference RLDP2 implementation.
2. For this one-part request, the sender sets `data_size` and `total_size` to the serialized `rldp.query` length. The RaptorQ encoder handles the final short symbol.
3. The sender creates a [RaptorQ encoder](https://github.com/ton-blockchain/ton/blob/v2026.06/rldp2/OutboundTransfer.cpp#L27-L40).

### Packet transmission [#packet-transmission]

The sender transmits encoded symbols in `rldp2.messagePart`:

```tl
rldp2.messagePart transfer_id:int256 fec_type:fec.Type part:int total_size:long seqno:int data:bytes = rldp2.MessagePart;
```

* `transfer_id` – random `int256`, same for all parts of this transfer.
* `fec_type` – `fec.raptorQ` with the part's byte length, `symbol_size = 768`, and the encoder's source-symbol count.
* `part` – zero-based part index. Start with 0.
* `total_size` – complete serialized `rldp.query` size across all parts.
* `seqno` – starts at 0, incremented for each packet.
* `data` – encoded symbol (768 bytes).

The sender wraps each structure in `adnl.message.custom` and transmits it over ADNL UDP. It processes `rldp2.confirm` feedback and continues according to its loss-recovery and pacing logic until `rldp2.complete` arrives.

### Response processing [#response-processing]

The response arrives as `rldp2.messagePart` messages inside `adnl.message.custom`. Its `transfer_id` is the request transfer ID with all 256 bits inverted.

1. The receiver extracts FEC parameters (`data_size`, `symbol_size`, `symbols_count`) from the first received message.
2. The receiver initializes a RaptorQ decoder.
3. The receiver feeds each received symbol and its `seqno` into the decoder.
4. The receiver sends `rldp2.confirm` as symbols arrive.
5. The receiver attempts decoding when the decoder reports that enough symbols are available. On success, it sends `rldp2.complete` for that part.

At this point, the decoded `rldp.answer` contains an `http.response` with the status code and headers, but not necessarily the body.

* If `no_payload` is `true`, the response has no body and the request is complete.
* If `no_payload` is `false`, the client fetches the body separately:

  1. The client sends an `http.getNextPayloadPart` request with incrementing `seqno`. The proxy requests at most 2,095,104 bytes per chunk.
  2. The client wraps each request in a new `rldp.query` transfer.
  3. The client repeats the request until the returned `http.payloadPart` has `last` set to `true`.

## See also [#see-also]

* [ADNL overview](https://docs.ton.org/llms/foundations/network/adnl/content.md)
* [TON Proxy](https://docs.ton.org/llms/foundations/web3/ton-proxy/content.md)
* [TON Sites](https://docs.ton.org/llms/foundations/web3/ton-sites/content.md)
* [RLDP implementation in the TON monorepo](https://github.com/ton-blockchain/ton/tree/v2026.06/rldp)
* [RLDP v2 implementation in the TON monorepo](https://github.com/ton-blockchain/ton/tree/v2026.06/rldp2)
