CLI tool
facebookincubator/fizz avatar
facebookincubator/fizz

Fizz, a TLS 1.3 implementation where states and actions are types

C++14 implementation of the TLS-1.3 standard

1,204 stars172 forksC++NOASSERTION

At a glance

What is it?
Fizz is a C++14 TLS 1.3 library built on folly, OpenSSL and libsodium, aimed at high connection counts and asynchronous work. Its distinguishing design choice is a handshake state machine that encodes states and actions in the type system, so an invalid transition is a compilation error rather than a runtime surprise, and it ships with an example binary whose command line resembles openssl.
Who is it for?
Fizz fits a C++ organisation that already depends on folly, runs a large number of TLS connections, and wants a TLS 1.3 implementation it can extend without fighting a generic SSL library, particularly one that needs certificate signing or ticket decryption offloaded to another thread.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
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 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A TLS 1.3 library shaped by folly

Fizz is a TLS 1.3 implementation, and the first line of the readme is the whole scope statement. The version support is given precisely: TLS 1.3 drafts 28 and 26, both wire-compatible with the final specification, and draft 23. All major handshake modes are supported, including PSK resumption, early data, client authentication and HelloRetryRequest.

The dependency list explains what kind of library this is. Fizz largely depends on three projects: folly, OpenSSL and libsodium. Folly is the Facebook C++ library that supplies the asynchronous runtime, the IOBufs buffer types and the futures, OpenSSL and libsodium supply the cryptographic primitives, and the crypto directory in the source tree is described as implementations that mostly wrap one or the other.

That dependency profile is the adoption decision. A project that already uses folly gets an asynchronous TLS library that fits its runtime, its buffer types and its futures. A project that does not use folly is being asked to adopt a large C++ library, with all that implies for build times, binary size and platform support, in order to get a TLS 1.3 implementation. The readme links to a post on the Facebook Code Blog from 2018 for background, which dates the design decisions.

The continuous integration badges show Linux, macOS and Windows workflows on the main branch, so the three desktop platforms are covered, which is a normal requirement for infrastructure code and still worth checking for a library that started inside one company's build system.

Six directories that mirror the protocol

The source layout is small enough to read as a description of the protocol itself. There are six directories under fizz/.

fizz/crypto holds the cryptographic primitive implementations, most of them wrapping OpenSSL or libsodium. fizz/record is the TLS 1.3 record layer parsing. fizz/protocol is the common protocol code shared between the client and the server. fizz/client and fizz/server are the client and server protocol implementations respectively. And fizz/tool is the source of the example command line application.

The split between protocol and client/server is the interesting structural decision. The parts of TLS 1.3 that differ between the two ends, such as how a client constructs a ClientHello and how a server processes it, live in their own directories, while the parts they share, the extension handling and the shared handshake logic, live in protocol. A library structured this way can have the two sides drift apart without either depending on the other.

The crypto directory being a wrapper layer rather than an implementation of its own is worth stating plainly. Fizz does not implement the cryptography; it implements the protocol, and it delegates the primitives. That is a deliberate division of labour for a library whose job is to be correct about the handshake rather than novel about the maths, and it also means the security of the primitives is the security of OpenSSL and libsodium as configured.

The state machine that catches mistakes at compile time

The design section of the readme is where the project explains itself, and the claim is about the type system. Fizz is built on a custom state machine which uses the power of the C++ type system to treat states and actions as types of their own, and as the code changes, this allows the team to catch invalid state transitions as compilation errors instead of runtime errors, which the readme says helps them move fast.

That is a specific and unusual design choice, and it explains a good deal about the class structure. The core protocol implementations are ClientProtocol and ServerProtocol. FizzClientContext and FizzServerContext provide the configuration options. FizzClient and FizzServer both inherit from FizzBase and provide applications with an interface to interact with the state machine.

The event flow is described in one sentence: FizzClient and FizzServer receive events from the application layer, invoke the correct event handler, and invoke the application ActionVisitor to process the actions. So the application does not call into the protocol directly. It receives actions, and an ActionVisitor implementation decides what to do with them, which means the same handshake core can drive a client, a server, or a test harness that simply records what it was asked to do.

