connet: a peer-to-peer reverse proxy that keeps tunnels off the open internet
A p2p reverse proxy with NAT traversal. Inspired by frp, rathole and ngrok
At a glance
- What is it?
- connet exposes a service running behind NAT by running its own client on both ends, so the control server only exchanges configuration and the two peers often connect directly over QUIC, with a relay as the fallback. It is alpha software with changing APIs, and it is written in Go with a well-defined API for embedding.
- Who is it for?
- Adopt connet if you need to reach a service behind NAT and you are willing to run the client on both the exposing machine and the accessing machine, because that is what buys the peer-to-peer path and keeps traffic off the public internet. Do not adopt it if you need a stable API, since the project states it is alpha software whose APIs are subject to change, or if you need only one side to run client software.
- 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 44 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 October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Why a client on both ends changes the privacy picture
connet is a peer-to-peer reverse proxy for NAT traversal, inspired by ngrok, frp, rathole and others. What separates it from that family is stated in one sentence in the README: the connet client runs on both the device that exposes the service, called the destination, and the device that wants to access the service, called the source.
The consequence is that communication between connet clients is never public and visible to the rest of the internet, and in many cases peers can communicate directly. A conventional reverse proxy puts your traffic through a hosted edge, which is fine for a public web service and wrong for something internal that you only want a colleague to reach.
Because both ends are yours, the control server's job shrinks. The server is only used for sharing configuration, and it hands out the information the peers need to find each other, including relay information. If a direct path works, the traffic never touches the server at all. If it does not, the peers fall back to a relay server, and the README is explicit that there are cases when clients are unable to find a path to communicate directly.
The audience is a developer who needs to reach a service on a laptop, a device in a lab, or a machine in a private network, and who would rather not open a port to the internet or route that traffic through a third party's edge. The project states its status plainly: connet is currently alpha software, issues are expected, and its APIs are subject to change.
That status line should shape your expectations. This is a tool to try on a weekend, not to build a service dependency on.
Control server, relay server and the two client roles
The architecture has three moving parts and the mermaid diagram in the README makes the relationships clear.
The control server is the rendezvous point. Both the source and the destination connect to it, and the diagram labels that exchange as the direct and relay information they trade with the control server. The control server also passes relay info to the relay server. The relay server exists for the case where a direct path is not available, and the diagram shows the source connecting to the destination directly when possible, and both connecting through the relay when not.
The client roles are symmetric in configuration. The destination side exposes something, and the source side consumes it, and both talk to the same control server with their own token.
Everything runs over QUIC for all communication, which the README notes is built on top of UDP. The choice matters for a NAT traversal tool in practice, because UDP is what hole punching needs and what QUIC gives you as a multiplexed, encrypted transport with connection migration properties that a TCP-based tunnel does not have.
The repository layout reflects the structure. There are separate files for the two roles at the top level, destination.go and source.go with their matching config files, plus direct.go, peer.go, peer_remote.go, relay.go and endpoint.go for the connection machinery. A server/ directory holds the server side, cmd/ holds the command line entry points, pkg/ holds shared code including the protobuf definitions, and examples/ contains runnable configurations.
Getting a three-device setup running
The quickstart asks for three devices. A server your clients can reach, in most cases with a public IP and directly accessible, and a VPS instance at a cloud provider is described as going a long way. A device D with the destination service running at port 3000, and a device S, the source, that wants to reach it on port 8000.
The server is started with connet server --config server.toml and needs a token list and an ingress entry with a certificate and key:
[server]
tokens = ["client-d-token", "client-s-token"]
[[server.ingress]]
cert-file = "cert.pem"
key-file = "key.pem"The server needs a TLS certificate, and the README gives three options: use an ACME client to provision one, with lego named as having worked well for the authors; buy one from a certificate authority; or use a self-signed certificate, described as most appropriate for testing. With a self-signed certificate your clients and relays have to trust the server's certificate, and the easiest way is to copy the certificate or CA public key to the clients and point the server-cas-file configuration option at it.
The destination client runs connet --config destination.toml:
# destination.toml
[client]
token = "client-d-token"
server-addr = "SERVER_IP:19190"
server-cas-file = "cert.pem"
[client.destinations.serviceA]
url = "tcp://:3000"And the source client runs connet --config source.toml with the same shape and its own token:
# source.toml
[client]
token = "client-s-token"
server-addr = "SERVER_IP:19190"
server-cas-file = "cert.pem"
[client.sources.serviceA]
url = "tcp://:8000"The two sides line up on a name, serviceA in both files, which is how the source knows which destination to reach.
Tokens, certificates and the QUIC connection settings
The security model has three parts, and they are worth reading in the configuration rather than in the abstract.
Everything is private and encrypted with TLS, and the public server and client certificates are exchanged between peers and are required and verified to establish connectivity. Separately, clients and relays must present a mandatory token when communicating with the control server, which is what allows tight control over who can use connet at all. Note that the token list lives in the server's own configuration, so the server is the one place where all valid tokens are known.
The full client configuration specification in the README shows how much of the protocol is exposed. There is a token-file option pointing at a file that contains the auth token for the control server, and a token option as a fallback for when token-file is not specified. If both are empty, it reads the CONNET_TOKEN environment variable. A metadata field is sent when authenticating, to help identify the client, with the example value home.
The connection settings show the default ports. The server-addr is the control server address over UDP and QUIC in host:port form, defaulting to 127.0.0.1:19190. The direct-addr is the address the client listens on for peer connections, also UDP and QUIC, defaulting to:19192. The server-name option covers the case where you connect by IP but the certificate contains only domain names, in which case it defaults to the host in server-addr.
Two QUIC-specific options are worth calling out because they are the kind of thing that bites in production. The direct-stateless-reset-key is the QUIC stateless reset key as a literal 32 byte value in base32hex format, and there is a companion file-based option for the same value. A stateless reset key lets the server close a connection that has lost its state without waiting for a timeout, which matters for a long-lived tunnel. And a status-addr listens over TCP and HTTP for status connections, disabled by default.
One more asymmetry to be aware of: command line options take precedence over the config file when you use both, and for simplicity the command line only supports a single destination or source configuration. The config file is the way to run several routes.
Embedding connet in a Go program
The fourth feature in the list is the one that separates connet from a set of shell scripts. It is embeddable: if you want connet running as part of another Go program rather than as a separate executable, connet has a well defined API for running both the client and the server.
The repository structure supports that reading. The core files sit at the top level of the module, with client.go, config.go, destination.go, destinations.go, direct.go, endpoint.go, peer.go, peer_remote.go, relay.go, source.go and sources.go, and the module path is github.com/connet-dev/connet. That layout is what a library-style API looks like when it is also a command line tool, with cmd/ providing the executables on top.
For a Go team this is the interesting adoption path. A service that needs to expose a local worker to a colleague, or that wants the control server embedded in an existing control plane rather than run as a separate binary, can do that without shelling out.
The cost of embedding is that you inherit the alpha status. The APIs are subject to change, so an embedded integration is coupled to a version of connet that will move, and a major version bump is more disruptive when the code is in your process than when it is in a container you can replace.
The dependency list in go.mod also tells you what you are taking on. quic-go at v0.60.0 is the transport, gorilla/websocket for the websocket support, pelletier/go-toml for the configuration format, spf13/cobra for the command line, pires/go-proxyproto for proxy protocol handling, jackpal/gateway for gateway discovery, klev-dev/klevdb for the storage layer, google.golang.org/protobuf for the wire format, and golang.org/x/crypto for the cryptography. The module targets Go 1.26.2.
Where connet is the wrong choice
Three situations call for a different tool, and the alpha status makes two of them sharper.
The first is when you only want to expose a public service. connet is built for reaching something specific, from a specific source, over a path that is not on the open internet. If what you need is a public URL in front of a web app, a hosted tunnel or a conventional reverse proxy with a public IP does that job, and does it without a client on the visitor's side. connet's design assumes a cooperating source, which is the point of it and also its limit.
The second is when you cannot run software on the accessing machine. Because the source side needs a connet client, the source cannot be an ordinary browser or an unmodified device. If the consumer of the service is a person with a laptop you do not control, the peer-to-peer model is the wrong shape, and a hosted tunnel with a public endpoint is the only option that works.
The third is anything that depends on a stable contract. The README's own status line says the APIs are subject to change, and the release numbers, v0.15.1, v0.15.2 and v0.15.3 in quick succession on consecutive days in May 2026, suggest active churn. Building a production dependency on alpha software with a moving API is a decision you should make deliberately rather than by default.
There is also a structural limit to the privacy claim. The traffic between peers is not visible to the rest of the internet, but the control server does see both clients and hands them the information they need, and the destination's address is registered with it. If your threat model includes hiding the existence of the service from the server operator, this is the wrong design.
Editorial conclusion
Adopt connet if you need to reach a service behind NAT and you are willing to run the client on both the exposing machine and the accessing machine, because that is what buys the peer-to-peer path and keeps traffic off the public internet. Do not adopt it if you need a stable API, since the project states it is alpha software whose APIs are subject to change, or if you need only one side to run client software. Verify first that your control server has a public IP and a TLS certificate, that the token you issue to each client matches the tokens list on the server, and that the ports the two clients expect for direct and relay connections are reachable in your network.
Frequently asked questions
What is connet and how is it different from ngrok or frp?
connet is a peer-to-peer reverse proxy for NAT traversal where the client runs on both the destination and the source, so the control server is only used for sharing configuration and peers often connect directly. That keeps communication between clients off the public internet and can improve both privacy and performance, at the cost of needing the client on both ends.
How do I set up a connet server?
Start it with connet server --config server.toml, giving it a tokens list and an ingress entry with cert-file and key-file. The server also needs a TLS certificate, which the README suggests obtaining through an ACME client such as lego, buying from a certificate authority, or using a self-signed certificate for testing.
How do the connet destination and source configurations link up?
Both clients point at the same control server with server-addr and their own token, and they reference the same route name, serviceA in the documented example. The destination exposes it under client.destinations with a url such as tcp://:3000, and the source consumes it under client.sources with a url such as tcp://:8000.
Does connet use TCP or QUIC?
All communication uses the QUIC protocol, which is built on top of UDP. The control server address and the direct peer address are both configured in host:port form over UDP and QUIC, and the status server listens over TCP and HTTP.
Is connet ready for production use?
The README states that connet is currently alpha software, that issues are expected and that its APIs are subject to change. The last push was on 2026-09-09 and the recent releases include v0.15.1, v0.15.2 and v0.15.3 in May 2026.
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/connet-dev-connet)Was this useful?