English | 简体中文
AliangGate (module
aliang.one/nursorgate) is a cross-platform, high-performance network gateway written in Go. It combines a TUN/HTTP proxy engine with an attachable AI agent client, and ships with a real-time web dashboard.
AliangGate intercepts and routes traffic at the OS network layer (TUN) or as a standard HTTP/SOCKS5 proxy. Matching AI-service traffic is transparently intercepted (MITM) and forwarded to the Aliang upstream; everything else goes through SOCKS5/VLESS/Shadowsocks or direct. On top of the proxy, the agent subsystem registers the device with a remote phone server and can execute AI-driven "goals" via Claude Code / Codex / OpenCode providers.
- Dual inbound mode
- TUN: transparent interception at the OS network layer (kernel/user-space TUN)
- HTTP proxy: standard HTTP/SOCKS5 listening
- Intelligent routing
- SNI/domain allowlist → MITM to Aliang upstream
- Otherwise: SOCKS5 / VLESS / Shadowsocks / direct
- GeoIP-based rules with cache-first optimization
- DNS/IP binding cache
- Multi-source bindings (SNI, HTTP Host, CONNECT, DNS)
- Bidirectional mapping (domain→IP, IP→domain), hit stats, TTL, LRU eviction
- HTTPS MITM — optional transparent interception with a custom CA
- Link self-healing — background health monitor with mTLS dial retries; transient relay blips are absorbed instead of flipping the link offline
- Agent client
- JWT device authentication and auto-reconnect
- Remote AI approval, slash-command capability discovery
- Goal planning/execution loop (multi-turn planner, read-only exploration, acceptance checks) with streaming thinking
- Session tunneling (piko-based)
- Traffic mirroring — TCP-level stream mirroring of matched domains to an HTTP endpoint (see Traffic Mirror)
- Web dashboard — real-time stats, DNS cache, traffic, rule management (Vue 3 + Vite + TailwindCSS)
- Cross-platform — Windows (service/tray), macOS, Linux; system service install/uninstall and desktop tray mode
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ TUN/HTTP │→→│ Metadata/Rules│→→│ Outbound │
│ Inbound │ │ Engine/Cache │ │ Proxy/Direct │
└─────────────┘ └──────────────┘ └──────────────┘
| Module | Purpose |
|---|---|
cmd/ |
CLI: start, tray, service, config, version (entry: cmd/aliang) |
inbound/ |
TUN / HTTP traffic capture |
processor/ |
Rules, cache, DNS, GeoIP, config, statistics |
outbound/ |
Proxy protocol implementations (incl. proxy/aliang link + health monitor) |
app/http/ |
REST API, dashboard server, agent/goal services |
app/website/ |
Web dashboard (Vue 3, Vite) |
app/tunnel/ |
Session tunnel client |
common/ |
Logger, version, shared utils |
- Go 1.25+
- CGO is required: the build pulls in
go-sqlite3(viagorm.io/driver/sqlite) and GTK/AppIndicator (viabeeep/systray). A binary built withCGO_ENABLED=0compiles but crashes at startup when opening the SQLite store. Go auto-disables CGO when cross-compiling, so setCGO_ENABLED=1explicitly and use a native toolchain for the target platform. - Linux additionally:
build-essential pkg-config libgtk-3-dev libayatana-appindicator3-dev(TUN mode also requires root — capability flags alone are not enough)
# Debian/Ubuntu
sudo apt-get install -y build-essential pkg-config libgtk-3-dev libayatana-appindicator3-dev# Standard build
go build -o aliang ./cmd/aliang
# Optimized for size
go build -trimpath -ldflags="-s -w" -o aliang ./cmd/aliangNote on the local
goproxyreplace:go.modcarriesreplace github.com/elazarl/goproxy v1.7.2 => ../goproxy. If../goproxyis not present on your machine, drop it before building:go mod edit -dropreplace=github.com/elazarl/goproxy
# Start in TUN mode (default)
./aliang start --config ./config.json
# Start HTTP proxy mode
./aliang start --config ./config.json --mode http
# Desktop tray (macOS / Windows)
./aliang tray --config ./config.json
# Install as system service (admin/root)
sudo ./aliang service install --system-wide --config /etc/aliang/config.jsonOpen http://localhost:56431 after start.
| Command | Purpose |
|---|---|
aliang start |
Start the core proxy engine |
aliang tray |
Start in system-tray mode |
aliang service |
Manage as system service (install/uninstall/start/stop) |
aliang config |
Manage / load / validate configuration |
aliang version |
Print version info |
See config.new.json for a full example. Key sections:
core.engine— TUN/HTTP mode, device, log level, …core.aliangServer— Aliang upstream (mTLS relay) addresscustomer.proxy— optional outbound proxy (enable, type, server, credentials)customer.ai_rules— domain/IP allowlists for AI services (routed via MITM)customer.proxy_rules— custom domain/IP routing rulescustomer.traffic_mirror— TCP stream mirroring (below)
config.jsonis machine-local and git-ignored; never commit real credentials.
The same rule syntax applies to customer.ai_rules include lists,
customer.proxy_rules, and customer.traffic_mirror.domains:
# Domains — exact + subdomains, or '*' globs
# ('*' matches one or more characters and may cross dots)
example.com example.com and all of its subdomains
*.epic.com subdomains only (cdn.epic.com), never the bare domain
aliang.* any TLD (aliang.com, aliang.co.uk)
*epic.com prefix match (cdnepic.com, cdn.epic.com)
# IP conditions — IPv4 and IPv6, bare or keyword-prefixed
1.2.3.4 single IP
121.32.12.0/24 CIDR (0.0.0.0/0 matches IPv4 only, ::/0 IPv6 only)
1.2.3.1-1.2.3.254 closed range
ip-cidr:121.32.12.0/24 explicit keyword (also ip-range:, ip-from:…,ip-to:…)
ip-cidr,121.32.12.0/24,proxy Clash style with trailing action (proxy_rules)
- IP conditions match the real destination IP of TCP connections on the TUN path. TLS traffic without SNI, UDP traffic, and local HTTP-proxy inbound traffic bypass the rules engine, so IP conditions have no effect there.
- Malformed entries (invalid IPs, empty labels like
a..b) are rejected on save. traffic_mirror.domainsis matched by domain (SNI/host): a bare single IP can still match literally, but CIDR/range entries never match in the mirror path — use AI rules orproxy_rulesfor IP-based routing.
When enabled, TCP traffic whose domain matches the configured list is mirrored (bypassed, in parallel) to an HTTP endpoint at the stream level. Mirroring applies regardless of the final route (Aliang / direct / other), and mirror failures never affect the live flow.
{
"customer": {
"traffic_mirror": {
"enabled": true,
"target": "http://172.16.159.219:443/mirror",
"domains": ["api.openai.com", "api.anthropic.com", "*.cursor.sh"]
}
}
}| Field | Type | Description |
|---|---|---|
enabled |
bool | Off switch — false costs nothing |
target |
string | HTTP POST endpoint receiving StreamChunks |
domains |
string[] | Domain patterns, see Rule Syntax; matched by SNI/host |
StreamChunk (HTTP POST JSON body):
{
"flow_id": "a1b2c3d4e5f6...",
"conn_id": "tcp-42",
"direction": "request",
"offset": 0,
"seq": 0,
"payload": "<base64 bytes>",
"timestamp": 1715673600123,
"src_addr": "192.168.1.5:54321",
"dst_addr": "104.18.6.192:443",
"client_addr": "192.168.1.5:54321",
"upstream_addr": "104.18.6.192:443",
"protocol_hint": "http1"
}direction:request(client→upstream) orresponse(upstream→client)offset/seq: byte offset and chunk index within one direction, from 0payload: plaintext bytes read in this chunkprotocol_hint:http1/http2/unknown
Flow lifecycle events — besides data chunks, the mirror emits lifecycle
events so the server knows each flow's start and end. Distinguish by
event_type: empty/absent = data chunk, flow_start / flow_end = lifecycle.
flow_start → StreamChunk × N → flow_end
flow_end additionally carries client_to_server_bytes,
server_to_client_bytes, duration_ms, and error_class
(clean / timeout / reset / tls_error / context_cancel / unknown);
flow_start / flow_end both carry host_name.
Reassembling the byte stream server-side:
- Group chunks by
flow_id— one connection - Split by
directioninto independent request/response streams - Sort by
offset(chunks may arrive out of order) - Concatenate
payloadin order → full plaintext stream - Gaps in
offsetmean chunks were dropped (channel full)
# Build
go build ./cmd/aliang
# Run tests for core packages
go test ./processor/cache ./processor/rules
# Test the outbound link + services
go test ./outbound/proxy/aliang/... ./app/http/services/
# Debug the running instance
curl http://localhost:56431/api/dns/stats | jq
curl http://localhost:56431/api/dns/hotspots | jq
curl "http://localhost:56431/api/dns/cache/query?domain=example.com" | jq
curl -X DELETE http://localhost:56431/api/dns/cacheMore docs (API references, service management, certificate trust, …) live in
docs/.
- HTTP/2 frames: extract the
priorityfield from the payload before parsing headers and restore it after modification — Envoy may force HTTP→H2 upgrade, and broken priority handling breaks some sites (e.g. Cursor). - MITM CA: interception requires explicitly trusting
mitm-ca.pem; certificate-pinned apps may resist interception. - GeoIP: routing uses GeoLite2 (
data/GeoLite2-Country.mmdb); refresh the database periodically.
- HTTPS MITM requires trusting the custom CA on the system
- SNI extraction happens at the TCP layer (plaintext TLS ClientHello)
- mTLS is used on the Aliang relay link; agent devices authenticate with JWT device tokens
Proprietary — internal project by aliang.one. All rights reserved.
Maintainer: aliang.one