← CLI Install Guide

Remote / Headless Linux Deployment

Install and run the howismywifi CLI hands-off on a remote, off-VPN box — a partner test machine, a lab mini-PC — for long unattended captures. One command to install, one line to run detached and uploading.

The whole thing, two commands

On the box (Linux or macOS, no VPN, no Git, no clone needed):

# 1. install everything (venv + wheel + video deps + Chromium), then preflight
$ curl -fsSL https://howismywifi.com/download/install.sh | bash

# 2. run a 24h video capture, detached + uploading (single line)
$ nohup ~/wm-venv/bin/wifi-capture --schedule more-video --duration 24h --allow-metered --upload --location "Site-Name" --network "WiFi-Name" > wm-run.log 2>&1 &

1. Install

The bootstrap installer picks a Python 3.12+ (fetching one via uv if the system Python is older), creates a venv at ~/wm-venv, installs the published self-contained wheel with the [field] extra (video + local analysis), installs the Chromium browser, and runs preflight:

$ curl -fsSL https://howismywifi.com/download/install.sh | bash

Environment overrides: WM_VENV (install location), WM_SKIP_BROWSER=1 (latency-only box, no video), WM_WHEEL_URL (pin/roll back to a specific wheel — see §7). Prefer to do it by hand, or want a developer checkout? See the CLI Install Guide.

Latency-only box (no video)? Skip the ~150 MB Chromium download entirely — useful on a bandwidth- or RAM-constrained in-flight box that only needs ping/loss/throughput:
$ curl -fsSL https://howismywifi.com/download/install.sh | WM_SKIP_BROWSER=1 bash
Behind a captive portal (in-flight Wi-Fi)? Authenticate through the portal in a browser first. Until you do, the network intercepts HTTPS — curl … | bash would pipe the portal's login page into your shell, and pip/playwright downloads fail against the intercepting proxy. Re-run the installer once you're actually online.
Why not git clone? The documented developer install needs VPN + GHE auth + Git — none of which a partner/test box has. The wheel is public and self-contained (it bundles the upload credentials), so --upload works off-VPN the moment it's installed.
Updating a box that's already installed. Every release has a new version number. The simplest update is to re-run the same installer:
$ curl -fsSL https://howismywifi.com/download/install.sh | bash
Use the installer, not a bare pip line, on any box that does video testing. Upgrading the [field] extra also re-resolves playwright (capped at <2, so it can move to a newer 1.x). A newer Playwright expects a newer Chromium revision than the one already on disk, and the bare pip line does not re-run playwright install — so the video probes just stop producing, with no error at install time. A headless box can't self-heal this: the deferred Chromium fetch is TTY-gated and never fires under systemd or nohup. The installer re-runs playwright install --only-shell chromium, which is a no-op when the revision already matches and a repair when it doesn't.
$ ~/wm-venv/bin/pip install --upgrade "wifi-monitor[field] @ https://howismywifi.com/download/wifi_monitor-0.1.6-py3-none-any.whl"
$ ~/wm-venv/bin/python -m playwright install --only-shell chromium   # do NOT skip this
(Only safe to omit the second line on a box with no video testing, or one you are about to re-run the installer on anyway.) There is no auto-update (unlike the desktop apps) — a box keeps running its installed build until you re-install, which is usually what you want mid-capture. You don't have to track builds yourself, though: when you run wifi-capture interactively on a box that's behind, it prints a one-line “newer build available” notice at startup (checked at most once a day; silent for headless / nohup and app --json runs so it never disturbs a detached capture). Silence it entirely with export WM_NO_UPDATE_CHECK=1. Because it's silent on headless runs, a box that only ever runs under nohup will never surface it on its own — to check on purpose, run ~/wm-venv/bin/wifi-capture --preflight interactively over your SSH/AnyDesk session; it prints the same notice if the box is behind. Analysis of your uploads is server-side, so a box a few builds behind still produces correct results-page stats; re-install when you need a specific capture-time fix (a probe/upload/crash fix). Drop [field] for a latency-only box with no video deps.

2. Preflight — confirm the box is ready

Before committing a box to a day-long run, check it:

$ ~/wm-venv/bin/wifi-capture --preflight

It prints one green/red line each for: Python version, Playwright + Chromium can navigate (not just launch), free RAM, CPU cores, disk space, the per-interface byte counter (for budget-video), and reachability of the upload endpoint. Exit code is non-zero if anything fails, so a script can gate on it. This catches the failures that are otherwise silent:

3. Run detached & uploading

Use nohup … & so the run survives your SSH/AnyDesk session closing. Headless runs skip the interactive tag prompt automatically, but pass --location/--network anyway so the capture is labeled:

$ nohup ~/wm-venv/bin/wifi-capture --schedule more-video --duration 24h --allow-metered --upload --location "Site-Name" --network "WiFi-Name" > wm-run.log 2>&1 &
$ pgrep -cf bin/wifi-capture   # confirm exactly one is running → 1
Two easy-to-miss traps.

4. Pick a schedule

ScheduleLinkUse it for
more-videounmeteredOffice / lab link with no data cap — dense back-to-back video that fills --duration. Needs --allow-metered + --duration.
budget-videometeredCellular / capped SIM — evenly-sampled video under a hard --data-budget. macOS/Linux only. See Video Data-Cap Testing.
defaultanyNormal characterization — no --schedule flag needed.

5. Resource requirements

6. Very long captures & the analysis limit

The server analyzes uploaded captures up to a size limit. Most day-long runs are well under it, but a very long capture (multi-day) can exceed it — the results page then shows blank stats until they're generated. Recover it from the same box, no admin access needed:

$ ~/wm-venv/bin/wifi-manage reanalyze <capture-id>

You don't have to watch for this: the CLI prints this exact command (with the id filled in) at upload time whenever a capture crosses the limit. The capture is safe on the server the whole time — reanalyze only regenerates the stats.

7. Roll back a bad build, or pin a box

A re-install (or the startup notice) moves a box to the latest build. If that build misbehaves — a probe regression, a dependency that broke on rebuild — you can put a box back on a known-good build. Every published build is also archived at an immutable, per-build URL that is never overwritten:

https://howismywifi.com/download/archive/<build-id>/wifi_monitor-<version>-py3-none-any.whl

The <build-id> is the short git sha the notice shows as installed: / latest:, and the last-published one is printed by the maintainer's publish step (ask the maintainer for the archive URL of the build you want). Roll a box back — or pin one that must not move — by pointing WM_WHEEL_URL at that URL and re-running the installer:

$ WM_WHEEL_URL=https://howismywifi.com/download/archive/<build-id>/wifi_monitor-<version>-py3-none-any.whl \
    curl -fsSL https://howismywifi.com/download/install.sh | bash
A pinned box still tells you when a newer build exists, but the notice says PINNED and won't nag you to move — it knows you chose this build. To later return to latest, just re-run the plain installer (curl … install.sh | bash with no WM_WHEEL_URL); that drops the pin.
The notice won't clear after I re-installed? If re-running the installer doesn't change the installed: build id, the published set may be mid-update (a partial publish) — wait a few minutes and try again, or pin a specific known-good build with WM_WHEEL_URL as above.

8. Checklist