Library / SDK
mas-bandwidth/reliable avatar
mas-bandwidth/reliable

reliable adds acknowledgements, fragmentation and RTT stats to your UDP socket

Packet acknowledgement system for UDP

657 stars89 forksCBSD-3-Clause

At a glance

What is it?
reliable is a BSD licensed C library that sits between your UDP socket and your protocol, adding packet acknowledgements, fragmentation and reassembly, duplicate detection and RTT, jitter and loss estimates. It is deliberately not a messaging layer, and its caveats section is unusually specific about ack buffer overflow, thread safety, pointer lifetimes and a transmit callback that must not re-enter its own endpoint.
Who is it for?
Engineers writing a real time protocol on UDP who need to know which packets arrived, want to fragment large messages and want latency and loss numbers to drive their own adaptation will get exactly that from this library, and the API stays small enough to read in an afternoon. It is the wrong tool if you want a transport with its own threads, its own sockets and its own reliability policy, since it deliberately owns none of those.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

An ack layer that leaves the socket to you

reliable is described as a simple packet acknowledgement system for UDP-based protocols, and the use case is stated in one sentence: it is useful when you need to know which UDP packets you sent were received by the other side. Everything else follows from that framing. The library does not open sockets, does not spawn threads and does not define a message format. It is designed to operate with your own network socket library, and for the case where you do not have one, the author points at netcode, which is designed to work well with reliable. The relationship to a socket is a pair of callbacks you supply, one to transmit a packet and one to process a received packet, and the library calls into them. The result is a layer you can drop into an existing engine, which is why the repository also ships other libraries by the same author, netcode, serialize and yojimbo, and it is written in C with a `reliable.h` header, a `reliable.c` implementation and a CMake build.

The config struct fixes the fragment limits up front

You start by filling in a configuration struct, and the fields are where the design decisions live. The example sets a maximum packet size of 32 KiB, a fragment threshold of 1200, a maximum of 32 fragments, a fragment size of 1024, and the two callback functions:

c
struct reliable_config_t config;

reliable_default_config( &config );

config.max_packet_size = 32 * 1024;
config.fragment_above = 1200;
config.max_fragments = 32;
config.fragment_size = 1024;
config.transmit_packet_function = transmit_packet;
config.process_packet_function = process_packet;

The `fragment_above` value of 1200 is the practical choice for UDP, since it keeps individual datagrams under the size that path MTU fragmentation tends to cause, and `max_fragments` caps how many pieces one logical packet may be split into, which is the setting that determines whether a large message is accepted at all. Endpoint creation is the point where those limits are checked, and the behaviour on failure is explicit: `reliable_endpoint_create` returns NULL, in debug and release alike, if the config is not valid or if an allocation fails, and it frees everything it took before doing so. The documentation is unambiguous that you must check the result rather than assume the endpoint exists.

Sending, receiving and mapping acks back to your messages

The API is a small set of functions on the endpoint. Incoming datagrams are handed to `reliable_endpoint_receive_packet`, outgoing ones go out through `reliable_endpoint_send_packet`, and acknowledgements are read with `reliable_endpoint_get_acks`, which returns a count and a pointer to an array of sequence numbers:

c
int num_acks;
const uint16_t * acks = reliable_endpoint_get_acks( endpoint, &num_acks );
for ( int i = 0; i < num_acks; i++ )
{
    printf( "acked packet %d\n", acks[i] );
}

The interesting part is how you tie that back to your own data. Before sending, you can ask the endpoint which sequence number the packet will have, using `reliable_endpoint_next_packet_sequence`, which lets you map an acknowledged sequence number to the contents of a packet you sent, for example so you can resend unacknowledged messages until a packet that included them is acknowledged. Acks are not cleared for you. You call `reliable_endpoint_clear_acks` once you have processed them, and the caveat section is explicit that if you do not, the buffer fills up, additional acks are dropped and an error is logged. That is the whole reliability model: the library tells you what was acknowledged, and resending policy stays with you.

One update per frame is what produces the statistics

The endpoint needs to be updated once per frame, which is how it keeps track of latency, jitter, packet loss and bandwidth:

c
reliable_endpoint_update( endpoint, time );

You pass the current time and the library derives the estimates from the acknowledgements it has seen. The three accessors the documentation uses in its example are the minimum round trip time, the jitter measured against that minimum, and the packet loss estimate:

c
printf( "rtt = %.1fms | jitter = %.1fms | packet loss = %.1f%%\n", 
    reliable_endpoint_rtt_min( endpoint ),
    reliable_endpoint_jitter_avg_vs_min_rtt( endpoint ),
    reliable_endpoint_packet_loss( endpoint ) );