For a reviewer, the interesting question is whether the type-level encoding is worth the complexity. The answer depends on the codebase. In a project where the handshake state machine is edited often, by several people, and where a wrong transition would be a security bug rather than a crash, moving that class of error to compile time is a good trade. In a small project with one handshake implementation, it is a lot of machinery.

Async by default, and the work you can push off the connection

The async section explains a decision that shows up in the API. Fizz has asynchronous APIs in order to be able to offload functions like certificate signing and ticket decryption, and the API is based on folly's Futures for painless async programming.

The mechanism is AsyncFizzClient and AsyncFizzServer, which provide implementations of the folly AsyncTransportWrapper interface. They own an underlying transport, for example an AsyncSocket, and perform the TLS handshake and encrypt and decrypt the application data.

Putting the transport inside the TLS object is what makes the offloading work. Because Fizz owns the socket, it can hand the socket to another thread for a slow operation, such as asking a certificate authority to sign, or decrypting a session ticket, and then resume the state machine when the answer arrives. With a design where the transport sits outside the TLS layer, that is much harder to arrange, because the TLS code has no way to suspend a read on a socket it does not own.

Two more TLS-specific capabilities are listed under the TLS features heading: APIs like exported keying material, and zero-copy APIs needed to use TLS inside other protocols such as QUIC. Exported keying material is the mechanism TLS 1.3 provides for deriving additional secrets from the handshake, which is what lets a protocol layered on top of TLS agree on keys without a second key exchange. The zero-copy path is the same requirement seen from the other side: QUIC encrypts its own packets, so it needs the record layer to hand over plaintext buffers rather than encrypted ones.

The trio, async by default, exported keying material and zero-copy support, is the feature set that distinguishes Fizz from a general purpose SSL library.

The performance claim, and where it comes from

The performance section makes two claims, and the second is the one an evaluator should look at closely.

The first is about mechanism. Fizz supports scatter/gather IO by default via folly's IOBufs, and will encrypt data in place whenever possible, saving memcpys. Scatter/gather means a write can take several buffers without concatenating them first, and in-place encryption means the record layer encrypts the application buffer where it already sits rather than into a fresh allocation. Both are the kind of detail that matters when you are doing tens of thousands of connections, and both are the sort of thing a general library tends to get wrong by copying.

The second claim is a number. The readme says that because of this and several other optimizations, the team found in its load balancer benchmarks that Fizz has 10 percent higher throughput than its prior SSL library, which uses folly's AsyncSSLSocket, and that Fizz also consumes less memory per connection than AsyncSSLSocket.

Two cautions apply. The comparison is against a specific internal baseline, folly's AsyncSSLSocket, not against OpenSSL or BoringSSL, so it tells you about a decision to replace an existing library rather than about absolute performance. And the benchmark is described as a load balancer benchmark on the project's own hardware, with no methodology, input size or connection count published, so the number is a report rather than a result you can reproduce. The direction of the claim is plausible given the mechanisms described, but treat 10 percent as the project's figure and not as a guarantee for your workload.

Building: getdeps in CI, CMake by hand

Two build paths are documented, and they reflect the project's dual life inside and outside Meta's build system.

The first is getdeps.py, described as a script that many fbcode open source projects use, which automatically downloads the dependencies of a project and builds the project. The readme says this is what the continuous integration uses as its main entry point:

bash
python3 build/fbcode_builder/getdeps.py --allow-system-packages build

The second is the manual route, and the readme is straightforward about the requirement: Fizz is a typical CMake project, and in order to build it you must configure CMake such that Fizz's dependencies can be found, for example by using the CMAKE_PREFIX_PATH variable. The example configures with Ninja and sets both the prefix path and the install prefix:

bash
cmake \
    -GNinja
    -DCMAKE_PREFIX_PATH=/path/to/prefix \
    -DCMAKE_INSTALL_PREFIX=/path/to/prefix \
    -B/build/fizz \
    /path/to/checkout/fizz

cmake --build /path/to/checkout/fizz
cmake --install /atph/to/checkout/fizz

The example has a typo in the install line, with a misspelled path, and the build line points at the source directory rather than the build directory, so expect to fix those two when you copy it. Everything else is ordinary CMake.

