OpenFlux: a TCP tunnel with pluggable transports and two exit-node backends
Network stack research tool. TCP tunnel with pluggable transports.
At a glance
- What is it?
- OpenFlux is a Go network stack research tool that carries raw IP packets over public document and chat services, with an L3 raw-forward or L4 gVisor exit node. It is early software at version 0.0.3, and the README is more explicit about its limits than most.
- Who is it for?
- Adopt OpenFlux if you are researching how tunneled traffic behaves over WebSocket and WebRTC transports and you can read Go, run a Linux exit node, and tolerate a 0.0.x API. Do not adopt it if you need a supported product with documented rollback, a Windows exit node today, or a stable configuration format across releases.
- 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 2 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 17, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
What OpenFlux actually does, and who is meant to run it
OpenFlux moves IP packets between a client and an exit node over a channel that is not an ordinary socket. The README describes it as a "Network stack research tool" and lists five transports: Yandex.Docs over WebSocket, Yandex Volga (an HTTP relay plus WebSocket), MAX/OneMe over a WebRTC DataChannel, Cups.online Centrifugo rooms, and Mail.ru Docs over WebSocket. The exit node then forwards traffic to the real internet.
The intended audience is narrow. This is a Go repository with a CLI entry point in main.go, separate files for a macOS utun client (tun_darwin.go, tun_watch.go, tun_learn.go), and platform-specific signal handling. Building it means reading go.mod, which pins Go 1.26.4. There is no package registry install and no prebuilt desktop binary: the client table says "build from source" for macOS, Linux and Windows. Android ships as a separate APK repository, and iOS through a TestFlight beta built by a contributor.
So the tool is for engineers who want to study how a tunnel behaves when its carrier is a document editor or a call signalling channel, and who are willing to compile it. It is not positioned as a consumer VPN, and the README's disclaimer says the project is non-commercial with no paid features.
Client, transport, exit node: the three-part path
The architecture section shows one pipeline. Any client works with either exit backend, and the backend is chosen on the exit node with --mode, not on the client. The client terminates TCP locally (gVisor, utun, or NEPacketTunnelProvider on iOS), then sends raw IP packets into the transport. The transport carries them. The exit node forwards them to the internet.
The two backends differ in where TCP ends. In l3, the exit node terminates nothing: it forwards raw IPv4 with SNAT/DNAT using SOCK_RAW plus conntrack and an egress-IP filter, so there is a single TCP connection end to end between client and real server. It requires root or CAP_NET_RAW and is Linux only. In l4, the exit node terminates TCP inside a userspace gVisor stack and re-dials the real server with net.Dial. That works on Linux, macOS and Windows without root, at the cost of terminating TCP twice. proxy is a deprecated alias for l4.
Two details matter more than the table. First, the l3 path has a known kernel interaction: the kernel sees return packets for connections it never opened and emits RSTs that tear the tunnel down. The README gives an iptables rule to drop them, scoped to a dedicated egress IP where possible. Second, the codec layer is not just framing. transport/batched.go and transport/framing.go coalesce many tunnel packets into one transport message with zstd, and --codec=legacy reverts to a per-packet LZ4 codec for compatibility with older clients. That compatibility switch is the clearest sign the wire format has already changed once before 1.0.
Running the client and exit node with docker compose
The repository ships a Dockerfile and docker-compose.yml, and compose is the shortest path to a working pair. The compose file defines two independent services, client and exit-node, behind profiles, and the header comment says to start only the end you need. Configuration comes from .env or inline environment variables.
Start by copying the example environment file. TRANSPORT selects the channel, and for the Yandex transports both sides must share the same public document URL. For the oneme transport, the client needs MAX_TOKEN and the other side's user id in MAX_UID.
cp .env.example .env
# edit .env: set TRANSPORT and DOC_URL (or MAX_TOKEN / MAX_UID)Then bring up the client. The compose file publishes SOCKS5 on 127.0.0.1:1080 only, and the comment is explicit that there is no auth on that server, so it should stay on the host loopback.
docker compose --profile client up -d --buildOn the exit host, start the other profile. The compose file grants the exit-node container NET_RAW and NET_ADMIN and confines the RST-drop rule to that container's network namespace, so the rule cannot touch the host.
docker compose --profile exit-node up -d --buildThe README also lists benchmark roles that measure goodput through the transport without touching the host network, which is the cleanest way to check a transport before pointing real traffic at it. It does not document how to stop or roll back a running tunnel beyond the usual compose down, and it does not document a version compatibility matrix between client and exit node.
Where OpenFlux is the wrong tool
The l3 backend is Linux only and needs root. If your exit host is a Windows box, the README says the intended path is running l3 inside a lightweight QEMU VM, and it marks that as a TODO: the WinDivert backend is not wired yet, and l4 is the working fallback until QEMU ships. That is a real gap, not a configuration detail. Anyone planning a Windows exit node today should plan on l4.
The l3 RST problem is the second limitation. The host-wide fallback rule drops all outbound RSTs, which the README itself notes makes closed ports look filtered. That changes how the machine behaves for every other process on it, which is why the scoped rule with a dedicated egress IP is the recommended one. If you cannot dedicate an egress IP, l3 is a poor fit.
The third is maturity. The released versions are 0.0.1, 0.0.2 and 0.0.3, all within the same week, and the presence of --codec=legacy shows the framing has already been revised in a way that splits old and new clients. A tool at this stage is a research instrument, not something to build a service contract on. And the transports themselves are the load-bearing assumption: every one of them is a third-party service the project does not control, so a change on that side is a failure mode with no fix inside this repository.
How it compares with WireGuard and sing-box
WireGuard is the obvious point of comparison, and the difference is in the carrier. WireGuard is a kernel and userspace implementation of a specific UDP protocol; it assumes a network path where UDP is allowed and the peer is reachable. OpenFlux assumes the opposite: it carries the tunnel inside a WebSocket or WebRTC DataChannel belonging to a service that is normally reachable, which is why the transport list is the headline feature rather than the crypto. WireGuard's cryptography is fixed and audited; OpenFlux's encryption is optional, enabled with --encryption-key-file to wrap the transport in AES-256-GCM, and both peers must share the secret. If you run without that flag, the README does not claim the transport provides confidentiality.
sing-box is closer in spirit: a large Go project with a plugin model for inbound and outbound protocols. The difference in approach is that sing-box composes documented, versioned protocols and expects you to pick from them, while OpenFlux composes transports that are not protocols at all. A Yandex document is not a transport specification. That is the research value and also the maintenance risk, because the upstream service can change without notice.
Against both, OpenFlux's exit-side split is unusual. l4 terminating TCP in a userspace gVisor stack is a design you see in sandboxed proxies; l3 raw SNAT/DNAT with conntrack is a design you see in low-level forwarding. Making both available behind one --mode flag on the exit node, with the same client on either, is the part of this project worth studying.
Licence, maintenance and what an upgrade costs
OpenFlux is GPL-3.0, and the repository carries LICENSE, COPYRIGHT and NOTICE files at the top level. The practical implication is the usual copyleft one: if you distribute a modified binary, the source for your version has to be available under the same terms. The README also states the author is not responsible for forks or derivative versions and that changes added to a fork are the responsibility of its author. That is a statement about support expectations, not a licence term, and it does not change what GPL-3.0 requires. None of this is legal advice; if you plan to ship OpenFlux inside a product, have someone qualified read the licence.
On maintenance, the last push was on 2026-09-16, and the three releases 0.0.1, 0.0.2 and 0.0.3 landed on 2026-09-10, 2026-09-12 and 2026-09-16. The repository is not archived. That is a fast early cadence on a pre-1.0 project, which tells you the code is moving but not that any interface is settled.
Upgrade cost is concentrated in two places. The wire format: --codec=legacy exists precisely because a codec change broke compatibility with older clients, so assume a client and exit node from different releases may not interoperate without that flag. The configuration surface: compose reads ROLE, TRANSPORT, URL, MAX_TOKEN, MAX_UID, SOCKS5_LISTEN, LOCAL_IP and DEBUG from the environment, and .env.example documents each, but there is no stated stability guarantee for those names. Pin the image you build rather than tracking main if you run this over anything you care about.
Editorial conclusion
Adopt OpenFlux if you are researching how tunneled traffic behaves over WebSocket and WebRTC transports and you can read Go, run a Linux exit node, and tolerate a 0.0.x API. Do not adopt it if you need a supported product with documented rollback, a Windows exit node today, or a stable configuration format across releases. Before deploying, verify three things yourself: that the transport you intend to use still accepts the shared document URL or MAX credentials, that your exit host can run the iptables RST rule in l3 mode, and that GPL-3.0 fits how you plan to distribute anything built on top of it.
Frequently asked questions
How do I install OpenFlux?
There is no package registry install. The README says to build the desktop client and exit node from source with Go, using the version pinned in go.mod, or to use the provided Dockerfile and docker-compose.yml, which build an openflux image with client and exit-node profiles.
Does OpenFlux need root to run an exit node?
It depends on the mode. The l3 backend forwards raw IPv4 with SOCK_RAW and conntrack, so it requires root or CAP_NET_RAW and is Linux only. The l4 backend terminates TCP in a userspace gVisor stack and needs no special privileges, so it runs on Linux, macOS and Windows.
What is the difference between l3 and l4 in OpenFlux?
In l3 the exit node terminates nothing and forwards raw IP packets with SNAT/DNAT, giving one TCP connection end to end, which the README calls faster but Linux-only and root-only. In l4 the exit node terminates TCP in a gVisor stack and re-dials the server with net.Dial, which works everywhere without root at the cost of terminating TCP twice.
Why does OpenFlux drop outbound TCP RST packets?
In l3 mode the kernel sees return packets for connections it never opened and emits RSTs that tear the tunnel connections down. The README gives an iptables rule to drop them, scoped to a dedicated egress IP where possible, with a host-wide fallback that it notes makes closed ports look filtered.
Is OpenFlux encrypted?
Encryption is optional. The README lists --encryption-key-file, which wraps the transport in AES-256-GCM, and states that both peers must share the secret. It does not claim the transport provides confidentiality when that flag is not used.
Community notes