Because the update is driven by you rather than by a background thread, the statistics are as fresh as your frame loop and the whole library is free of internal threading. The trade-off is that the call must actually happen: an endpoint that is never updated will not accumulate useful estimates, and the multi-client pattern the documentation describes, one endpoint per client slot on a server, means the update cost scales with the number of connections.

Four caveats that decide whether it fits your design

The caveats section is the most valuable part of the documentation, and each item is a design constraint rather than a warning about bugs. Acks accumulate until you clear them, and a full buffer drops further acks and logs an error, so a frame loop that forgets `reliable_endpoint_clear_acks` will quietly lose acknowledgement information. Endpoints are not thread safe, so you use one endpoint per thread or protect each with your own lock, and the log level, printf and assert handlers are global to the process, which is a problem when you embed the library in something that already has its own logging. Every pointer a callback receives is valid only for the duration of the call, and the array returned by `reliable_endpoint_get_acks` is a read only view that the next receive, clear, reset or destroy invalidates, so anything you want to keep must be copied, with the lifetime rules stated in `reliable.h`. Finally, the transmit callback must not send on the same endpoint it was called from, because it runs while that endpoint's transmit buffer is in use, though sending on a different endpoint or from the process packet callback is fine.

Recent releases are about memory safety during reassembly

The release history shows where the maintainer's attention has been, and it is not on features. v1.4.3, published on 2026-09-10, moved the fragment arithmetic into 64 bits and made configurations that cannot fit a packet be refused. v1.4.4, the next day, made fragment reassembly survive an allocator that returns NULL. v1.4.5 on 2026-09-14 guarantees that a reassembled packet never carries stale heap bytes, and that a caller's unterminated endpoint name cannot be over-read. Three patch releases in four days, each one a memory safety or arithmetic fix in the reassembly path, is a useful signal about where the risk in this kind of library lives. The repository reflects the same priorities in its layout, with `fuzz.c`, `fuzz_target.c` and an `oss-fuzz/` directory alongside `soak.c` for long running tests, `test.cpp` for the test suite, `stats.c` and `example.c`. The last push to the repository was on 2026-10-14, and the repository is not archived.

BSD 3-Clause, and a credit request rather than a requirement

The licence is the BSD 3-Clause, which is a permissive choice with no copyleft obligations, and there is a `LICENCE` file in the repository root alongside `SECURITY.md` and a `STANDARD.md` document. The library also asks for credit, and the request is framed precisely: if you use the library in a product, credit it in your product credits, with the wording "reliable - Glenn Fiedler and Rowan Claude". The documentation notes that the licence does not require this, that it is an official request, and that honouring it is appreciated, with the line that fair credit keeps open source honest. That combination, a permissive licence plus a courtesy request, is worth knowing before you ship, because a project can ask for attribution even where the terms do not compel it. The author is Glenn Fiedler, and the other open source libraries credited to the same author are netcode, serialize and yojimbo.

Editorial conclusion

Engineers writing a real time protocol on UDP who need to know which packets arrived, want to fragment large messages and want latency and loss numbers to drive their own adaptation will get exactly that from this library, and the API stays small enough to read in an afternoon. It is the wrong tool if you want a transport with its own threads, its own sockets and its own reliability policy, since it deliberately owns none of those. Before you adopt it, read the caveats section end to end, decide whether one endpoint per thread fits your threading model, and check the `reliable_endpoint_get_acks` lifetime rules against the way you intend to buffer acknowledged messages.

Frequently asked questions

What does the reliable library add to UDP?

It adds acknowledgement when packets are received, packet fragmentation and reassembly, RTT, jitter and packet loss estimates, and duplicate packet detection with dropping. It is a packet acknowledgement system rather than a full messaging layer, and it works with your own socket library.

How do I set up an endpoint in reliable?

Fill in a `struct reliable_config_t` starting from `reliable_default_config`, set the packet size, fragment threshold, fragment size, maximum fragment count and your transmit and process callbacks, then call `reliable_endpoint_create`. It returns NULL if the config is invalid or an allocation fails, in debug and release alike, and it frees what it had taken, so you must check the result.

Is reliable thread safe?

No. Endpoints are not thread safe, so you should use one endpoint per thread or protect each endpoint with your own lock. The log level, printf and assert handlers are global to the process.

How long is the array returned by reliable_endpoint_get_acks valid?

It is a read only view that the next receive, clear, reset or destroy invalidates, so you should copy anything you need to keep. The same rule applies to every pointer a callback receives, which is valid only for the duration of the call. The header states the lifetime of each one.

Official sources

  1. Issues
  2. License: BSD-3-Clause
  3. mas-bandwidth/reliable on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mas-bandwidth-reliable.svg)](https://hysenlabs.com/projects/mas-bandwidth-reliable)

Was this useful?