Skip to main content
A Tower with a screen or an Ethernet cable is easy to onboard. A headless, WiFi-only box has a chicken-and-egg problem: it needs WiFi credentials before it can be reached. AP-mode onboarding solves it — on first boot with no saved network the box brings up its own access point and captive portal.

The flow

  1. No saved network → the box starts an AP named Naive-XXXX (balena wifi-connect, which drives NetworkManager over DBus and runs a dnsmasq captive portal).
  2. Your phone joins Naive-XXXX; the OS captive-portal detection opens the portal (served by wifi-connect, or the box’s own page at GET /v1/device/portal).
  3. The portal scans nearby WiFi (GET /v1/device/network/scan) and you pick your home network + enter the password.
  4. The box joins it (POST /v1/device/network/join → NetworkManager nmcli dev wifi connect), the AP tears down, and the box comes up on your LAN.
  5. You continue in the normal onboarding wizard at http://tower.local — this AP step runs before Step 1.

Endpoints (self-hosted only, inert in cloud)

All three are gated to NAIVE_BUILD=tower and 404/return empty in cloud.

Install

The AP stack is opt-in (it needs AP-capable WiFi hardware and would disrupt a box already on the LAN):
This installs network-manager + dnsmasq-base and fetches the balena wifi-connect binary for the box’s arch (aarch64 or x86_64). wifi-connect needs NET_ADMIN and an AP-capable WiFi adapter.

What is VM-testable vs. hardware-gated

The web flow + join endpoint are fully testable in a VM with no radio: set NAIVE_WIFI_MODE=mock (or just run a non-Tower build) and the scan returns a synthetic network list and join returns a synthetic success — so the captive portal page and /network/join can be exercised end-to-end in CI.The hardware leg is genuinely not VM-testable — bringing up a real AP, serving the captive portal over it, and joining a real WiFi radio require AP-capable WiFi hardware. That leg is validated on a real device.

Real-device checklist

Run these on real AP-capable hardware (marked BLOCKED until validated on a device — do not assume they pass from the VM subset):
  • Boot headless with no saved WiFi → AP Naive-XXXX appears.
  • Phone joins the AP → captive portal auto-opens (iOS + Android).
  • Portal scan lists real nearby SSIDs (nmcli dev wifi list parity).
  • Selecting a network + password joins it (nmcli dev wifi connect).
  • The AP tears down and the box is reachable at http://tower.local.
  • A wrong password surfaces a clear error and the AP stays up for a retry.
  • Reboot after a successful join reconnects automatically (no AP).