p1neappleXpress/OpenFlux

▲ 28 stars today★ 1,862⑂ 158

Network stack research tool. TCP tunnel with pluggable transports.

About p1neappleXpress/OpenFlux

p1neappleXpress/OpenFlux is an open-source project on GitHub, mainly written in Go. Network stack research tool. TCP tunnel with pluggable transports. It currently holds 1,862 stars and 158 forks with 75 open issues, and was last pushed on 2026-09-25 (repository created 2026-06-21).

Project Overview

Git Homed tracks it on the Today's Trending board, currently at rank #97 with 28 new stars today.

GitHub Repository Details

Repository p1neappleXpress/OpenFlux · default branch main · size 39653 KB · watchers 23 · source: GitHub REST API and repository README

README

OpenFlux

English | Русский

Network stack research tool. IPv4 TCP/UDP tunnel with pluggable transports, batched+zstd codec, and two exit-node backends (L3 raw forward / L4 gVisor proxy).

Disclaimer

The author of OpenFlux does not encourage the use of this project to bypass restrictions or violate the rules of any platform, and is not responsible for the final scenarios of how users apply this tool in real life or on the Internet. Any specific technical features of the application are nothing more than an architectural coincidence, created without any intent.

The project is entirely non-commercial, contains no paid features, hidden subscriptions, or commercial benefit.

The author is not responsible for forks, modifications, or derivative versions of OpenFlux created by third parties. Any changes added to a fork are the responsibility of its author.

The author is not responsible for:

The original code is provided as is, without any warranties.

Clients

| Platform | Download | Notes | |----------|----------|-------| | macOS | build from source | CLI + utun L3 client (--inbound=tun, default on macOS) | | Linux | build from source | CLI client (SOCKS5) / exit node (L3 or L4) | | Windows | build from source | CLI client (SOCKS5) / exit node (l4, or l3 via QEMU - see TODO) | | Android | OpenFluxAndroid releases | Standalone APK | | Android | OpenFlux-Android releases | Fork: system-wide VPN or SOCKS5 proxy, multi-transport sessions, captcha handling, phone as exit node | | iOS | TestFlight beta | System-wide VPN via Network Extension |

