Describe your remotes in a schema. Get server and client modules that pack them into buffers, check everything a client sends, and hold up when a client is hostile.
You describe your events and functions, and what they carry, in a .blink file. The compiler turns
that file into a server module and a client module. Both are plain Luau with no runtime dependency.
Calls are batched into one buffer per frame, and each type is packed as tightly as its declaration
allows.
Packets are bounded before they are parsed. Each player gets a byte budget, and each event can
get its own rate limit, each function a cap on the calls still running. Every length is
checked before the read it pays for. A malformed event ends its packet without throwing, and
the game is told who sent it. How the server protects
itself.
Small on the wire
Booleans and optional flags share a bitfield, and boolean[] packs eight elements to a byte.
A length is sent relative to its range, a rotation fits in 7 bytes, and 24-bit numbers make a
position 9 bytes instead of 12. Unreliable events that cannot fit are refused at compile time. Bandwidth and sizes.
Checked at compile time
A schema that cannot work never builds. That covers map keys that can never be looked up,
cyclic imports, unbounded decode loops and debug remotes left in a release build. Each gets a
diagnostic with a code and a source span. Diagnostics.
Tooling included
The CLI has watch mode and build profiles, and --check --json hands every diagnostic to an
editor or an AI assistant as JSON. A Studio plugin gives you an editor with live diagnostics
and autocomplete, and TypeScript definitions are available for roblox-ts.
Studio plugin.
BlinkBlox is a maintained fork of Blink. Upstream froze this line
of the compiler in 2026 and began a rewrite, and it declared an unbounded parse of a hostile client
buffer out of scope. This fork fixes that and continues from there, without giving up the
TypeScript output, the Studio plugin or the documentation.
Buffer code written by hand is the other neighbour, and for a server’s own sends or a remote or two
it is the simpler choice. What it leaves to you is the receiving side: every length, range and class
a hostile client can get wrong, in every remote.
Compared with hand-written buffers shows both readers for
one event.
Zap is a neighbouring compiler that focuses on bandwidth. It
leaves rate limiting to the game, and its decode loop has no pcall: one malformed event throws out
of it, discarding the rest of that player’s batch with nothing for the game to catch.
QuickNet and
Warp are libraries rather than compilers: a schema is declared at
runtime and packed into buffers. QuickNet limits each event per player and bounds a packet’s size
and its calls, and a pcall around each packet stops a malformed event from throwing, as BlinkBlox’s
does. It only warns in the output rather than telling the game who sent it, and allocates whatever
array length a client names before reading any of it. Warp counts a player’s bytes a packet at a
time, outside Studio only, and reads lengths the client sends straight into table.create.
BlinkBlox
Blink 0.18
Zap
QuickNet 0.3
Warp 1.1
Per-player, per-event rate limits
Yes
No
No
Yes
No
A decode failure is caught and reported to the game
Speed is not the reason to choose BlinkBlox: a reader written by hand for one event can be as fast
as anything generated for it. The numbers compare tools that do the same job, and hold each release to
the one before it.
A thousand events a frame from client to server, run in Studio on an Intel Core i7-13700K on 0.41.2.
Blink is upstream’s last release, 0.18.9. The numbers are median frame rates, with the milliseconds a
frame’s thousand fires took in brackets. Warp’s Fire only queues its value and encodes it later in
the frame, where the bench cannot time it, so only its frame rate is shown.