Open-source project
NodePassProject/Nowhere avatar
NodePassProject/Nowhere

Nowhere: a Rust relay that splits uplink and downlink across TLS/TCP and QUIC/UDP

Split-direction TLS / QUIC relay in Rust

453 stars34 forksRustGPL-3.0

At a glance

What is it?
NodePassProject/Nowhere puts a SOCKS5 Vector in front of a Portal that authenticates carriers, then lets each flow pick its uplink and downlink transport independently. It is a small, opinionated relay for people who already know why they want two carriers.
Who is it for?
Adopt Nowhere if you want a single Rust binary that terminates SOCKS5 locally, authenticates carriers at a Portal, and lets you assign TLS/TCP and QUIC/UDP per direction, including a native next hop chain. Do not adopt it if you need a mature, widely deployed proxy with a long operational track record, or if GPL-3.0-only does not fit how you ship.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What Nowhere solves, and who it is actually for

Most relays pick one carrier and stay on it. Nowhere's premise is that the two directions of a flow do not have to travel the same way. The README states it plainly: "One relay. Two carriers. Independent directions." A flow that starts as a SOCKS5 CONNECT at the local Vector can leave over TLS/TCP and come back over QUIC/UDP, or the reverse, and the choice is made per flow rather than per process.

That matters to a narrow audience. If you run a single relay hop and your only concern is that traffic gets through, a conventional SOCKS5 proxy is simpler. Nowhere is aimed at people who already have a reason to separate the carriers: a network where one transport is throttled or unreliable in one direction, an environment where UDP is treated differently from TCP, or a deployment where the next hop is another Nowhere instance rather than a generic proxy. The README also advertises TCP and UDP support through SOCKS5 CONNECT and UDP ASSOCIATE, so it is not limited to stream traffic.

The project is written in Rust, licensed GPL-3.0-only according to Cargo.toml, and the last push to the default branch was on 2026-09-13. The most recent release listed is v2.0.0 on 2026-09-11. It is not archived.

Vector, Portal, and the split-direction policy table

The architecture is two roles plus an optional third. Vector accepts local SOCKS5 traffic. Portal authenticates carriers and reaches the target. The README's diagram shows the uplink carrier and downlink carrier as separate arrows between Vector and Entry Portal, and then a native next uplink and next downlink between Entry Portal and an optional Next Portal. Both Portal hops can forward either directly or through a SOCKS5 proxy.

The mechanism that makes the split concrete is the up and down parameters, which accept tcp, udp, or mix. The README gives a full table of the nine combinations. tcp/tcp means TLS/TCP both ways; udp/udp means QUIC/UDP both ways; mix on either side means the flow makes one 50/50 choice and may try the alternate route once before committing. Setting mux=1 enables TLS multiplexing. Portal's next= parameter applies the same policy independently on each hop, which is the part that makes chaining interesting: hop one can be UDP while hop two is TCP.

Two constraints are worth noting. First, the wildcard address is reserved for Portal listeners; Vector and next require a concrete address or hostname, so you cannot write a wildcard endpoint on the client side. Second, next is lazy, mutually exclusive with outbound socks, and bounded to seven hops. That bound is a real design decision, not a suggestion: the README says bounded to seven hops, and there is no documented way to raise it.

The wire path: carriers authenticate, flows route

The README separates two layers. Authentication belongs to each physical carrier; routing belongs to each logical flow. Carrier bootstrap uses a 32-byte AuthFrame. After bootstrap, a logical flow begins with a 5-byte FlowHeader, an optional variable-length target, and then payload once Portal returns READY.

On the carrier side, TLS uses either a dedicated lane or Mux, while QUIC uses the first stream only. On the flow side, TCP is a reliable byte stream and UDP is either UoT or a QUIC DATAGRAM payload. The README notes that frames are compact, that DATA payload queues are bounded by byte credit, and that hot-path buffers are reused. Those are implementation choices visible in the description rather than benchmark results, and the project does not publish throughput figures in the README.

Morph is the other piece of the wire path. With morph=1, a transform derived from the shared key masks the TLS/QUIC wire image. Over TCP the shape is a 12-byte nonce followed by ChaCha20-XOR of the TLS stream in each direction; over UDP each datagram carries a 12-byte nonce followed by ChaCha20-XOR of the QUIC datagram. Both endpoints on a hop must enable it. The README is explicit that this is wire masking with no protocol camouflage or added security semantics, which is an unusually honest framing and worth taking at face value: do not treat morph as a security feature.

Installing Nowhere and running a first relay

The README's quick start assumes a stable Rust toolchain on a supported target. Build with the locked flag so the committed Cargo.lock is respected:

bash
cargo build --release --locked

The binary lands at target/release/nowhere. Start a Portal listening on TLS/TCP and QUIC/UDP at port 2000. The key in this example is the literal placeholder change-me, which you should replace:

bash
./target/release/nowhere 'portal://[email protected]:2000'

Then start a Vector in a second terminal. This exposes SOCKS5 on 127.0.0.1:1080 and pins both directions to TCP:

bash
./target/release/nowhere \
  'vector://[email protected]:2000?up=tcp&down=tcp&socks=127.0.0.1:1080'

Point a SOCKS5 client at 127.0.0.1:1080 and traffic should traverse the Portal. To watch what is happening, the README offers a read-only TUI that discovers local Portal and Vector instances:

bash
./target/release/nowhere tui

