Architecture

How WRTune communicates with OpenWRT routers under the hood.

WRTune communicates with your router through multiple channels, each chosen for the right task.

1. ubus JSON-RPC over HTTP (Primary) 🔗

The primary communication channel is ubus (OpenWRT’s inter-process communication bus), exposed via HTTP by the rpcd daemon:

HTTP POST → http://<router_ip>/ubus
Body: { "jsonrpc": "2.0", "id": 1, "method": "call", "params": [...] }

The core class OpenWrtApiService handles all ubus communication with these key behaviors:

  • Authentication: Logs in via session.login ubus call, caches the session token
  • Auto-refresh: If a call fails due to expired session, it transparently re-authenticates using stored credentials and retries once
  • Batch calls: batchCall() sends multiple ubus calls in a single HTTP POST (used by WiFi overview for efficiency)
  • Background isolate JSON decode: Large responses are decoded off the main thread via compute() to prevent UI jank
  • Self-signed certs: Detects HandshakeException, throws CertificateException, and supports loginWithTrustedCert()
  • Timeout: 5-second connect and receive timeouts

2. The WRTune Engine (Custom rpcd Script) 🔗

The WRTune Engine is a custom shell script at /usr/libexec/rpcd/wrtune that creates a wrtune ubus namespace. It handles advanced features like WiFi management, client blocking, guest WiFi, and Wake-on-LAN — tasks that stock OpenWRT’s rpcd doesn’t expose natively.

3. Direct uci / luci-rpc Calls 🔗

For data retrieval, WRTune calls standard ubus namespaces directly:

NamespaceUsed For
uciRead/write UCI config (firewall, DHCP, wireless)
luci-rpcDHCP leases, host hints, realtime stats
iwinfoRadio info, associated stations, signal strengths
hostapd.*Wireless client lists
fileExecute shell commands (nlbw, vnstat for statistics)
systemBoard info, uptime, CPU/RAM health

4. SSH (Engine Installation & Terminal) 🔗

  • Engine installation: HeadlessSshRunner connects via SSH, pipes the WRTune script content, and waits for a success marker
  • Interactive terminal: TerminalService spawns a Dart Isolate running sshWorker with dartssh2. All terminal I/O streams between isolates via SendPort/ReceivePort — keeping SSH off the UI thread

5. AdGuard Home API 🔗

The DNS Control feature communicates directly with an AdGuard Home instance on the router via its REST API (not ubus). A separate Dio instance handles AdGuard Home’s HTTP API for DNS stats, filtering rules, and blocked services.

Session Management 🔗

The _sessionRefresher callback pattern allows the API service to silently re-login when a session expires, without user intervention. The AuthRepository provides stored credentials to the API service for transparent refresh.