cmux: one TCP listener, several protocols, matched on the first bytes
Connection multiplexer for GoLang: serve different services on the same port!
At a glance
- What is it?
- A small Apache-2.0 Go library that inspects the start of every inbound connection and hands it to the right server, so gRPC, HTTP and SSH can share a port. The README documents its limits more carefully than most libraries document their features.
- Who is it for?
- cmux is worth using when a service must speak more than one protocol on one port and you would rather not front it with a reverse proxy or allocate port ranges. It is small enough to read, its matchers are ordered so the routing logic is explicit, and the README is honest about the two situations where it does not work, TLS type assertions and mixed protocols on one connection.
- Can I use it commercially?
- Yes. Apache-2.0 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 last received commits 124 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Matching the first bytes of a connection, in order
The library does one thing: it reads the beginning of each accepted connection and decides which listener should own it. The README's description is that cmux is a generic Go library to multiplex connections based on their payload, letting you serve gRPC, SSH, HTTPS, HTTP, Go RPC and pretty much any other protocol on the same TCP listener.
The mechanism is a lookahead. When a connection arrives, cmux holds it until enough bytes have arrived to classify it, then passes it to whichever matcher claimed it. The `example` directory and the README's How-To show the whole shape, which is four steps: create the listener, create a mux over it, register matchers in priority order, then serve.
l, err := net.Listen("tcp", ":23456")
m := cmux.New(l)
grpcL := m.Match(cmux.HTTP2HeaderField("content-type", "application/grpc"))
httpL := m.Match(cmux.HTTP1Fast())
trpcL := m.Match(cmux.Any())
m.Serve()The ordering is the design. `HTTP2HeaderField` claims gRPC first, then `HTTP1Fast` claims ordinary HTTP, and `Any()` catches everything not yet matched. First match wins, so a permissive matcher placed early silently swallows everything behind it. That is the main thing to get right when writing the list.
Each matched listener is an ordinary `net.Listener`, so it drops into a gRPC server, an `http.Server` or a Go RPC server without either side knowing a mux is involved.
Where the routing logic actually lives
The repository is small enough to read in a sitting, and the file names explain the design. `cmux.go` holds the mux itself, `matchers.go` holds the matchers, `buffer.go` the buffered reader used during lookahead, and `patricia.go` a patricia trie, with `patricia_test.go` beside it. The trie is for matching multi-field patterns efficiently rather than comparing every matcher against every connection.
That is the interesting engineering in the package. A matcher such as `HTTP2HeaderField` cannot decide from the first two bytes, because HTTP/2 starts with a connection preface that is shared with ordinary HTTP/2. It has to buffer, parse headers, and compare. Doing that efficiently across many matchers is what the trie is for, and it is why the package can afford to expose a large matcher vocabulary without a per-connection cost that scales with the number of matchers.
The dependency list is correspondingly thin. The `go.mod` names Go 1.23.0 and requires `golang.org/x/net v0.42.0`, with `golang.org/x/text` marked indirect:
module github.com/soheilhy/cmux
go 1.23.0
require golang.org/x/net v0.42.0One direct dependency for HTTP/2 handling, which is the honest minimum for a library whose job involves inspecting HTTP/2 frames. Tests are in `cmux_test.go` and `bench_test.go`, and `doc.go` holds the package documentation.
The limitations section is the most useful documentation here
Many library READMEs list features. This one also lists what breaks, in three named cases, and each one is worth understanding before you commit to the approach.
TLS is the first. `net/http` identifies a TLS connection through a type assertion on the connection object, and because cmux's lookahead wraps the underlying connection to implement buffering, that assertion fails. So you can serve HTTPS through cmux, but `http.Request.TLS` will not be set in your handlers. Any middleware that reads `r.TLS` to enforce HTTPS, set HSTS or inspect client certificates will silently see a nil pointer and behave as though the connection were plaintext.
The second limitation is a design assumption you may not share: cmux matches the connection once, when it is accepted. One connection is either gRPC or REST, never both. If your service needs a single connection to carry gRPC and ordinary HTTP traffic interleaved, this library cannot do it and no configuration will change that.
The third is a concrete interop bug with a specific fix. Java gRPC clients block until they receive a SETTINGS frame from the server, so matching with the read-only header matcher leaves those clients hanging. The README gives the alternative matcher:
grpcl := m.MatchWithWriters(cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc"))`MatchWithWriters` exists because writing to the connection during classification is sometimes necessary to complete the handshake. That it is documented here, with the reason, tells you something about the maintainer.
Where the README has fallen behind the repository
Two details suggest the README was written earlier than the code and not fully revisited.
The first is the build badge. The title line carries a Travis CI status image, and the URL behind it points at `travis-ci.org/soheilhy/args.svg`, referencing a repository named args rather than cmux. That badge is measuring a different project, or a project that has been renamed, and either way it tells you nothing about this code. The tree does contain a `.github/` directory, so whatever CI runs now lives there instead.
The second is the performance section, which says there is room for improvement but that since only the very first bytes of a connection are matched, the overhead on long-lived connections such as RPCs and pipelined HTTP streams is negligible. It then adds a note that benchmarks are still to be added. Meanwhile the repository contains `bench_test.go`. So the file the README says is missing exists, and the README's performance claim has no numbers behind it either way.
The module path tells you where to read the current documentation. The README points at godoc.org and at `godoc.org/github.com/soheilhy/cmux`, and while that is where Go package documentation for this vintage of project lives, the versioned API docs on pkg.go.dev for `github.com/soheilhy/cmux` are the ones that match what `go get` will actually fetch you.
On releases, the picture is worth understanding. Tagged releases are v0.1.3 from 2017, v0.1.4 from 2018 and v0.1.5 from 2021, a version number that signals an API the author never promised to stabilise. The repository is not archived and the last push was on 2026-06-08, so work continues while the version stays below one.
When to reach for a reverse proxy instead
The honest comparison is not with another multiplexer but with not multiplexing at all. Putting nginx, Envoy or a load balancer in front lets you route by host, path and header on connections that cmux cannot distinguish, gives you TLS termination with proper `http.Request.TLS`, and adds request-level metrics and access logs that a byte-level mux has no visibility into. If you are running this in production and can afford another process, that is often the better trade.
cmux wins in a narrower set of circumstances. When the service is a single Go binary that clients address directly, and adding a proxy means adding a deployment, a configuration language and a failure mode. When the protocols are genuinely distinguishable at connection start, such as SSH against an HTTP API. And when the deployment target makes binding one port materially easier than binding four, which is common in container platforms with strict ingress rules.
The name collision is worth a warning too. Searching for cmux returns an unrelated project of the same name in current results, so the import path, `github.com/soheilhy/cmux`, is the only reliable identifier. It is also worth noting what cmux does not attempt: no health checking of the servers behind the matched listeners, no load balancing, no metrics, and no connection-level logging. Those remain the application's problem, and a mux that silently drops an unmatched connection will leave you guessing whether the client was wrong or the matcher list was.
Editorial conclusion
cmux is worth using when a service must speak more than one protocol on one port and you would rather not front it with a reverse proxy or allocate port ranges. It is small enough to read, its matchers are ordered so the routing logic is explicit, and the README is honest about the two situations where it does not work, TLS type assertions and mixed protocols on one connection. What it will not do is route by anything other than the opening bytes, and it makes no attempt at load balancing or health checking. Note that the project name collides with at least one unrelated product of the same name, so import it as `github.com/soheilhy/cmux` rather than searching for cmux alone. The last tagged release is v0.1.5 from 2021 while commits have continued since, so treat the module as maintained but unpinned.
Frequently asked questions
What is cmux used for in Go?
It multiplexes connections on a single TCP listener by inspecting the payload at the start of each connection and handing it to the matching server. The README says you can serve gRPC, SSH, HTTPS, HTTP and Go RPC, or any other protocol, on the same port.
How do you use cmux to serve gRPC and HTTP on one port?
Create the listener, wrap it with cmux.New, then register matchers in order and serve. The README's example matches cmux.HTTP2HeaderField("content-type", "application/grpc") first, then cmux.HTTP1Fast(), then cmux.Any() as the catch-all for anything not yet matched.
What are the limitations of cmux?
The README lists three. TLS connections are wrapped by the lookahead, so http.Request.TLS is not set in handlers. Matching happens once when a connection is accepted, so one connection cannot carry both gRPC and REST. And Java gRPC clients hang unless you match with MatchWithWriters and HTTP2MatchHeaderFieldSendSettings.
What Go version and dependencies does cmux need?
The go.mod declares Go 1.23.0 and requires golang.org/x/net v0.42.0, with golang.org/x/text v0.27.0 as an indirect dependency. The module path is github.com/soheilhy/cmux and the license is Apache 2.0.
Is cmux actively maintained?
The repository is not archived and the last push was on 2026-06-08, so commits continue. The most recent tagged release is v0.1.5 from 2021-03-26, after v0.1.4 in 2018 and v0.1.3 in 2017, so the version has stayed below one for years while the code kept moving.
Official sources
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.
[](https://hysenlabs.com/projects/soheilhy-cmux)