iOS app built by @saharev1 - full iOS client,
TestFlight pipeline, system VPN support, DNS-over-TLS, and many stability fixes.
HUGE thanks!
> OpenFlux-Android built by @damnurmum - an
Android client with a system-wide VPN and a local SOCKS5 proxy mode, connection
profiles, multi-transport sessions with failover (direct included), SmartCaptcha
and login handling in a WebView (the exit node's too, passed through the tunnel
from its address), the phone as an l4 exit node, Kill Switch and per-app and
per-domain routing. Also contributed end-to-end encryption (#38), the Mail.ru
transport (#60) and session resilience with exit captcha handling (#93) to this
repository. HUGE thanks!
> Android app - p1neappleXpress/OpenFluxAndroid.

Architecture

Any client works with either exit backend. --mode is chosen on the exit node, not on the client.

Client (any):  macOS (utun) / Linux / Windows / iOS (packet tunnel) / Android
                    |
                    v
               Transport (Yandex.Docs / Volga / Board / MAX / Cups / Mail.ru / Direct)
                    |
                    v
               Exit node  -->  Internet
                 --mode l3   (raw SNAT/DNAT, Linux + root)
                 --mode l4   (gVisor proxy, any platform)

| Client (any) | Exit backend | Requires | |-----------------------------------------|--------------|-----------------------| | macOS / Linux / Windows / iOS / Android | --mode l3 | exit on Linux + root | | macOS / Linux / Windows / iOS / Android | --mode l4 | nothing |

In l3, the exit node terminates nothing: it forwards raw TCP and UDP packets with SNAT/DNAT (conntrack + egress-IP filter). TCP remains end-to-end between the client and the real server.

In l4, the exit node terminates TCP/UDP in a userspace gVisor stack, then re-dials the real server. Works on any OS, no root.

The client terminates TCP locally (gVisor, utun, or NEPacketTunnelProvider), then sends raw IP packets into the transport. In a multi-transport session several transports run at once and traffic fails over between them (see Multi-transport sessions).

Exit-node backends

The exit node has exactly two backends, selected with --mode on the exit node. The client does not choose a backend - the same client works against either.

| --mode | Backend | Forwarding | Requires | Platforms | |----------|---------|-----------|----------|-----------| | l3 | Raw L3 | SNAT/DNAT on raw IPv4 via SOCK_RAW + conntrack. No userspace TCP stack. | root / CAP_NET_RAW | Linux only | | l4 (alias proxy) | gVisor proxy | Terminates TCP/UDP in a userspace gVisor stack, then dials the real server. | nothing | Linux, macOS, Windows |

l4 is the canonical name going forward. but Linux-only and needs root. (client -> gVisor on exit -> real server). inside a lightweight QEMU VM (see TODO) - the WinDivert backend is not wired yet, and l4 is the working fallback until QEMU is shipped. On non-root hosts, use l4.

l3 and kernel RSTs

In l3 mode the kernel sees return packets for connections it never opened and emits RSTs, tearing the tunnel connections down. Drop them:

# Scoped (recommended): assign a dedicated egress IP, run with --local-ip, then:
sudo iptables -A OUTPUT -p tcp --tcp-flags RST RST -s  -j DROP

Host-wide fallback (drops ALL outbound RST; makes closed ports look filtered):

sudo iptables -A OUTPUT -p tcp --tcp-flags RST RST -j DROP

Client-originated RSTs are forwarded normally. The rule above is only for RSTs generated locally by the exit-node kernel.

Highlights

Yandex Board (WS), MAX/OneMe (WebRTC DataChannel), Cups.online (Centrifugo rooms), Mail.ru Docs (WS), Direct (plain TCP to the exit, sessions only). transport message. Fewer channel messages, higher throughput. See transport/batched.go and transport/framing.go. coverage. Linux raw L3 UDP remains experimental; see the limitations below. fresh session challenges, packet limits and replay checks. A restarted client or exit is accepted again after proving a fresh challenge, so the other side keeps running. The old unauthenticated wire-v3 startup option is retired. Legacy mode is unchanged. [Transport] sections in a .conf) runs every transport at once. Traffic uses the highest-priority transport that actually reaches the peer and fails over when it stops. See Multi-transport sessions. automatically. SmartCaptcha and login walls go to the app over IPC; the ones the exit hits are relayed to the client over any working transport, together with a local proxy that lets the app pass them from the exit's own address. See Captchas. See Exit-node backends. interface, watches its own sockets to install bypass routes, then takes the default route. No SOCKS5, no gVisor on the client. (compatible with older clients). AES-256-GCM. Both peers must share the secret. measure raw goodput through the transport without touching the host network.

Requirements

1. Go - to build the desktop client / exit-node binary. See go.mod for the exact version. 2. Android NDK r27+ - to build the Android client binary. 3. Xcode 26.6+ - to build the iOS client binary. 4. A Linux VPS / VDS for the exit node. The l3 backend requires root; l4 works without.

Structure

OpenFlux/
  main.go                          # CLI entry (client / exit / benches)
  conf.go                          # .conf parser
  transport_spec.go                # --transports parsing, session bootstrap
  transport_factory.go             # Builds a transport from its type
  ipc_handler.go                   # IPC: cookies from the app
  auth_proxy.go                    # Local HTTP proxy for the exit's checks
  share_cli.go                     # --share: link and QR code for clients
  share/                           # openflux:// links and QR codes
  bench.go                         # Benchmark helpers
  tun_darwin.go                    # macOS utun L3 client
  tun_watch.go                     # Socket watcher for bypass routes
  tun_learn.go, tun_other.go       # utun helpers / non-darwin stubs
  signals_{unix,windows}.go        # Shutdown signals
  export_ios.go                    # cgo bridge for the iOS static library
  export_ios_packet.go             # iOS packet tunnel bridge
  transport/
    transport.go                   # Transport interface
    batched.go                     # BatchedTransport (coalescing + zstd)
    framing.go                     # Wire framing for batched frames
    compressor.go                  # Legacy per-packet LZ4 codec
    encrypted.go                   # Optional AES-256-GCM wrapper
    session.go                     # Negotiated multi-transport session
    session_add_after_start.go     # Adding a transport to a running session
    direct.go                      # Direct TCP transport
    portdemux.go                   # Splits replies between two client stacks
    cookies.go, cookiestore.go     # Cookie exchange and persistence
    error_notifier.go              # Out-of-band errors (captcha, login)
    control/                       # Envelope and control messages
    manager/                       # Transports, cookies and checks per session
    ipc/                           # App <-> core IPC over a Unix socket
    yandex/                        # Yandex.Docs, Volga, Board, captcha solver
    oneme/                         # MAX Messenger backend
    cupsonline/                    # Cups.online backend
    mailru/                        # Mail.ru Docs backend
  tunnel/
    tunnel.go                      # Client tunnel (gVisor + TunnelLinkEndpoint)
    endpoint.go                    # Virtual NIC (client)
    packettunnel.go                # Packet tunnel (iOS)
    httpproxy.go                   # HTTP proxy over a tunnel stack
    exit.go                        # NewExitNode dispatcher (l3 / l4)
    proxy_exit.go                  # L4 exit (gVisor + net.Dial)
    l3/
      l3.go                        # L3Exit: SNAT/DNAT, conntrack, egress filter
      backend.go                   # L3Backend interface
      backend_linux.go             # SOCK_RAW backend (Linux)
      backend_windows.go           # Stub (WinDivert not wired yet)
      backend_other.go             # Unsupported-platform stub
      conntrack.go                 # Conntrack table
      flow.go                      # Flow keys, SNAT/DNAT, checksums
      udp_nat.go                   # UDP NAT
      icmp.go                      # ICMP errors and MTU feedback
      reassembly.go                # IPv4 fragment reassembly
    windivert/                     # WinDivert backend (present, not wired to L3 yet)
  socks5/                          # SOCKS5 server (client fallback)
  network/                         # Checksums, packet parsing
  utils/                           # Logging
  ios-app/                         # SwiftUI iOS client (XcodeGen)
  build_all.sh                     # Cross-build release binaries
  build_ios.sh                     # Build iOS static library (liboflux.a)
  build_ios_app.sh                 # Build + archive + export iOS app IPA
  build_android.sh                 # Build Android client binary
  scripts/
    cleanup-utun.sh                # Remove leftover utun routes (macOS)

Build

go mod tidy
go build -o openflux .

Cross-build for the exit node (Linux amd64), stripped:

CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
    go build -ldflags="-s -w" -trimpath -o openflux-linux .

Usage

Exit node - L3 (Linux, root)

sudo ./openflux --role=exit --mode=l3 \
    --transport=yandex \
    --url="YOUR_YANDEX_DOC_URL"

Requires root / CAP_NET_RAW. Install the iptables rule (see l3 and kernel RSTs).

Exit node - L4 (any OS, no root)

./openflux --role=exit --mode=l4 \
    --transport=yandex \
    --url="YOUR_YANDEX_DOC_URL"

Fallback for platforms where l3 is unavailable (Windows without WinDivert, macOS, non-root Linux). Slower than l3 (double TCP termination).

Client - macOS utun (default on macOS)

sudo ./openflux --role=client --inbound=tun \
    --transport=yandex \
    --url="YOUR_YANDEX_DOC_URL"

Creates a utun interface, installs bypass routes for the transport, waits for the transport to connect, then takes the default route. No SOCKS5. Requires sudo. All traffic except the transport goes through the tunnel.

Client - SOCKS5 (all platforms, fallback)

./openflux --role=client --inbound=socks5 \
    --transport=yandex \
    --url="YOUR_YANDEX_DOC_URL" \
    --socks5=:1080

Point your browser / app at 127.0.0.1:1080 as a SOCKS5 proxy. This is the default inbound on non-macOS platforms. UDP-capable applications may use the SOCKS5 UDP ASSOCIATE command.

UDP limitations

datagrams, 128 fragments per datagram and a 4 MiB byte budget per direction. Overlaps and malformed fragments are discarded; expiry is swept on input. restoring the quoted client address/port and checksums. Redirects and echo traffic are not relayed. Egress EMSGSIZE produces ICMP fragmentation-needed with the kernel route MTU; non-DF packets can instead be fragmented. Outgoing fragmentation of IPv4 headers containing options is not supported. filter ICMP can still black-hole large DF packets; real-network tests remain necessary. The negotiated packet ceiling is distinct from the Internet MTU. using a real UDP socket and restores the client's port on return. This avoids taking ports owned by host applications and is intended to prevent kernel ICMP port-unreachable without firewall changes. There are at most 256 mappings; idle expiry is 2 minutes (15 seconds for DNS). Source-port preservation and endpoint-independent NAT/hole-punching are not provided. loopback inside a disposable network namespace, including host-port conflicts and false ICMP port-unreachable responses; it is not an Internet/PMTU canary. TCP's existing raw-port ownership and RST-suppression requirements are unchanged. calls OpenFluxTunSetUDPEnabled(1) for a known UDP-capable exit. Reset it to 0 when switching to an older exit. Physical-device QUIC is not validated. them, but packet loss in the carrier can still cause head-of-line blocking; this is not equivalent to a native datagram transport.

Codec selection

By default the transport uses the batched + zstd codec (transport/batched.go + transport/framing.go). For the old per-packet LZ4 codec, pass --codec=legacy:

./openflux --role=client --codec=legacy ...

Important: batched and legacy LZ4 codecs remain incompatible. Default batched mode remains v2. The old OPENFLUX_EXPERIMENTAL_WIRE_V3=1 prototype now fails startup rather than accepting unauthenticated capability messages.

Authenticated capability negotiation (opt-in CLI)

Add these options on both updated peers, using the same secret and codec:

--codec=batched --encryption-key-file=/path/to/secret.txt --negotiate

The handshake runs inside AES-GCM and confirms fresh random challenges, peer roles, IPv4/TCP/UDP support, ICMP-error support and maximum IPv4 packet size. L4 does not advertise raw ICMP forwarding. Only the intersection of capabilities is enabled. Data carries both session IDs and a sequence number; a 64-packet sliding replay window tolerates bounded reordering. The old batch-v2 envelope and encryption key derivation are unchanged; this is not forward secrecy or a replacement for a future key-exchange/rekey design.

Negotiated mode never falls back to unencrypted or legacy peers. The client gives up after 20 seconds if negotiation cannot complete (wrong key, incompatible codec, missing option, or unavailable peer); the exit waits for a client indefinitely. --max-packet-size=1280..65000 caps the complete IPv4 packet; the default is 65000, leaving room for authenticated envelopes. The agreed limit is used by the gVisor link; the macOS TUN remains 1280. Raw-exit replies exceeding the agreed limit are fragmented without DF, or produce ICMP feedback to the Internet sender with DF.

Carrier reconnects keep the session. A restarted peer is accepted again without restarting the other one: a hello from an unknown sender gets a challenge minted for it alone, and the session is replaced only once that challenge is echoed, so old traffic replayed from a carrier (anyone with access to a document sees the ciphertext) cannot displace it. An exit therefore serves one active client at a time. A client whose exit went silent on every transport starts a new handshake on its own, within about a minute. Existing iOS builds have no negotiation setting and must use an exit without --negotiate. Their UDP switch remains manual. No claim of device-level QUIC validation is made.

Multi-transport sessions

Run several transports in one negotiated session, for example a direct TCP connection to the exit plus a Yandex document as the fallback:

# Exit: direct listener on :8445 plus the document
./openflux --role=exit --mode=l3 --negotiate \
    --transports=direct:100,yandex:50 --direct-listen=0.0.0.0:8445 \
    --encryption-key-file=secret.txt --url="YOUR_YANDEX_DOC_URL"

Client

./openflux --role=client --inbound=socks5 --negotiate \ --transports=direct:100,yandex:50 --direct-dial=EXIT_IP:8445 \ --encryption-key-file=secret.txt --url="YOUR_YANDEX_DOC_URL"
captcha) is retried in the background with backoff. that reaches the peer. Transports with equal priority share flows. are pinged), not merely while it is attached to its document. Peers that predate this keep the old behavior. --yandex-url, --vyandex-url, --boards-url, --mailru-url, --cupsonline-url; MAX takes --oneme-token / --oneme-uid. --url is used for yandex when --yandex-url is empty. firewall); it is only available in a session.

