NomadWiFi

Command line reference

One executable, no dependencies, no installer. Every command accepts --json and writes a single JSON document to stdout, so it composes with anything.

nomadwifi help output listing the status, scan, optimize, connect, watch, vpn, warm, logs and version commands with their flags.
nomadwifi help

Usage

nomadwifi <command> [flags]

Run it with no command to open the interactive terminal menu.

Global flags

FlagApplies toEffect
--jsonevery commandMachine-readable output instead of the rendered view.
--dry-runwatchReport what it would do without actually roaming.
--no-vpnoptimize, connect, watchDo not pause or resume the VPN around a switch.
--password <key>connectThe network key.
--interval <seconds>watchHow often to measure. Default 20.
--allscanList every radio separately instead of one row per network.

status

Link statistics, gateway latency, captive portal state and VPN tunnels.

nomadwifi status showing network, access point, band, signal, link speed, gateway latency, internet reachability and three VPN tunnels.
$ nomadwifi status --json
{
  "interface_name": "Wi-Fi",
  "description": "Intel(R) Dual Band Wireless-AC 8265",
  "connected": true,
  "ssid": "INDY_5G",
  "bssid": "e8:81:75:29:73:bd",
  "band": "5 GHz",
  "channel": 36,
  "radio_type": "802.11ac",
  "rx_mbps": 866,
  "tx_mbps": 866,
  "signal_percent": 100,
  "rssi": -43,
  "gateway_ip": "192.168.1.1",
  "gateway_latency_ms": 3.73,
  "captive_portal": false
}

When a sign-in page is in the way, captive_portal is true and captive_portal_url carries the address to open.

scan

Ranks every network in range. By default one row per network, with the strongest radio standing in for the rest; --all lists each BSSID separately, which is the view the roaming engine works from.

nomadwifi scan output: ranked table with network, band, channel, standard, signal, score and status columns.
$ nomadwifi scan --json
[
  {
    "ssid": "INDY_5G",
    "bssid": "e8:81:75:29:73:bd",
    "signal_percent": 100,
    "rssi": -43,
    "band": "5 GHz",
    "channel": 36,
    "frequency_khz": 5180000,
    "radio_type": "802.11ax",
    "authentication": "WPA2-Personal",
    "cipher": "CCMP",
    "quality_score": 126,
    "auth_status": "SAVED",
    "is_warm": false,
    "reasons": ["Wi-Fi 6 (802.11ax)", "5 GHz band", "AP airtime nearly idle", "Saved network"],
    "has_channel_util": true,
    "station_count": 4,
    "bssid_count": 1
  }
]
FieldMeaning
band2.4 GHz, 5 GHz or 6 GHz, derived from frequency_khz — 6 GHz channel numbers overlap the lower bands, so the channel alone cannot tell them apart.
rssiMeasured signal in dBm. signal_percent is derived from it, not from the driver's link quality.
auth_statusSAVED, INFERRED (a venue sibling's key), OPEN or LOCKED.
is_warmNomadWiFi has already written a Windows profile for this network.
station_count, has_channel_utilFrom the access point's BSS Load element, when it publishes one.
bssid_countHow many radios this network was collapsed from. Only in the default view.
reasonsPlain-language explanation of the score.

optimize

Switch to the best access point once and stop. If the new one does not reach the internet within the deadline, the previous network is restored.

$ nomadwifi optimize --json
{
  "success": true,
  "switched": true,
  "current_ssid": "Hotel_Lobby",
  "target_ssid": "Hotel_5G",
  "band": "5 GHz",
  "reason": "5 GHz band, Wi-Fi 6 (802.11ax), stronger signal"
}

switched is false when the current access point is already the best choice.

connect

Connect to a named network. Open networks need no key; for a locked one, either the key is already saved, NomadWiFi can infer it from a venue sibling, or you supply it.

$ nomadwifi connect "Hotel Guest"
$ nomadwifi connect "Hotel_5G" --password "roomnumber123"
If the key turns out to be wrong, the profile NomadWiFi wrote is rolled back rather than left behind in your Windows profile list.

watch

Stay resident: measure link health on an interval, react to disconnects the moment the wireless service reports them, and roam when it is warranted. This is the same engine the desktop app runs.

$ nomadwifi watch
$ nomadwifi watch --interval 10
$ nomadwifi watch --dry-run     # report decisions, change nothing
$ nomadwifi watch --no-vpn      # never touch the tunnel

With --json, each event is written as one JSON object per line as it happens.

vpn

Inspect the tunnels on this machine, or coordinate one by hand.

nomadwifi vpn status output listing detected tunnels, whether each is up, whether it owns the default route and whether NomadWiFi can pause it.
CommandEffect
nomadwifi vpn statusEvery detected tunnel, whether it is up, whether it owns the default route, and whether NomadWiFi can control it.
nomadwifi vpn holdPause the active tunnel — for signing in to a captive portal, for instance.
nomadwifi vpn resumeBring it back and flush the DNS cache.
$ nomadwifi vpn status --json
{
  "tunnels": [
    {
      "provider": "NordVPN (NordLynx)",
      "adapter": "NordLynx",
      "up": true,
      "owns_default_route": true,
      "controllable": false,
      "control_hint": "This client has no command line on Windows. ...",
      "local_ip": "10.5.0.2"
    }
  ],
  "active": { /* the tunnel that owns the default route, if any */ }
}

See VPN & captive portals for which clients are controllable and what happens when one is not.

warm

Prepare the current venue's networks for instant failover and clean up profiles that turned out to be wrong. Runs automatically in the background while watch or the desktop app is running; this command is the manual trigger.

nomadwifi warm output listing networks prepared for instant failover and where each key came from.

logs and version

$ nomadwifi logs           # the last 40 lines of activity
$ nomadwifi version
NomadWiFi 1.2.0
Native Wi-Fi API: true
State: C:\Users\you\.nomadwifi\state.json

Native Wi-Fi API: false means the Windows WLAN service could not be opened and NomadWiFi has fallen back to parsing netsh. Everything still works, but scans are slower and 6 GHz cannot be told apart from 5 GHz.

Exit codes

CodeMeaning
0The command completed. With --json, check the payload: a failed connect still exits 0 and reports "success": false.
1The command could not run at all — no wireless interface, a scan the driver refused, an unknown command.

agent (the app's protocol)

nomadwifi agent --stdio starts the long-lived engine the desktop app drives. It speaks newline-delimited JSON on stdin and stdout: one request per line, one reply per line, plus unsolicited events. It is documented because it is stable, not because you need it — scripts are better served by --json.

 {"id":1,"method":"scan"}
 {"id":1,"ok":true,"result":[ ... ]}
 {"event":"roamed","ssid":"Hotel_5G","reason":"previous AP stopped carrying traffic"}

Methods: status, scan, optimize, connect, vpn_status, vpn_hold, vpn_resume, warm, set_autoroam, get_autoroam, version, ping. Events: ready, log, roamed.