VilaNet OpenWrt
Official VilaVPN router build

VilaVPN for every device
on your network.

vilanet for OpenWrt is the router-class VilaVPN client. Install one IPK, log in once, and every device on your LAN — phones, TVs, IoT, consoles, laptops — exits through VilaVPN with zero per-device setup. Managed from LuCI or UCI.

2
IPK packages
≥24.10
OpenWrt release
11
CLI verbs
AES-256
GCM envelope
What is VilaNet for OpenWrt

Two IPKs. One router. Every device covered.

VilaNet ships to OpenWrt as a pair of IPK packages — a vilanet-core service binary plus a luci-app-vilanet web UI. Install both with opkg, log in once, and every device on your LAN exits through VilaVPN. No per-device VPN apps, no profile installs. Routing, DNS leak protection, and a kill-switch are managed in UCI and visible in LuCI.

AES-256-GCM credentials envelope

Password is sealed with an AES-256-GCM envelope keyed off a per-device 32-byte secret in /etc/vilanet/device.secret. No plaintext password on flash; legacy plaintext is migrated transparently on first read.

Embedded sing-box 1.13

Hysteria2, VLESS-Reality, Trojan, VMess, AnyTLS, Shadowsocks-2022 — every protocol the VilaNet app supports, in one router-side binary. No external sing-box install needed.

LuCI + UCI native

Three LuCI tabs (Overview, Servers, Settings) plus the standard uci CLI, in 9 languages. Anything you can flip in LuCI is also a UCI option — perfect for scripts, Ansible, or backup-restore.

Full-device TUN

When TUN is enabled, every LAN client's traffic enters the tunnel — including DNS over the encrypted path. No per-device config, no PAC files, no client-side leaks.

Kill-switch

When the tunnel drops, the firewall blocks LAN forwarding paths that would bypass it — a fail-closed allow-list keeps essential maintenance paths reachable. Survives DHCP renewals and link bounces.

Per-device credential vault

Account credentials are sealed in an AES-256-GCM envelope keyed off a 32-byte per-device secret via HKDF. A casual strings dump reveals nothing useful, and a copied envelope file won't decrypt on a different router.

Installation

Pick your architecture

Pre-built IPKs for the latest release are published on the GitHub releases page. Pick the architecture that matches opkg print-architecture on your router.

iStoreOS / iStore users: Download vilanet-istore-install_1.0.33.run from the releases page, then open iStore → Manual Install and upload the .run file. iStore installs everything automatically (core + LuCI + all languages). Skip the manual steps below.
OpenWrt 25.x (APK-based): VilaNet also ships .apk packages for OpenWrt 25 and newer, which uses the APK package manager instead of opkg. Both formats are published for every release.
# Transfer APKs to the router router:~# cd /tmp router:~# wget https://github.com/vilavpn/vilanet-openwrt/releases/download/v1.0.33/vilanet-core_1.0.33_x86_64.apk router:~# wget https://github.com/vilavpn/vilanet-openwrt/releases/download/v1.0.33/luci-app-vilanet_1.0.33_noarch.apk router:~# apk add --allow-untrusted --force-non-repository /tmp/vilanet-core_1.0.33_x86_64.apk /tmp/luci-app-vilanet_1.0.33_noarch.apk
After install, hard-refresh LuCI and navigate to Network > VilaNet.
router:~# cd /tmp router:~# wget https://github.com/vilavpn/vilanet-openwrt/releases/download/v1.0.33/vilanet-core_1.0.33_x86_64.ipk router:~# wget https://github.com/vilavpn/vilanet-openwrt/releases/download/v1.0.33/luci-app-vilanet_1.0.33_all.ipk router:~# opkg update router:~# opkg install vilanet-core_1.0.33_x86_64.ipk luci-app-vilanet_1.0.33_all.ipk router:~# /etc/init.d/vilanet enable router:~# vilanet version # sanity check
opkg ≥24.10 required: The IPK uses the tar/ustar 1.0 format. Older opkg builds (the ar-format dialect) will reject it. Newer OpenWrt releases accept both.
Upgrading from v1.0.7 or older? Earlier releases shipped translations as separate luci-i18n-vilanet-* packages. Starting with v1.0.8, all languages are bundled into luci-app-vilanet. Remove any installed luci-i18n-vilanet-* packages before upgrading to avoid file-clash errors (fixed automatically from v1.0.10+):
router:~# opkg remove $(opkg list-installed | awk '/^luci-i18n-vilanet-/{print $1}') router:~# opkg install vilanet-core_1.0.33_x86_64.ipk luci-app-vilanet_1.0.33_all.ipk