The same setup as a .conf file (./openflux --config=client.conf; flags on the command line override the file):

[Interface]
Role = client
Inbound = socks5
EncryptionKeyFile = secret.txt
URL = YOUR_YANDEX_DOC_URL

[Transport "direct"] Priority = 100 Dial = EXIT_IP:8445

[Transport "yandex"] Priority = 50 URL = YOUR_YANDEX_DOC_URL

[Interface] keys: Role, Inbound, Transport, Mode, Codec, Socks5, EncryptionKeyFile, CookieStore, IPCSocket, URL, Debug. Transport sections take Type (defaults to the section name), Priority (default 50), URL, Dial / Listen (direct) and Token / UID (MAX). A .conf with transport sections always runs as a negotiated session.

Sharing an exit with a QR code

Start the exit with --share to print an openflux:// link and its QR code (in the terminal or the service log). A client scans it, or opens the link, and gets the exit's transports, priorities, session mode, key and encryption context, with direct pointing at the exit:

./openflux --role=exit --mode=l3 --negotiate \
    --transports=direct:100,yandex:50 --direct-listen=0.0.0.0:8445 \
    --encryption-key-file=secret.txt --url="YOUR_YANDEX_DOC_URL" \
    --share --share-host=EXIT_PUBLIC_IP
key file. first public IPv4 of the host. when its rooms are created at startup. PNG, Bitmap, Terminal), for apps to use as well.