The repository root shows the Buck side of the project as well, with a BUCK file,.buckconfig,.buckconfig.d,.buckroot, a buck2 directory, a bad_targets file and an oss_buck_build_and_test.sh script. So the project is buildable with both CMake, for the outside world, and Buck, for the inside one, and a reader looking at the tree will find both.

Where Fizz is the wrong tool

The clearest limit is the protocol version. The readme describes TLS 1.3 support and nothing else, with drafts 28, 26 and 23, and says nothing about earlier versions of the protocol. A deployment that has to negotiate TLS 1.2 with an old client, or a device that only speaks TLS 1.0, is outside what is described here, and a TLS library that only does one version is a specialist choice.

The second is the dependency weight. Carrying folly into your build to get a TLS implementation is a significant commitment, and it is the reason the example client and server exist: a small demonstration that a TLS library can be driven end to end without a large application around it.

The third is the security reporting process, which is unusual and worth reading before you file anything. The readme asks explicitly that you do not open GitHub issues or pull requests for security issues, because that makes the problem immediately visible to everyone including malicious actors, and directs reports to Facebook's Whitehat Bug Bounty program, where the security team will triage the report and determine whether it is eligible for a bounty. For a user of the library that is a good arrangement. For a researcher who wants to report a bug and be acknowledged, it is a closed door, and it is a policy you inherit the moment you depend on the code.

The licence is worth a sentence of its own. The readme says Fizz is BSD licensed, as found in the LICENSE file, while GitHub's own classifier cannot categorise it, so read the file rather than relying on either label. The release naming is date based and weekly, with v2026.10.12.00, v2026.10.05.00 and v2026.09.28.00 in the recent list, which is a sensible scheme for infrastructure code that is deployed continuously.

Editorial conclusion

Fizz fits a C++ organisation that already depends on folly, runs a large number of TLS connections, and wants a TLS 1.3 implementation it can extend without fighting a generic SSL library, particularly one that needs certificate signing or ticket decryption offloaded to another thread. It does not fit a team that needs a broad TLS version range, a small project that would rather take a dependency than carry folly, or anyone who expects to report a vulnerability through the normal issue tracker, because the README asks you not to. Before adopting it, confirm that the drafts it implements match what your peers negotiate, since it supports drafts 28, 26 and 23, and read the LICENSE file directly, because the readme calls it BSD licensed while GitHub cannot classify it. Releases are date-stamped and weekly, with v2026.10.12.00 the most recent, and the last push was on 2026-10-15.

Frequently asked questions

What is Fizz?

Fizz is a C++14 TLS 1.3 implementation. It largely depends on three libraries, folly for the asynchronous runtime and buffer types, OpenSSL and libsodium for the cryptographic primitives, and it is organised into directories for crypto, record, protocol, client, server and a command line tool.

Which TLS 1.3 versions does Fizz support?

TLS 1.3 drafts 28 and 26, both wire-compatible with the final specification, and draft 23. All major handshake modes are supported, including PSK resumption, early data, client authentication and HelloRetryRequest.

How do I build Fizz from source?

Continuous integration uses the getdeps script, run as `python3 build/fbcode_builder/getdeps.py --allow-system-packages build`. Manually, Fizz is a CMake project and you must configure it so its dependencies are found, for example with CMAKE_PREFIX_PATH and Ninja, then run cmake --build and cmake --install.

How do I report a security issue in Fizz?

Do not open a GitHub issue or pull request, since that would make the problem immediately visible to everyone. Reports go through Facebook's Whitehat Bug Bounty program, where the security team triages the report and decides whether it is eligible for a bounty.

Is Fizz asynchronous, and can slow work be offloaded?

Yes. AsyncFizzClient and AsyncFizzServer implement folly's AsyncTransportWrapper interface, own the underlying transport such as an AsyncSocket, and use folly's Futures so that work like certificate signing and ticket decryption can be offloaded.

Official sources

  1. facebookincubator/fizz on GitHub
  2. Issues
  3. README
  4. 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/facebookincubator-fizz.svg)](https://hysenlabs.com/projects/facebookincubator-fizz)

Was this useful?