x86_64 — VMs, mini-PCs, modern routers

For Proxmox VMs, generic x86 routers, OPNsense-killer boxes, and OpenWrt-on-laptop builds. Verify with opkg print-architecture — you should see x86_64 in the output.

Which architecture do I have? SSH into the router and run opkg print-architecture. The last column of any line is the architecture string — pick the IPK whose filename ends in that suffix. If you see arm_cortex-a7_neon-vfpv4, select the armv7 · cortex-a7 option above — that variant is built for this arch. For anything else exotic, contact [email protected] with your opkg print-architecture output.
Quick Start

From flashed router to working tunnel

Both IPKs installed? These four steps take you from a freshly-flashed router to every LAN device exiting through VilaVPN. Run them from the router's shell.

1. Authenticate

Log in once. The password is sealed into /etc/vilanet/.credentials with AES-256-GCM — no plaintext on flash.

router:~# vilanet login Email: [email protected] Password: # typed silently, sealed with AES-256-GCM Login successful.

2. Browse your nodes

List packages and servers. Node IDs are stable hex digests — safe to scriptify or paste into UCI.

router:~# vilanet packages router:~# vilanet servers router:~# vilanet servers --country HK

3. Connect

Connect to a country, a specific node ID, or let auto-select choose. The service brings up sing-box on TUN and updates the firewall rules in one go.

router:~# vilanet connect -country HK Connected to HK-01 (Hong Kong) — exit 198.51.100.42 # Or by node ID for stable scripts: router:~# vilanet connect -node 7a4f2e1b8c3d9f5e # Or via UCI + init script (survives reboots): router:~# uci set vilanet.global.selected_server=7a4f2e1b8c3d9f5e router:~# uci commit vilanet router:~# /etc/init.d/vilanet restart

4. Verify on a LAN client

From any device on the LAN, check the public IP. It should match the exit node — proof that traffic is taking the encrypted path.

# On a laptop / phone connected to the router's LAN $ curl https://ifconfig.me 198.51.100.42 # <- VilaNet HK exit, not your ISP # From the router itself, check live status router:~# vilanet status Connected · HK-01 · 198.51.100.42 Uptime: 00:14:32 Tx/Rx: 2.4 MB / 18.7 MB Protocol: Hysteria2
How traffic flows

From LAN port to exit node

Every LAN client's packets are intercepted by the router's TUN device, handed to the embedded sing-box for protocol multiplexing, encrypted, and sent over the WAN port to a VilaVPN exit node. DNS rides the same encrypted path; the kill-switch firewall rules ensure nothing leaks if the tunnel drops.

┌────────────────────┐ │ LAN clients │ phones · TVs · IoT · laptops · consoles └─────────┬──────────┘ │ unmodified packets via br-lan ▼ ┌────────────────────┐ │ OpenWrt firewall │ fw4 / nftables — kill-switch rules live here │ + TUN inbound │ catches all LAN-egress when tun.enabled=1 └─────────┬──────────┘ │ packets reach the vilanet daemon ▼ ┌────────────────────┐ │ sing-box (embedded)│ generated config from UCI │ DNS-over-HTTPS │ block_dot · block_quic · block_stun (tri-state) │ geoip + geosite │ bypass_china · custom domain rules └─────────┬──────────┘ │ encrypted: Hysteria2 / VLESS-Reality / Trojan / VMess ▼ ┌────────────────────┐ │ VilaNet exit node │ high-throughput, resilient └─────────┬──────────┘ │ ▼ The open internet
LuCI

Three tabs do everything

After installing the LuCI app, open http://router.lan/cgi-bin/luci/admin/network/vilanet in a browser. Sign in with the same credentials you use for OpenWrt's admin UI. You'll see three tabs.

9 languages on the router: The LuCI VilaNet app ships translations for English, Chinese (Simplified), Chinese (Traditional), Japanese, Korean, Russian, Persian (Farsi), Arabic, and Vietnamese. The router interface language follows OpenWrt's own locale setting (System → System → Language) — no separate language package to download. (This guide page is available in 7 languages via the switcher above.)

Sign in

First visit shows the sign-in card on the Overview tab. Enter your VilaVPN email and password — credentials are sealed into the AES-256-GCM envelope at /etc/vilanet/.credentials on the router. Nothing is stored in the browser.