The TUI presents traffic, carrier, process, and log data and does not control the lifecycle of the instances it finds. There is also a Dockerfile in the repository root that builds with rust:alpine and produces a scratch image with the binary at /nowhere and the CA bundle at /etc/ssl/certs/ca-certificates.crt, with ENTRYPOINT ["/nowhere"]. The README does not document a published container image, so treat the Dockerfile as something you build yourself.

The certificate trap in the local examples

This is the limitation to read before anything else. The README says the local examples disable certificate verification by omitting sni. That is convenient for a loopback test and wrong for anything reachable from a network you do not control. The public deployment section shows what changes: the Portal takes tls=2 along with crt and key paths, and the Vector takes sni set to the relay hostname.

bash
nowhere 'portal://change-me@:2000?tls=2&crt=/etc/nowhere/cert.pem&key=/etc/nowhere/key.pem'
nowhere 'vector://[email protected]:2000?sni=relay.example&socks=127.0.0.1:1080'

Certificate pinning is also available according to the README. The README does not spell out the pinning syntax in the text available; it defers to docs/security.md and docs/configuration.md. That is a documentation gap worth closing before you rely on pinning in production.

The other honest limitation is the endpoint grammar itself. The compact form @*:2000 puts both carriers on one port, and the explicit forms assign carriers, ports, and address families (for example @*/tcp4:2006/udp6:2017). The README states that the full grammar is documented in docs/configuration.md, which means the README alone is not enough to configure a non-trivial deployment. If you cannot read that document, you cannot safely guess at a mixed-family endpoint.

Where Nowhere is the wrong tool, and what to use instead

Nowhere is the wrong tool when you want a general-purpose proxy with a large operational base and years of deployment reports. It is a young project: the release history in the repository runs from v1.8.2 in late August 2026 to v2.0.0 in mid September 2026, and the major version bump means the configuration surface may still be moving. If your requirement is "a SOCKS5 proxy that many people already run in production," a long-established proxy such as Dante or a mature TLS tunnel is a better fit, and the difference is not cosmetic: those projects do not give you per-direction carrier selection at all.

The real alternative in approach is a conventional single-carrier tunnel. A standard TLS tunnel carries both directions of a flow over the same connection, so there is one failure domain, one set of tuning parameters, and one thing to reason about when it misbehaves. Nowhere trades that simplicity for the ability to send the uplink over TLS/TCP and the downlink over QUIC/UDP, and to chain that decision across up to seven Portal hops. If you do not have a concrete reason to separate the directions, you are paying configuration complexity for a capability you will not use.

There is a second case where Nowhere is the wrong choice: if morph is the reason you are interested. The README states it is wire masking with no protocol camouflage or added security semantics, so it does not turn the relay into a censorship-resistant transport in the way the name might suggest to a casual reader.

Licence and upgrade cost

Cargo.toml declares license = "GPL-3.0-only", and the repository carries a LICENSE file at the top level. GPL-3.0-only is a copyleft licence, which for a network-facing binary has consequences that depend entirely on how you distribute and combine it. If you link Nowhere into a product you ship, or modify and distribute it, the obligations differ from a permissive licence. This is not legal advice, and the licence text is the authority; the practical point is that GPL-3.0-only is a deliberate choice by the project, not an accident, and it should be checked against your distribution model before you build on it.

Upgrade cost is dominated by the version jump. The release list shows v1.8.3 and v1.8.2 in the 1.8 line and then v2.0.0, with the README describing the endpoint grammar as living in docs/configuration.md rather than being fully reproduced in the main file. Any upgrade between major versions should start by diffing that configuration document, because the service URL grammar is the surface most likely to change. The Cargo.lock is committed and the build instructions use --locked, so dependency drift is at least pinned at build time.

Editorial conclusion

Adopt Nowhere if you want a single Rust binary that terminates SOCKS5 locally, authenticates carriers at a Portal, and lets you assign TLS/TCP and QUIC/UDP per direction, including a native next hop chain. Do not adopt it if you need a mature, widely deployed proxy with a long operational track record, or if GPL-3.0-only does not fit how you ship. Before deploying, verify the certificate story: the local examples omit sni and therefore disable certificate verification, and the README points to docs/security.md and docs/configuration.md for pinning and the full endpoint grammar.

Frequently asked questions

What is Nowhere?

It is a cross-platform relay written in Rust that composes TLS/TCP and QUIC/UDP independently for every flow. A Vector accepts local SOCKS5 traffic and a Portal authenticates carriers and reaches the target.

How do you install and start Nowhere?

Build with a stable Rust toolchain using cargo build --release --locked, then run the resulting binary with a portal:// or vector:// service URL. The README's quick start starts a Portal at port 2000 and a Vector exposing SOCKS5 on 127.0.0.1:1080.

Does Nowhere support UDP as well as TCP?

Yes. The README states that SOCKS5 CONNECT and UDP ASSOCIATE are both supported, and the up and down parameters accept tcp, udp, or mix to choose the carrier for each direction.

What does the morph option in Nowhere do?

With morph=1, a transform derived from the shared key masks the TLS/QUIC wire image, using a 12-byte nonce and ChaCha20-XOR over the stream or datagram. The README states it is wire masking with no protocol camouflage or added security semantics, and both endpoints on a hop must enable it.

Is Nowhere free to use in my own product?

Cargo.toml declares license = "GPL-3.0-only", so the terms are copyleft rather than permissive. Whether that fits your distribution model depends on how you ship or modify it, and the LICENSE file is the authority.

Official sources

  1. Issues
  2. License: GPL-3.0
  3. NodePassProject/Nowhere on GitHub
  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/nodepassproject-nowhere.svg)](https://hysenlabs.com/projects/nodepassproject-nowhere)