KKRainbow e313ba8efb fix(wasi): reduce startup RSS, align ABI v4 and wire v3 (#2588)
* feat(easytier-go): accept data plane ABI v4

The core raised DATA_PLANE_ABI_VERSION to 4 in f26c2aa1 ("feat(wasi):
run EasyTier core on Cloudflare Workers and browsers") to advertise the
new guest exports easytier_data_plane_tcp_shutdown_write_submit and
easytier_data_plane_tcp_shutdown_write_result_take, which let a host
half-close guest TCP streams.

The Go host has no caller for half-close: net.Conn exposes only Close,
which already tears down both directions, so the v3 behavior is
preserved and no new plumbing is added. Without this bump, any artifact
rebuilt from current core source is rejected at instance creation with
"unsupported EasyTier data plane ABI version 4, want 3".

* fix(easytier-go): decode socket options wire v3, rebuild core artifact

38e2a621 ("refactor(ohos): 拆分 OHRS 包并按 socket 精细保护 VPN 流量")
raised the host socket options wire format from version 2 to 3: a
need_protect byte is appended after the purpose byte in TCP connect,
UDP bind, and TCP listen options, shifting bind_device one byte later.
The Go hostABI decoders were never updated, so every socket operation
from a HEAD-built core was rejected as "invalid options" and required
listeners failed to start.

Bump the accepted wire version to 3 and skip the need_protect byte in
all three decoders. The byte requests VPN socket protection, which only
Android-style VPN hosts can honor; on every other platform sockets are
already protected, so reading and discarding it is correct.

Regenerate the embedded core artifact and protobuf bindings from HEAD
(599e4eac) so the shipped artifact matches the Go host again. The proto
regeneration also picks up schema fields added since the last embed
(e.g. prefer_peer_relay).

* perf(easytier-go): release compiler garbage after host init

wazero's optimizing compiler allocates ~100MB of throwaway state on
the Go heap while compiling the embedded core. Go's runtime does not
return that memory to the OS after the initiating GC, so the process
retained the compilation peak for its entire lifetime: RSS sat at
~164MB before any instance or network activity.

Call debug.FreeOSMemory() once after the module is instantiated.
NewHost is a one-time initialization path outside the dataplane, so
the stop-the-world pass is safe here. Measured RSS after host.New
drops from ~164MB to ~64MB; no behavioral change.

* chore(easytier-js): bump toolchain dependencies

- vitest 2.1.9 -> 3.2.7 (all three packages)
- esbuild 0.25.9 -> 0.28.2 (browser bundler)
- wrangler 4.114.0 -> 4.134.0 (cloudflare + web example)
- @cloudflare/workers-types 5.20260724.1 -> 5.20260917.1
- binaryen 131.0.0 -> 132.0.0 (JS bindings only; the wasm build
  uses the standalone wasm-opt binary fetched by the build script)

Not bumped: typescript stays at 5.9.3 (latest 5.x; 7.0 is a major
jump not worth taking for this workspace), vite stays at 5.4.21
(web-example only; 5->8 spans three majors).

Verified with pnpm test (28 + 2 + 2 tests across runtime, browser,
cloudflare) and pnpm check (tsc + wrangler deploy --dry-run).
pnpm-workspace.yaml gained three minimumReleaseAgeExclude entries
recorded automatically by pnpm for freshly published versions.

* fix(easytier-js): align host wire format with core wire v3

The core socket options wire format moved to version 3 in 38e2a621
(need_protect byte after purpose). The JS websocket-host still
required version 2 and rejected every TCP bind from a HEAD-built core,
breaking browser port leases.

- websocket-host.ts: accept version 3, minimum length 49. The
  need_protect byte sits after purpose (offset 43) and is a no-op
  outside Android VPN hosts, so the decoder just skips it.
- websocket-host.test.ts: update the test encoder to emit v3.
- binaryen stays at 131.0.0 to match script/build-wasi-core.sh, which
  intentionally pins binaryen 131 for the Go-side embedded core.
  The npm binaryen package provides the wasm-opt binary used by
  build-wasm.mjs, so keeping both sides on the same version avoids
  divergent optimization output.
- pnpm-workspace.yaml: drop two stale minimumReleaseAgeExclude entries
  for @cloudflare/workers-types versions no longer in the lockfile.

* test(easytier-go): cover socket options wire v3 decoders

Direct unit tests for decodeTCPConnectOptions, decodeUDPBindOptions, and
decodeTCPListenOptions with wire v3 documents. Covers combinations of
socket mark, netns, bind device, and local address presence, plus
rejection of wire v2.

Resolves review feedback on PR #2588.
2026-09-19 00:13:47 +08:00
2025-08-01 23:58:01 +08:00
2026-09-16 20:50:43 +08:00
2026-09-16 20:50:43 +08:00
2026-09-16 20:50:43 +08:00
2026-09-16 20:50:43 +08:00
2026-09-16 20:50:43 +08:00
2026-09-16 20:50:43 +08:00
2025-06-07 10:56:54 +08:00

EasyTier

Github release GitHub GitHub last commit GitHub issues GitHub Core Actions GitHub GUI Actions GitHub Test Actions Ask DeepWiki

简体中文 | English

✨ A simple, secure, decentralized virtual private network solution powered by Rust and Tokio

config page running page

📚 Full Documentation | 🖥️ Web Console | 📝 Download Releases | 🧩 Third Party Tools | ❤️ Sponsor

Features

Core Features

  • 🔒 Decentralized: Nodes are equal and independent, no centralized services required
  • 🚀 Easy to Use: Multiple operation methods via web, client, and command line
  • 🌍 Cross-Platform: Supports Win/MacOS/Linux/FreeBSD/Android and X86/ARM/MIPS architectures
  • 🔐 Secure: AES-GCM or WireGuard encryption, prevents man-in-the-middle attacks

Advanced Capabilities

  • 🔌 Efficient NAT Traversal: Supports UDP and IPv6 traversal, works with NAT4-NAT4 networks
  • 🌐 Subnet Proxy: Nodes can share subnets for other nodes to access
  • 🔄 Intelligent Routing: Latency priority and automatic route selection for best network experience
  • ⚡ High Performance: Zero-copy throughout the entire link, supports TCP/UDP/WSS/WG protocols

Network Optimization

  • 📊 UDP Loss Resistance: KCP/QUIC proxy optimizes latency and bandwidth in high packet loss environments
  • 🔧 Web Management: Easy configuration and monitoring through web interface
  • 🛠️ Zero Config: Simple deployment with statically linked executables

Quick Start

📥 Installation

Choose the installation method that best suits your needs:

Linux (Recommended):

curl -fsSL "https://github.com/EasyTier/EasyTier/blob/main/script/install.sh?raw=true" | sudo bash -s install

Homebrew (MacOS/Linux):

brew tap brewforge/chinese
brew install --cask easytier-gui

Windows (Recommended, run with administrator privileges):

irm "https://github.com/EasyTier/EasyTier/blob/main/script/install.ps1?raw=true" | iex

Install via cargo (Latest development version):

cargo install --git https://github.com/EasyTier/EasyTier.git easytier

Install pre-built binary (Recommended, All platforms supported)

Install via Docker

Install OpenWrt ipk package

Additional steps:

One-Click Register Service (Automatically start when the system boots and run in the background)

🚀 Basic Usage

Quick Networking with Shared Nodes

EasyTier supports quick networking using shared public nodes. When you don't have a public IP, you can use the free shared nodes provided by the EasyTier community. Nodes will automatically attempt NAT traversal and establish P2P connections. When P2P fails, data will be relayed through shared nodes.

When using shared nodes, each node entering the network needs to provide the same --network-name and --network-secret parameters as the unique identifier of the network.

Taking two nodes as an example (Please use more complex network name to avoid conflicts):

  1. Run on Node A:
# Run with administrator privileges
sudo easytier-core -d --network-name abc --network-secret abc -p tcp://<SharedNodeIP>:11010
  1. Run on Node B:
# Run with administrator privileges
sudo easytier-core -d --network-name abc --network-secret abc -p tcp://<SharedNodeIP>:11010

After successful execution, you can check the network status using easytier-cli:

| ipv4         | hostname       | cost  | lat_ms | loss_rate | rx_bytes | tx_bytes | tunnel_proto | nat_type | id         | version         |
| ------------ | -------------- | ----- | ------ | --------- | -------- | -------- | ------------ | -------- | ---------- | --------------- |
| 10.126.126.1 | abc-1          | Local | *      | *         | *        | *        | udp          | FullCone | 439804259  | 2.6.2-70e69a38~ |
| 10.126.126.2 | abc-2          | p2p   | 3.452  | 0         | 17.33 kB | 20.42 kB | udp          | FullCone | 390879727  | 2.6.2-70e69a38~ |
|              | PublicServer_a | p2p   | 27.796 | 0.000     | 50.01 kB | 67.46 kB | tcp          | Unknown  | 3771642457 | 2.6.2-70e69a38~ |

You can test connectivity between nodes:

# Test connectivity
ping 10.126.126.1
ping 10.126.126.2

Note: If you cannot ping through, it may be that the firewall is blocking incoming traffic. Please turn off the firewall or add allow rules.

To improve availability, you can connect to multiple shared nodes simultaneously:

# Connect to multiple shared nodes
sudo easytier-core -d --network-name abc --network-secret abc -p tcp://<SharedNodeIP1>:11010 -p udp://<SharedNodeIP2>:11010

Once your network is set up successfully, you can easily configure it to start automatically on system boot. Refer to the One-Click Register Service guide for step-by-step instructions on registering EasyTier as a system service.

Decentralized Networking

EasyTier is fundamentally decentralized, with no distinction between server and client. As long as one device can communicate with any node in the virtual network, it can join the virtual network. Here's how to set up a decentralized network:

  1. Start First Node (Node A):
# Start the first node
sudo easytier-core -i 10.144.144.1

After startup, this node will listen on the following ports by default:

  • TCP: 11010
  • UDP: 11010
  • WebSocket: 11011
  • WebSocket SSL: 11012
  • WireGuard: 11013
  1. Connect Second Node (Node B):
# Connect to the first node using its public IP
sudo easytier-core -i 10.144.144.2 -p udp://FIRST_NODE_PUBLIC_IP:11010
  1. Verify Connection:
# Test connectivity
ping 10.144.144.2

# View connected peers
easytier-cli peer

# View routing information
easytier-cli route

# View local node information
easytier-cli node

For more nodes to join the network, they can connect to any existing node in the network using the -p parameter:

# Connect to any existing node using its public IP
sudo easytier-core -i 10.144.144.3 -p udp://ANY_EXISTING_NODE_PUBLIC_IP:11010

🔍 Advanced Features

Subnet Proxy

Assuming the network topology is as follows, Node B wants to share its accessible subnet 10.1.1.0/24 with other nodes:

flowchart LR

subgraph Node A Public IP 22.1.1.1
nodea[EasyTier<br/>10.144.144.1]
end

subgraph Node B
nodeb[EasyTier<br/>10.144.144.2]
end

id1[[10.1.1.0/24]]

nodea <--> nodeb <-.-> id1

To share a subnet, add the -n parameter when starting EasyTier:

# Share subnet 10.1.1.0/24 with other nodes
sudo easytier-core -i 10.144.144.2 -n 10.1.1.0/24

Subnet proxy information will automatically sync to each node in the virtual network, and each node will automatically configure the corresponding route. You can verify the subnet proxy setup:

  1. Check if the routing information has been synchronized (the proxy_cidrs column shows the proxied subnets):
# View routing information
easytier-cli route

Routing Information

  1. Test if you can access nodes in the proxied subnet:
# Test connectivity to proxied subnet
ping 10.1.1.2

WireGuard Integration

EasyTier can act as a WireGuard server, allowing any device with a WireGuard client (including iOS and Android) to access the EasyTier network. Here's an example setup:

flowchart LR

ios[[iPhone<br/>WireGuard Installed]]

subgraph Node A Public IP 22.1.1.1
nodea[EasyTier<br/>10.144.144.1]
end

subgraph Node B
nodeb[EasyTier<br/>10.144.144.2]
end

id1[[10.1.1.0/24]]

ios <-.-> nodea <--> nodeb <-.-> id1
  1. Start EasyTier with WireGuard portal enabled:
# Register one WireGuard client as virtual peer 10.144.144.3
sudo easytier-core -i 10.144.144.1 \
  --network-secret portal-secret \
  --vpn-portal wg://0.0.0.0:11013 \
  --vpn-portal-private-key "$(wg genkey)" \
  --vpn-portal-client phone=10.144.144.3
  1. Get WireGuard client configuration:
# Get WireGuard client configuration
easytier-cli vpn-portal
  1. In the output configuration, replace a wildcard Peer.Endpoint with the public IP/domain of your EasyTier node, then import it. Interface.Address is local to that WireGuard client and may be changed to any IPv4 address; EasyTier translates it to the registered virtual-peer address.

Self-Hosted Public Shared Node

You can run your own public shared node to help other nodes discover each other. A public shared node is just a regular EasyTier network (with same network name and secret) that other networks can connect to.

To run a public shared node:

# No need to specify IPv4 address for public shared nodes
sudo easytier-core --network-name mysharednode --network-secret mysharednode
  • ZeroTier: A global virtual network for connecting devices.
  • TailScale: A VPN solution aimed at simplifying network configuration.

Contact Us

License

EasyTier is released under the LGPL-3.0.

Responsible Use

Use EasyTier only for lawful purposes and in compliance with applicable laws and regulations. You are responsible for ensuring that you are authorized to connect to and administer the networks and devices involved.

Sponsor

CDN acceleration and security protection for this project are sponsored by Tencent EdgeOne.

EdgeOne Logo

Special thanks to Langlang Cloud and RainCloud for sponsoring our public servers.

If you find EasyTier helpful, please consider sponsoring us. Software development and maintenance require a lot of time and effort, and your sponsorship will help us better maintain and improve EasyTier.

Languages
Rust 82.3%
Go 5.2%
Vue 4.9%
TypeScript 3.5%
JavaScript 1.8%
Other 2%