LuCI VilaNet login screen — email and password fields with the Login button
Overview · not signed in The sign-in card on a fresh router. After login, this same tab shows live status, account info, and quick actions.

Overview · post-login

After signing in, Overview becomes the live dashboard: VPN service state, selected server, routing mode, account, and a Quick Actions panel for connect / disconnect / mode switching without leaving the page.

LuCI VilaNet Overview — running, connected, signed in, with Quick Actions and Configuration Snapshot
Overview · connected to HK-Nube VPN service running, connection connected, selected node visible, account signed in. The Quick Actions block lets you connect/disconnect/swap mode in place; Configuration Snapshot echoes the most useful UCI knobs as a sanity check.

Servers · pick any node

The full server catalogue with a live search filter and a per-row Connect button. Rows show the node name and country; the currently selected node ID is pinned at the top.

LuCI VilaNet Servers tab — nodes across DE / HK / JP / SG / TW / UK / US with per-row Connect buttons
Servers · pick any node Hysteria2, Shadowsocks-IPLC, and IPv6 nodes across DE, HK, JP, SG, TW, UK, US. Type into Search to filter; click Connect to switch nodes in place — the router restarts sing-box with the new selection.

Settings · every UCI knob

All UCI keys surfaced as form fields: auto-connect, domain strategy, tunnel/direct DNS, MTU, LAN proxy sharing, authentication. Save Settings writes UCI and restarts the service in one step.

LuCI VilaNet Settings tab — form fields for every UCI key with Save Settings and Reset buttons
Settings · OpenWrt 24.10 Same fields as the UCI reference table below, in form-field shape. Save Settings performs the uci set + uci commit + /etc/init.d/vilanet restart cycle for you.
LuCI VilaNet Settings tab on OpenWrt 25 — showing APK package manager, Traffic Policy (PAC/Global), Routing Mode (TUN gateway), Block QUIC, and custom rules
Settings · OpenWrt 25 (APK) Same LuCI app on OpenWrt 25 with APK package manager. Shows v1.0.13 additions: Traffic Policy (PAC / Global), Routing Mode (transparent TUN gateway), Block QUIC (no_drop fix for Chrome/Gemini), and custom domain rules. The package manager field confirms APK is in use.
Command reference

vilanet on the shell

Every LuCI action has a CLI equivalent. Run vilanet help for the index, or vilanet help <verb> for per-command flags. All output is plain text by default — pass -json on read-only commands to get machine-readable output.

login

Interactive prompt → seals email + password into the AES-256-GCM credentials envelope. Idempotent.

logout

Erases stored credentials and the cached package list. Idempotent.

status

Live state: connected node, public exit IP, uptime, Tx/Rx, protocol. Returns exit 0 when connected, 3 when idle.

connect

Bring up the tunnel. Flags: -country ISO, -node ID, -package NAME. With no flags, picks from the active UCI selection.

disconnect

Tear down the tunnel. Restores baseline firewall rules. Idempotent.

servers

List nodes. Filter with -country / -package. Add -json for scripting.

packages

List packages on your account: name, node count, system type, due date.

mode

Switch routing policy: global (all traffic tunnelled), rule (smart China bypass), or pac (alias for rule). Persists to connection_mode in UCI and restarts the service.

config

Read or write any UCI key. Sub-verbs: show, get, set, show-singbox. Setting auth.email or auth.password is rejected with a redirect to vilanet login.

diagnostics

Writes a redacted .tar.gz support bundle: log tail, redacted UCI, public-node DTOs, environment. Mode 0600. Safe to email to support.

version

Print version, sing-box version, build hash. Useful in bug reports.

Try it now

Interactive terminal simulator

Type any vilanet command below and see the (simulated) output. Or click a chip to drop in a ready-made example. No router required — it all runs in your browser.

vilanet version vilanet login vilanet servers --country HK vilanet connect -country HK vilanet status vilanet disconnect vilanet config show vilanet config show-singbox vilanet mode rule vilanet diagnostics vilanet help
[email protected] — vilanet simulator
router:~#
Note: the simulator reproduces the look of real router output but does not connect to anything — it can't reveal your account info or actually establish a tunnel.
UCI

/etc/config/vilanet — every knob

