Open-source project
howmp/reality avatar
howmp/reality

reality: a reverse SOCKS5 tunnel that looks like ordinary TLS

grs内网穿透工具通过reality协议隐藏特征

635 stars58 forksGoMIT

At a glance

What is it?
reality is a Go implementation of a reverse SOCKS5 proxy built on the REALITY protocol, so the traffic between the server, the client in the intranet and the user side is shaped like a TLS 1.2 or 1.3 connection to a real HTTPS site. It suits operators who need a self-hosted tunnel that resists active probing, and it asks the user to run three binaries and keep their clocks in sync.
Who is it for?
reality fits an operator who needs a self-hosted reverse tunnel to a machine behind a NAT, and who wants the connection to a server to be indistinguishable from traffic to a real HTTPS site rather than a proxy handshake. It is the wrong choice for a team that wants a general purpose reverse proxy, since this project is a single SOCKS5 path with three binaries.
Can I use it commercially?
Yes. MIT 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 76 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 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Three binaries and one tunnel direction

The project is called grs and consists of three Go programs with distinct roles. grss, the Golang Reverse SOCKS5 Server, runs on a machine with a public IP address. grsc, the Golang Reverse SOCKS5 Client, runs inside the intranet you want to reach. grsu, the Golang Reverse SOCKS5 User, runs on the machine of the person who wants the access and provides a SOCKS5 service locally.

The direction of travel is what makes this a reverse proxy rather than a forward one. The client inside the intranet dials out to the public server, and the server multiplexes that connection. The user side then connects through the server to the intranet client, and traffic is proxied back through that already-established channel. The user never needs an inbound port on the intranet side, which is exactly the problem the design is meant to solve.

The comparison the README makes is with frp and nps, and it lists three differences. The first is that network fingerprints are eliminated, which is the project's own claim rather than an audited property. The second is that the server is protected from active probing. The third is that the client and the user side carry their configuration inside the binary, so no command line and no extra configuration file are needed on those machines.

That last point is a genuine design decision rather than a convenience. The intranet machine is often the one you have least control over, and a binary that needs no arguments and no config file is a binary you can push onto a box and run.

What the REALITY layer changes on the wire

The core of the project is that grs is a reverse SOCKS5 proxy based on the REALITY protocol, and the communication between grss, grsc and grsu is disguised as a TLS 1.2 or 1.3 connection to a real HTTPS site. The stated purpose is to remove network characteristics and to prevent active probing, and the README points to a separate README-REALITY.md file for the protocol details.

The Go module list shows the mechanism. The dependency on refraction-networking/utls is the important one: uTLS lets the client present a chosen TLS fingerprint rather than the one Go's standard library produces. The generator exposes this directly through a finger print option that accepts chrome, firefox, safari, ios, android, edge, 360 and qq, with chrome as the default. A TLS ClientHello that matches a real browser is the difference between a connection that looks like a browser and one that looks like a Go program.

Two other dependencies are structural. hashicorp/yamux provides the multiplexing over the single outbound connection, and armon/go-socks5 provides the SOCKS5 server side for grsu. The rest are ordinary: go-flags for the command line, logrus for logging, x/crypto and x/net for the cryptography and transport.

The fingerprint option is the part of the design worth thinking about carefully, because it is a choice about what your traffic should look like rather than about what protocol it uses. Every fingerprint is a claim, and a claim that a Chrome client is talking to a specific site is a claim that can be checked against the site actually running that TLS stack.

Generating the server config and the clients

Everything starts with a single generator command, which takes two arguments: the address of the site to be simulated and the address the server listens on. The README is explicit that the first is the target being simulated and the second is the server listen address, where you put the public IP and ideally a port matching the simulated target. If the SNIAddr or ServerAddr arguments are not given, an existing configuration file is loaded instead.

The generator writes the server configuration and produces clients. By default it creates three clients with different id file names, and the count is settable with the -c parameter. The full option set is listed in the usage output:

bash
  grss [OPTIONS] gen [gen-OPTIONS] [SNIAddr] [ServerAddr]

generate server config and client

Help Options:
  -h, --help                                                 Show this help message

[gen command options]
      -d                                                     debug
      -f=[chrome|firefox|safari|ios|android|edge|360|qq]     client finger print (default: chrome)
      -e=                                                    expire second (default: 30)
      -o=                                                    server config output path (default: config.json)
      -c=                                                    client count (default: 3)
      -s                                                     skip client cert verify
          --dir=                                             client output directory (default: .)

Reading that list tells you the shape of the whole tool. The server configuration goes to config.json by default. The client output directory defaults to the current directory, which matters because the generated client files are what you copy to the intranet machine. The expire second option, 30 by default, is a clock-skew tolerance, and the skip client cert verify flag exists for environments that lack root certificates.

Generating three clients by default is a small but sensible default. It means a deployment has more than one client identity available, and the id of a client is what the user side selects when connecting.

Running the server, the client and the user side

The server takes a single subcommand, serv, and a path to the config file:

bash
  grss [OPTIONS] serv [serv-OPTIONS]

run server

Help Options:
  -h, --help      Show this help message

[serv command options]
      -o=         server config path (default: config.json)

The client is started by running the generated file whose name carries the id, and the README denotes it as grscX where X is the id. The user side is started with grsu and an id argument, and the documentation is clear that the id corresponds to the grsc id, so a different id connects to a different client.

The user side options are short:

bash
Usage of grsu:
  -i uint
        id
  -l string
        socks5 listen address (default "127.0.0.1:61080")