Captchas

itself, nothing to do. --ipc-socket=PATH the core asks the app (CookiesRequest), the app opens the page in a browser view and answers with the cookies (CookiesOffer); the transport applies them and reconnects. message over any transport that still works (for example direct while the document is the one stuck). The client passes it to the app as a CookiesRequest with remote: true and proxy: a local HTTP proxy whose connections leave through the tunnel and the exit, so the check is passed from the exit's address. The app answers with remote: true and the exit applies the cookies. The proxy's TCP stack shares the tunnel address and uses local ports 12000-12999. straight through to the document (the captcha targets the transport's HTTP client), so loading the page and sending its cookies is typically enough. ./cookies-.json) and reused after restarts. Under systemd with ProtectSystem=strict, point it at a writable directory.

Wire details: PROTOCOL_NEGOTIATION.md.

Encryption (optional)

./openflux ... --encryption-key-file=/path/to/secret.txt

Both peers must use the same secret file. AES-256-GCM, directional keys. Unset means unencrypted, unchanged behavior.

Benchmarks

Measure raw goodput through the transport, without touching the host network:

```

Sender: push 100 MB

./openflux --role=bench-send --bench-bytes=100 --transport=yandex

GitHub Stars & Activity

1,862Stars
158Forks
75Open issues
GoLanguage

GitHub Popularity

GitHub stars1,862
Forks158
Open issues75
Primary languageGo
LicenseGPL-3.0
Stars gained today28
Created2026-06-21
Last pushed2026-09-25

Trending History

Daily boardrank #97 · ▲ 28 stars

Related GitHub Projects

1

golang / go

Go★ 139,033⑂ 20,563▲ 44 stars
→
2

kubernetes / kubernetes

Go★ 128,026⑂ 45,310▲ 41 stars
→
3

prometheus / prometheus

Go★ 66,254⑂ 10,863▲ 28 stars
→
4

MHSanaei / 3x-ui

Go★ 46,994⑂ 11,863▲ 48 stars
→
5

grafana / k6

Go★ 31,635⑂ 1,645▲ 33 stars
→
6

gastownhall / beads

Go★ 27,449⑂ 1,859▲ 30 stars
→
7

putyy / res-downloader

Go★ 20,206⑂ 2,511▲ 152 stars
→
8

openbao / openbao

Go★ 7,959⑂ 590▲ 360 stars
→

More Trending Repositories