Every LuCI form field is backed by a UCI option in /etc/config/vilanet. Read with uci show vilanet; write with uci set vilanet.<section>.<key>=<value> && uci commit vilanet. Changes take effect after /etc/init.d/vilanet restart.

global

KeyValuesDescription
enabled0 · 1Run on boot when 1. Set by connect / disconnect.
auto_connect0 · 1Bring up the tunnel automatically when the service starts.
auto_reconnect0 · 1Re-dial the same node on transient failure.
selected_servernode IDStable hex digest of the active node.
selected_packagepackage IDConstrains selected_server to a package.
connection_modeglobal · rule · pacRouting mode. global routes all traffic through the tunnel; rule (or its alias pac) enables smart China-bypass routing. pac is accepted as a user-friendly alias for rule — both produce identical sing-box config.
routing_modeproxy · tunproxy = SOCKS/HTTP inbound only (LAN devices proxy-aware). tun = whole-LAN transparent gateway — every LAN client's traffic captured via TUN, zero per-device config. Use tun for IoT, consoles, and devices that can't use a proxy.
log_levelerror · warn · infoVerbosity for /var/log/vilanet.log. Clamped to {error, warn, info} — debug and higher are not persisted via UCI; use VILANET_DEBUG=1 in the daemon environment instead.
TUN whole-LAN gateway (routing_mode=tun): With routing_mode=tun every LAN device's traffic is transparently captured — phones, consoles, IoT, anything — with zero per-device proxy setup. sing-box handles DNS and routing entirely on-router. Enable it with:
router:~# uci set vilanet.global.routing_mode=tun router:~# uci commit vilanet router:~# /etc/init.d/vilanet restart
To revert to proxy-only mode (LAN devices must manually set a proxy):
router:~# uci set vilanet.global.routing_mode=proxy router:~# uci commit vilanet && /etc/init.d/vilanet restart

network

KeyValuesDescription
domain_strategyipv4_only · prefer_ipv4 · prefer_ipv6How the resolver mixes A and AAAA.
dns_remotehttps://1.0.0.1/dns-queryDoH endpoint used over the tunnel.
dns_locale.g. 223.5.5.5Bypass resolver — used for direct-route names.
bypass_china0 · 1Apply the geosite-CN / geoip-CN smart bypass rules.
bypass_lan0 · 1Keep RFC1918 traffic on the direct path.
sniff_enabled0 · 1Leading sniff rule on inbound — needed for SNI-based routing.
mux_enabled0 · 1Multiplex outbound where the protocol supports it.
tun_enabled0 · 1Whole-router TUN capture. Default for headless routers.
dns_modefakeip · realsing-box DNS strategy — fake-ip is faster, real-DNS works with apps that don't honour DNS.
block_ads0 · 1Geosite-category-ads reject.
block_porn0 · 1Geosite-category-porn reject.
block_dotdefault · 0 · 1Tri-state. default = on. Blocks DNS-over-TLS on the LAN to force resolution through the tunnel.
block_quicdefault · 0 · 1Tri-state. default = on. Blocks UDP/443 so HTTP/3 falls back to TCP/TLS, eliminating QUIC-path DNS leaks. Also fixes blank-page errors in Chrome and Google apps (including Gemini) when the QUIC handshake fails mid-tunnel — the no_drop reject rule redirects rather than silently drops the packet.
block_stundefault · 0 · 1Tri-state. default = off. Blocks STUN endpoint discovery — useful for WebRTC IP leak prevention.
enable_ipv60 · 1Allow IPv6 over the tunnel. Off by default — many exit nodes are v4-only.
mtue.g. 1420TUN MTU. Lower if you see fragmentation on bridged uplinks.

proxy (LAN SOCKS sharing)

KeyValuesDescription
enabled0 · 1Expose SOCKS5+HTTP on the LAN. Opt-in — off by default.
port10081Mixed-inbound listen port.
auth_enabled0 · 1Require username + password.
auth_userstringProxy username.
auth_passstringProxy password.

kill_switch

KeyValuesDescription
enabled0 · 1Block LAN forwarding paths that would bypass the tunnel. The fail-closed allow-list is maintained internally — no manual sync needed.
If TUN isn't active: Enabling the kill-switch without TUN running will interrupt LAN internet during service restarts. For headless routers, leave tun.enabled=1 as well.

hy2 (Hysteria2 tuning)