The default SOCKS5 listen address is 127.0.0.1 on port 61080, bound to loopback. That is a deliberate default: a SOCKS5 proxy with no authentication listening on all interfaces would be an open proxy for the whole network, and binding to loopback means only processes on that machine can use it, with a local client or an SSH port forward for anything else.

The design overall is that the only thing an operator moves between machines is a file. The server needs a config file, the intranet machine needs a generated client, and the user machine needs a generated user binary. None of them needs a config file of their own, which is the third of the three differences the README claims over frp and nps.

Clock skew, key rotation and the certificate error

The FAQ section is the most useful part of the documentation, because it covers the two failure modes you will actually hit.

The first is a verify failed message, and the documented causes are a time difference between the server and the client or user side exceeding the expire second window, a mismatch after the server configuration is regenerated, and a hijacked client network. The default tolerance is 30 seconds, and the fix given for a wider window is to pass -e 60 at generation time, or to synchronise the clocks over NTP. The second cause is the one that bites hardest: after regenerating the server configuration you must use the latest grsc and grsu files, otherwise the preshared keys do not match.

That second point deserves emphasis, because it is the kind of operational detail that produces a confusing outage. The keys are baked into the generated binaries, so regenerating the server config invalidates every previously issued client, and the failure looks like a network or certificate problem rather than a key rotation.

The second FAQ covers the certificate signed by unknown authority error, which happens when the runtime environment lacks root certificates, and the documented workaround is to generate with -s to skip verification.

The first FAQ answers what happens when the server is probed. The answer is that the server acts as a port forward when probed, and the certificate is exactly the one belonging to the simulated target. The documentation suggests verifying this by editing the local hosts file and visiting the site in a browser, or with a curl invocation using resolve to point the simulated hostname at the loopback address. That is a testable claim, and testing it is the right way to confirm the setup is doing what the documentation says.

Where reality is the wrong tool

The clearest mismatch is with a general purpose reverse proxy. frp and nps are general purpose intranet penetration tools, whereas reality exposes one SOCKS5 path. reality exposes one SOCKS5 path with three binaries and a generator, and it has no dashboard at all, since the server takes a config file and a path to it. If you need more than a single SOCKS5 path, a general purpose tool is likely the better fit.

The second limitation is the client count and the shape of the multiplexing. The generator produces a fixed set of client identities, and the user side selects one by id. A deployment that needs dynamic registration of many endpoints, or authentication of the user side against a server with a user database, is outside what is described here.

The third is the operational surface of the disguise itself. The mechanism depends on a simulated target, a chosen client fingerprint and a clock tolerance window, and each of those is a thing that can be wrong. A client fingerprint that does not match the site being simulated, or clocks drifting past the window on a busy node, produces a broken tunnel. The documentation treats the clock issue as a matter of NTP discipline, which is a real operational requirement and not a detail.

The alternative worth comparing is a plain SOCKS5 or WireGuard tunnel with a well-known provider, where the client is a standard binary with a standard config file and the traffic is a standard protocol. The difference in approach is that reality makes the tunnel harder to fingerprint at the cost of requiring a specific client build, a simulated target and synchronised clocks, while a standard protocol is easier to reason about and easier to inspect. For a private path where the operator controls both ends, a standard VPN or SOCKS5 server is the more predictable choice, and reality is for the case where the link has to look like ordinary HTTPS to something watching the network.

Editorial conclusion

reality fits an operator who needs a self-hosted reverse tunnel to a machine behind a NAT, and who wants the connection to a server to be indistinguishable from traffic to a real HTTPS site rather than a proxy handshake. It is the wrong choice for a team that wants a general purpose reverse proxy, since this project is a single SOCKS5 path with three binaries. Before deploying it, read README-REALITY.md for the protocol details, keep the server, client and user side on NTP-synchronised clocks because a skew past the expire window breaks verification, and reissue the grsc and grsu files whenever you regenerate the server configuration, since the preshared keys must match.

Frequently asked questions

What are the three binaries in the reality project?

grss is the reverse SOCKS5 server that needs a machine with a public IP, grsc is the reverse SOCKS5 client that runs in the intranet you want to reach, and grsu is the user side that runs on the user's machine and provides a SOCKS5 service, by default on 127.0.0.1:61080.

How do I generate a server config and clients with reality?

Run grss gen with the simulated target address and the server listen address, for example grss gen www.qq.com:443 127.0.0.1:443. The generator writes config.json by default and creates three clients with different id file names, with the count settable through the -c option.

Why do the reality client and user side report verify failed?

The documented causes are a time difference between server and client or user side beyond the expire second window, which defaults to 30 and can be widened at generation time with -e, using outdated grsc and grsu files after the server config was regenerated, and a hijacked client network. Clock synchronisation over NTP is the recommended fix for the first.

How do I set the TLS client fingerprint in reality?

The generator takes a -f option accepting chrome, firefox, safari, ios, android, edge, 360 or qq, with chrome as the default. The project depends on refraction-networking/utls for this, which lets the client present a chosen TLS fingerprint.

What licence is reality released under?

The project is MIT licensed. The repository is not archived and the last push was on 2026-08-08, with v1.1.7 released the same day and v1.1.6 and v1.1.5 both on 2026-06-28.

Official sources

  1. howmp/reality on GitHub
  2. Issues
  3. License: MIT
  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/howmp-reality.svg)](https://hysenlabs.com/projects/howmp-reality)

Was this useful?