Documentation

From a flashed router
to a managed device.

Add the feed, build the agent, install it, claim the device. Every command below was run against a live OpenWRT router before it was published.

The feed on GitHub → About ac-client
Step 1

Add the feed

The packages are built from source in your own OpenWRT buildroot or SDK. There is no public binary repository yet, so opkg install ac-client will not find anything — you build it.

# Add the feed to your OpenWRT buildroot or SDK echo 'src-git optimwrt https://github.com/optim-enterprises-bv/optim-wrt.git' >> feeds.conf ./scripts/feeds update optimwrt ./scripts/feeds install -a -p optimwrt
Step 2

Build

ac-client alone gets the router managed. aether-sensord adds traffic classification, app blocking and threat reputation — it reads packet payload and is consented separately.

# Build the agent make package/feeds/optimwrt/ac-client/compile # Optional: traffic classification make package/feeds/optimwrt/aether-sensord/compile # Packages land in bin/packages/<arch>/optimwrt/
Step 3

Install

# BusyBox has no SFTP server, so stream the file ssh root@192.168.1.1 'cat > /tmp/ac-client.apk' < ac-client-0.1.0-r16.apk ssh root@192.168.1.1 'apk add --allow-untrusted /tmp/ac-client.apk' # On older OpenWRT releases this is opkg install instead.

The defaults in /etc/config/optimacs already point at the controller, and the agent ships with a bootstrap certificate — there is no token to paste in. Confirm it connected with logread | grep ac-client; a healthy agent sends a heartbeat every 60 seconds.

Step 4

Find your serial

# The agent logs the endpoint ID it registered with logread | grep -oE 'oui:[0-9A-Fa-f]{6}:[0-9a-f:]{17}' | tail -1 oui:00005A:ea:5e:ca:cf:3f:18 # Not yet logged? Derive it — the OUI is fixed: echo "oui:00005A:$(cat /sys/class/net/br-lan/address)" # Do NOT use uci get optimacs.agent.mac_addr — it is empty # on a normal device (the MAC is auto-detected, not stored).
Step 5

Claim the device

Sign up and verify your email first. Your device list will be empty until you claim — that is expected.

IN THE PORTAL

Open My Network and press Claim a device. Enter your serial, then the code your router shows you. That is the whole flow — the API below is the same two steps if you would rather script it, or are onboarding more than one device.

# 1. Log in and start the claim TOKEN=$(curl -sS -X POST "$BASE/portal/v1/login" \ -H 'Content-Type: application/json' \ -d '{"email":"you@example.com","password":"..."}' \ | sed -n 's/.*"token":"\([^"]*\)".*/\1/p') curl -sS -X POST "$BASE/portal/v1/me/devices/claim" \ -H "Authorization: Bearer $TOKEN" \ -d "{\"serial\":\"$SERIAL\"}" {"pending":true,"expires_in":600} # 2. Read the code off the router logread | grep aether-claim aether-claim: claim code is CEAMJP7X # 3. Confirm it curl -sS -X POST "$BASE/portal/v1/me/devices/claim/confirm" \ -H "Authorization: Bearer $TOKEN" \ -d "{\"serial\":\"$SERIAL\",\"code\":\"CEAMJP7X\"}" {"claimed":true}
WHY A CODE OFF THE ROUTER

A serial is not a secret. It is printed on the box, it appears in DHCP logs, and it is derivable from a MAC address your radio broadcasts in every beacon frame. If a serial were enough, anyone within WiFi range could bind your router to their account and inherit your WiFi settings, your client list and your traffic history. Reading the code requires SSH or LuCI access — which is what owning the router actually means. The code expires in 10 minutes and allows 5 attempts; an already-claimed device is refused rather than transferred.

Gotchas

Things that fail quietly

status_interval

Leave it at 60. It is the only periodic traffic on the WebSocket, so it decides whether the connection survives an idle timeout. Cloudflare closes an idle socket at ~126s; at the old default of 300 a board reconnected roughly 28 times an hour.

ca_file

Must be the public CA bundle. The controller's certificate is publicly issued; pointing this at a private CA breaks the connection at TLS, before anything useful is logged.

iw and tc

Runtime dependencies of the telemetry, not the binary. Strip them from a minimal image and the radio survey and queue statistics report nothing — the RF and bufferbloat views go silently empty.

Radios ship disabled

Stock OpenWRT behaviour, not ours. uci set wireless.radio0.disabled='0' then wifi reload. It is the usual reason a new device shows no RF data.

Full reference, including troubleshooting and per-device certificates, is in the feed README.