KeyValuesDescription
speed_modeoff · server_default · customDefault off (BBR). server_default uses the package's advertised cap. custom honours custom_mbps.
custom_mbpse.g. 200Mbit/s in both directions. Only used when speed_mode='custom'.
hop_intervalseconds, e.g. 30Port-hopping interval — improves resilience under aggressive throttling.

domains (custom rules)

Custom rules apply before the geosite block, so they override defaults. Each entry is <domain> <action> where action is bypass, proxy, or block. Wildcards: *.youtube.com matches subdomains; bare youtube.com matches exactly.

router:~# uci add_list vilanet.domains.entry='*.youtube.com proxy' router:~# uci add_list vilanet.domains.entry='github.com bypass' router:~# uci add_list vilanet.domains.entry='ads.example.net block' router:~# uci commit vilanet router:~# /etc/init.d/vilanet restart

clash_api (off by default)

KeyValuesDescription
enabled0 · 1Expose sing-box's clash-api on 127.0.0.1:<port>. Router product is headless, so off by default.
port9090Clash-API listen port. Loopback only.
secretstringAPI token. Shown as secret_set: true in config show, never as plaintext.
Apply changes: Every uci set must be paired with uci commit vilanet to persist, and /etc/init.d/vilanet restart to take effect. LuCI's Save & Apply does all three for you.
On-disk layout

Where everything lives on the router

The IPK is well-behaved: every file goes to a predictable path. Useful for backup-restore, Ansible playbooks, and post-mortem debugging.

PathPurpose
/usr/bin/vilanetService binary (single static Go executable, embedded sing-box).
/etc/init.d/vilanetprocd init script: start · stop · restart · enable.
/etc/config/vilanetUCI config — the table above documents every key.
/etc/vilanet/device.secret32-byte per-device secret. Mode 0400. Never back this up to git or cloud storage.
/etc/vilanet/.credentialsAES-256-GCM-sealed credential blob. Mode 0600. Legacy plaintext is migrated on first read.
/var/log/vilanet.logRolling daily log file. vilanet diagnostics bundles the tail.
/var/run/vilanet.pidprocd PID file.
/var/lib/vilanet/State directory — sing-box working state, cached package list.
/usr/libexec/rpcd/vilanetrpcd shim — LuCI talks to vilanet through this.
/www/luci-static/resources/view/vilanet/LuCI JS views: overview.js, servers.js, settings.js.
Treat device.secret as a secret: If device.secret leaks, the AES-256-GCM credentials envelope is decryptable. Exclude it from cloud backups, Ansible vaults, and config-export images. Re-running vilanet login after a fresh generation invalidates anything sealed against the old secret.
Troubleshooting

When something's off, start here

opkg refuses to install (Cannot satisfy the following dependencies / Malformed package)

The IPK ships in tar/ustar 1.0 format. Older OpenWrt builds with the ar-format opkg dialect reject it. Upgrade to OpenWrt ≥24.10, or use the matching archive/ snapshot for your release.

opkg upgrade fails with file-clash / "exists on filesystem" error

Upgrading from an earlier split-package layout (where translations shipped as separate luci-i18n-vilanet-* IPKs) to the bundled layout (v1.0.8+, where all translations are included in luci-app-vilanet) could produce file-clash errors. Fixed in v1.0.10. If you see this error on an older install, remove the old translation IPKs first: opkg remove luci-i18n-vilanet-zh-cn luci-i18n-vilanet-ja (or whichever locales are installed), then re-run opkg install luci-app-vilanet_*.ipk.

Chrome, Edge, or Google apps show a blank page through the tunnel

The browser tried QUIC (HTTP/3) to reach a Google endpoint, the sing-box reject rule silently dropped the UDP/443 packet instead of sending an ICMP unreachable, and the browser waited for a timeout before falling back to TCP — resulting in a blank page. Fixed in v1.0.11 (vilanet-core v0.1.8): the sing-box config now emits a no_drop reject action so the browser falls back immediately. If you're on v1.0.10 or older, upgrading to v1.0.11+ resolves this.

vilanet status says "idle" but connect just printed Connected

Check the log: logread -e vilanet and tail -200 /var/log/vilanet.log. The most common cause is a routing-mode/DNS mismatch (R1 — fixed in v0.2.0). If you're on an older build, try vilanet mode rule as a workaround, then upgrade.

LAN clients get connectivity drops every few seconds

Almost always MTU. Drop network.mtu from 1420 to 1380 (some PPPoE uplinks) or 1280 (encapsulated WAN). Restart the service after each change.

YouTube / Netflix is slow, but speedtest is fine

Browser is preferring QUIC. With block_quic=1 (the default) sing-box rejects UDP/443, forcing the browser to fall back to TCP. Either accept the fallback (slight loss in throughput) or set block_quic=0 if you accept the DNS-leak risk.

"Failed to bind TUN" / "operation not permitted"

TUN needs the tun kernel module. opkg install kmod-tun if missing. Some minimal OpenWrt images strip it.

Login keeps failing with "invalid credentials" — but the password is right

Probably a stale envelope from before device.secret was rotated. vilanet logout && vilanet login reseats both files. If that fails, delete /etc/vilanet/.credentials manually and log in again.

Need to ship a bug report

Run vilanet diagnostics. It writes a redacted .tar.gz bundle (log tail, redacted UCI, public-node DTOs, environment) to /var/lib/vilanet/diag/. Mode 0600. Email the file to [email protected].

Live log tailing: For most problems, logread -f | grep -i vilanet in one window and the failing command in another tells you everything.
AI assistants

Drive vilanet-openwrt with your AI

vilanet-openwrt ships a universal Agent Skill at ai/vilanet-openwrt/SKILL.md — a single plain-markdown file that teaches Claude Code, Codex CLI, Gemini CLI, Cursor, Cline, Aider, Copilot CLI, and any other modern AI assistant how to drive the router-side VPN on your behalf. Install on the machine you SSH into the router from, then ask your AI to install the IPK, flip UCI keys, drive ubus call vilanet ..., or diagnose a stuck connection.

Claude Code

Drop the skill file into ~/.claude/skills/vilanet-openwrt/SKILL.md on your laptop. Claude Code auto-discovers it on next start, so any new session can install IPKs, flip UCI keys, and drive ubus end-to-end.

$ mkdir -p ~/.claude/skills/vilanet-openwrt $ curl -fsSL https://testingcf.jsdelivr.net/gh/vilavpn/vilanet-openwrt@main/ai/vilanet-openwrt/SKILL.md \ -o ~/.claude/skills/vilanet-openwrt/SKILL.md

Gemini CLI ≥ 0.41

Use native Agent Skills support. From a local checkout the skills link form is the fastest path; on older Gemini builds, append a @./relative-path import to GEMINI.md instead.

$ gemini skills install https://github.com/vilavpn/vilanet-openwrt.git --path ai/vilanet-openwrt # Or from a local checkout: $ gemini skills link ./ai/vilanet-openwrt

Codex CLI · Cursor · Cline · Aider · Copilot CLI

The skill content is identical across every tool — only the install path differs. Write to a neutral location, then point your AI at it (and, for tools that read AGENTS.md, append idempotently rather than overwriting).

$ mkdir -p .agents/skills/vilanet-openwrt $ curl -fsSL https://testingcf.jsdelivr.net/gh/vilavpn/vilanet-openwrt@main/ai/vilanet-openwrt/SKILL.md \ -o .agents/skills/vilanet-openwrt/SKILL.md # For tools that only read AGENTS.md, append (idempotent — re-runs are a no-op): $ grep -qxF '## VilaNet OpenWrt Agent Skill' AGENTS.md 2>/dev/null || \ { echo; echo '## VilaNet OpenWrt Agent Skill'; cat .agents/skills/vilanet-openwrt/SKILL.md; } >> AGENTS.md

Once installed, here's how you talk to it

You don't need to memorise UCI keys, ubus methods, or LuCI menus. Once the skill is loaded, describe what you want in plain language — examples below are the kind of thing you can paste into your AI chat.

Install the vilanet-core and luci-app-vilanet IPKs on my router at 192.168.1.1 — it's running OpenWrt 24.10.2 on x86_64.
Log in to my VilaVPN account and connect the router to a Hong Kong exit.
The LuCI Overview tab keeps showing the sign-in form even though I logged in via SSH. Diagnose and fix it.
Set network.dns_mode=real and switch to connection_mode=rule (smart bypass), then restart so the config takes effect.
Block STUN, DoT, and QUIC across the LAN so DNS can't leak around the tunnel.
Switch routing to global so even mainland China traffic goes through the VPN, and reconnect.
My exit IP didn't change after vilanet connect. Pull a vilanet diagnostics bundle and tell me what's wrong.

Your AI translates the request into the right vilanet / uci / ubus invocation, runs it over your existing SSH session, and explains the result back in plain language.