SimpliFi — router & controller vendors
Module M2 (MikroTik is module M1 — see its own notes). Every vendor is a driver implementing
App\Services\Routers\Contracts\RouterDriver (ARCHITECTURE.md §7.1), registered in
config('simplifi.router_drivers'). The same steps are shown in the app on each device page
(Captive portal setup) and on Routers & Controllers › Watch Tutorial (tenant.devices.guide).
Common to every vendor
- Portal URL — each device has its own captive-portal entry,
Device::portalUrl()(/portal/d/{uuid}on the workspace host), shown with a copy button on the device page. Set it as the hotspot's external portal / splash page. - Walled garden / pre-authentication — customers must reach the portal and the payment pages
before they are online: the workspace host (from the portal URL),
*.paystack.com,*.paystack.co,*.hubtel.comandfonts.bunny.net(listed on the device page). - RADIUS — server =
RADIUS_SERVER_HOST(device page), auth port 1812, accounting 1813, the device's shared secret (reveal on click), accounting on with 5-minute interim updates. Seedocs/RADIUS.mdfor the FreeRADIUS server itself. - After payment / voucher / free trial the portal asks the driver for a
loginAction. Controller vendors are authorised server-side (done); RADIUS vendors get a browser step that logs the client in on the hotspot with the generated username/password. Login addresses coming from the hotspot's redirect are only followed when they point at a private/LAN IP, the device's own host / public IP / hotspot DNS name, or a host listed in the device's Extra login hosts.
Capability matrix
✅ supported · ⚠️ partial / see notes · ❌ not available
| Omada | UniFi | Ruijie / Reyee | Grandstream GWN | Cambium | OpenWrt / Coova / pfSense | Generic RADIUS | |
|---|---|---|---|---|---|---|---|
| Connection modes | controller | controller | radius | radius | radius | radius | radius |
| Grant access after payment | ✅ extPortal/auth | ✅ authorize-guest | ✅ RADIUS user | ✅ RADIUS user | ✅ RADIUS user | ✅ RADIUS user | ✅ RADIUS user |
| Automatic login on the device | ✅ server-side | ✅ server-side | ⚠️ only with a login URL¹ | ✅ EXCAP redirect | ✅ POST to AP | ✅ Coova / openNDS / pfSense | ⚠️ link-login or login URL¹ |
| Time limit | ✅ | ✅ minutes | ✅ Session-Timeout + Expiration | ✅ | ✅ | ✅ | ✅ (if the NAS honours Session-Timeout) |
| Speed limit | ⚠️ controller 6.2.10+ | ✅ up/down kbps | ⚠️ if WISPr is honoured | ✅ WISPr | ✅ WISPr (WIFI_ALLIANCE_MAX_*) | ✅ WISPr | ⚠️ vendor dependent |
| Burst | ❌⁵ | ❌⁵ | ❌⁵ | ❌⁵ | ❌⁵ | ❌⁵ | ⚠️ MikroTik-compatible only⁵ |
| FUP throttle (continue at reduced speed) | ❌⁶ | ❌⁶ | ⚠️ next re-auth⁷ | ⚠️⁷ | ⚠️⁷ | ⚠️⁷ | ⚠️⁷ |
| PPPoE usage (per-subscriber counters) | ❌⁸ | ❌⁸ | ⚠️ accounting⁹ | ⚠️⁹ | ⚠️⁹ | ⚠️⁹ | ⚠️⁹ |
| Data cap | ⚠️ controller 6.2.10+ | ✅ MB | ❌² | ❌² | ❌² | ✅ ChilliSpot-Max-Total-Octets | ⚠️ MikroTik-compatible only |
| Revoke / disconnect | ⚠️ re-authorise for 1 s³ | ✅ unauthorize-guest | ⚠️ next login refused⁴ | ⚠️⁴ | ⚠️⁴ | ⚠️⁴ | ⚠️⁴ |
| Health | ⚠️ controller version | ✅ version, uptime, clients | ⚠️ last accounting | ⚠️ last accounting | ⚠️ last accounting | ⚠️ last accounting | ⚠️ last accounting |
| Live clients | ✅ with Open API | ✅ stat/sta | ✅ radacct | ✅ radacct | ✅ radacct | ✅ radacct | ✅ radacct |
| Access points list | ✅ with Open API | ✅ stat/device | ❌ | ❌ | ❌ | ❌ | ❌ |
| Package sync | not needed | not needed | not needed | not needed | not needed | not needed | not needed |
Polled by devices:poll |
✅ | ✅ | — | — | — | — | — |
- Without a login address the customer sees their username/password and types it on the device's own login page.
- The vendor ignores RADIUS data-limit attributes; SimpliFi still records usage from accounting.
- The documented External Portal API has no "unauthorise" call; SimpliFi re-authorises the client for one second. If a controller version keeps the longer grant, unauthorise manually in Hotspot Manager › Authorized Clients.
- RADIUS revoke deletes the user, so the next login fails; an active session lasts until its
Session-Timeout(a Disconnect-Request needs UDP from the RADIUS box, not cPanel). - Burst only exists in the burst fields of
Mikrotik-Rate-Limit(and in RouterOS queues). Controllers take a single up/down number, andWISPr-Bandwidth-Max-Up/Downhas no burst at all, so a package's burst settings are simply not sent: these devices get the plan rate. MikroTik routers do support it — seedocs/MIKROTIK.md§"Speed: burst and fair use". - Omada's and UniFi's documented APIs cannot change the rate of a client that is already
authorised. SimpliFi therefore does not claim
Capability::FUP_THROTTLEfor them: a package set to "continue at reduced speed" falls back to its stop behaviour on these devices and the reason is written towifi_sessions.meta.fup.message, so the ISP sees why instead of being promised a feature their gear lacks. - The reduced rate is written to
radreply(Mikrotik-Rate-Limit+ WISPr, and the data-limit attributes are removed so the NAS does not cut the customer off instead). The NAS reads those attributes when it authenticates a user, and shared cPanel hosting cannot send a RADIUS Disconnect-Request / CoA, so the new rate takes effect at the customer's next re-authentication — immediately on reconnect, or whenSession-Timeoutelapses. Until then they stay online at the full rate. MikroTik in RADIUS mode is the exception: SimpliFi also kicks the session through cloud sync, which forces the re-authentication at once. Seedocs/RADIUS.md§2. - These are controller-managed access points: they do not terminate PPPoE at all, so there are
no subscriber counters to read and nothing to slow down. SimpliFi claims neither
Capability::PPPOEnorCapability::PPPOE_USAGEfor them. If a PPPoE subscriber is pointed at such a device,pppoe:usageleaves them at full speed and writes the reason topppoe_accounts.meta.fup.message— a monthly subscription is never cut off instead. - A NAS integrated through RADIUS writes
acctinputoctets/acctoutputoctets/acctsessiontimefor whatever authenticates, PPPoE usernames included, sopppoe:usagecan read the counters (Capability::PPPOE_USAGE) if the ISP actually runs PPPoE on the device — SimpliFi does not provision PPPoE secrets on these vendors. How fresh the numbers are is decided by the NAS'sAcct-Interim-Interval, and with accounting switched off there is no PPPoE usage at all. The reduced rate is written toradreplyand applies at the subscriber's next re-dial; the live session is deliberately not disconnected. Seedocs/RADIUS.md§2.2. MikroTik is the full-featured case in every mode (docs/MIKROTIK.md§"PPPoE usage").
TP-Link Omada (controller)
Works with the Omada software controller 5.0.15+, OC200/OC300 and the Omada Cloud-Based Controller. Reference: "API and Code Sample for External Portal Server" (5.0.15 – 6.2.0, 6.2.10+).
- Settings › Authentication › Portal: create a portal for the hotspot SSID, Authentication Type = External Portal Server, Custom Portal Server = the device's portal URL.
- Hotspot Manager › Operators: create a hotspot operator (not the admin account) — its username/password go into SimpliFi.
- Access Control › Pre-Authentication Access: allow the walled-garden hosts above.
- In SimpliFi: controller URL (
https://host:8043for the software controller,https://hostfor OC200/OC300/cloud), controller ID (omadacId, the ID in the URL after you sign in), site name, operator credentials, Controller version. - Optional Open API app (Settings › Platform Integration › Open API, client-credentials mode, site privileges) → client ID / secret in SimpliFi to list access points and connected clients.
- Save, then Test connection (reads
/api/infoand signs in as the operator).
How it works: the EAP redirects to the portal with clientMac, apMac, ssidName, radioId, site, t, redirectUrl (gateways: clientMac, gatewayMac, vid, site, t, redirectUrl). After payment SimpliFi
calls POST /{omadacId}/api/v2/hotspot/login then POST /{omadacId}/api/v2/hotspot/extPortal/auth
(Csrf-Token header + session cookie) with authType 4 and time = time allowed. Units:
controller 5.0.15–6.2.0 = microseconds, 6.2.10+ = milliseconds (choose Controller version);
6.2.10+ also receives downloadRateLimitKbps, uploadRateLimitKbps and totalTrafficLimitBytes.
The vendor documents time both as "time allowed" (sample code) and "expiration time" (field table);
SimpliFi sends a duration by default — if clients are cut off right after paying, switch
Authorisation time to Expiry timestamp.
Ubiquiti UniFi (controller)
UniFi Network Application (classic, https://host:8443) or a UniFi OS console (UDM/UDR/UCG/Cloud
Key Gen2+, https://host). References: ubntwiki UniFi API,
Art-of-WiFi UniFi API client.
- Settings › Profiles › Guest Hotspot (or Hotspot Manager › Portal on newer versions): Authentication: External Portal Server, enter the portal host/IP.
- Pre-Authorization Access: allow the walled-garden hosts above.
- Enable the guest hotspot on the customer SSID.
- Create a local account without 2FA (a Hotspot role is enough) → SimpliFi username/password.
- In SimpliFi: controller URL, site ID (usually
default), console type (auto-detect works for most), then Test connection.
How it works: the AP redirects guests to …/guest/s/{site}/?id=<client mac>&ap=<ap mac>&t=…&url=…&ssid=….
SimpliFi signs in (/api/login, or /api/auth/login + /proxy/network prefix + X-CSRF-Token on
UniFi OS) and calls cmd/stamgr authorize-guest with minutes, up/down (kbps), bytes (MB)
and ap_mac; revoke = unauthorize-guest. Clients from stat/sta, APs from stat/device,
health from stat/sysinfo + stat/health.
Ruijie / Reyee (RADIUS)
Ruijie Cloud's open API (/service/api/…, appid/secret → access_token) is not publicly documented
for external-portal authorisation or client control, so SimpliFi integrates through RADIUS only.
- Ruijie Cloud › Configuration › Authentication: for the SSID choose an external / captive portal that authenticates with RADIUS; portal URL = the device's portal URL.
- RADIUS server = SimpliFi's (device page), shared secret, accounting on.
- Allowed URLs (walled garden): the hosts above.
- In SimpliFi: NAS IP = the public IP of the Ruijie gateway / EG; optional Portal login URL if your portal setup posts credentials to the device.
Customers buy on the SimpliFi portal and receive a username/password; if the redirect carries no login address they type them on the Ruijie login page.
Grandstream GWN (RADIUS + External Captive Portal API)
Reference: Grandstream External Captive Portal API Guide (AP firmware 1.0.11.8+).
- GWN.Cloud / GWN Manager › Captive Portal › Policy: Splash Page = External, external portal URL = the device's portal URL.
- Authentication type: RADIUS Server — SimpliFi's server, ports 1812/1813, the shared secret, PAP.
- Pre Authentication Rules: allow the walled-garden hosts.
- Attach the policy to the SSID. In SimpliFi: NAS IP = the public IP the APs' RADIUS traffic comes from.
How it works: the AP redirects with login_url, ap_mac, client_mac, orig_url, ssid; after payment
the portal redirects the browser to <login_url>?username=…&password=…&redirect=…; the AP then asks
RADIUS (Session-Timeout and WISPr bandwidth are honoured). If login_url is a public host name, add
it to Extra login hosts.
Cambium cnPilot / cnMaestro (RADIUS + external hotspot)
Reference: Cambium "Guest Access Portal Integration — cnPilot E400/E500/ePMP1000 Hotspot".
- cnMaestro / cnPilot › WLAN › Guest Access: Portal mode External Hotspot, external portal URL = the device's portal URL, success action redirect to original URL.
- WLAN RADIUS: SimpliFi's server, ports, shared secret, accounting; NAS-ID as entered in SimpliFi.
- Walled garden / whitelist: the hosts above.
How it works: the AP redirects with ga_ap_mac, ga_nas_id, ga_srvr, ga_cmac, ga_Qv, ga_orig_url;
after payment the browser POSTs ga_user / ga_pass to
http://<ga_srvr>:880/cgi-bin/hotspot_login.cgi (HTTPS: port 444, choose AP login protocol), with
the redirect parameters (including the opaque ga_Qv) echoed back. ga_srvr must be a LAN IP or the
device's host / public IP. cnMaestro's REST API is not used (its reference is not public), so access
points are not listed.
OpenWrt / CoovaChilli / openNDS / pfSense (RADIUS)
CoovaChilli (/etc/chilli/config or chilli.conf):
HS_UAMSERVER / uamhomepage = <portal URL>
HS_RADIUS / radiusserver1 = <RADIUS server>
HS_RADSECRET / radiussecret = <shared secret from the device page>
HS_UAMSECRET / uamsecret = <copy into SimpliFi "CoovaChilli UAM secret">
HS_UAMALLOW / uamallowed = <walled-garden hosts>
Chilli redirects with res=notyet&uamip&uamport&challenge&called&mac&ip&nasid&userurl; after payment
SimpliFi redirects to http://uamip:uamport/logon?username=…&response=<CHAP>&userurl=… (or
password=<PAP> when Login method = PAP), computed exactly like hotspotlogin.cgi
(challenge hashed with the UAM secret). Data caps use ChilliSpot-Max-Total-Octets.
openNDS (FAS level 0): set fasremotefqdn/faspath to the portal; the redirect carries tok,
gatewayaddress, authdir, redir, and SimpliFi sends the browser to
http://<gatewayaddress>/<authdir>/?tok=…&redir=…. openNDS does not use RADIUS, so session length is
openNDS's own sessiontimeout and SimpliFi cannot revoke it.
pfSense captive portal (RADIUS authentication): upload a portal page that forwards to SimpliFi, for example
<meta http-equiv="refresh" content="0; url=https://<portal URL>?portal_action=$PORTAL_ACTION$&zone=$PORTAL_ZONE$&redirurl=$PORTAL_REDIRURL$">
SimpliFi then POSTs auth_user, auth_pass, zone, redirurl, accept=Login back to
$PORTAL_ACTION$ (the pfSense LAN address).
Generic RADIUS (Huawei, Cisco, Aruba, Netgear, Teltonika, …)
- External portal / login page = the device's portal URL; RADIUS server, ports, secret as above.
- If the device sends a login address in the redirect (
link-login,login_url, …), SimpliFi postsusername/password(dst= original URL) to it automatically. Otherwise set Hotspot login URL (and the field names if they differ) on the device, or customers type their credentials.
Adding another vendor
Create app/Services/Routers/Drivers/<Vendor>Driver.php extending
App\Services\Routers\Vendors\VendorDriver (controller API) or RadiusVendorDriver (RADIUS only),
register it in config/simplifi.php (router_drivers, vendor_labels) and in
VendorCatalog, and add a section + matrix column here. Never throw from driver methods — return
ActionResult::fail() (RouterManager also catches and records devices.last_error).
Report only capabilities the hardware really has. Capability::BURST and
Capability::FUP_THROTTLE are read by the package editor to warn the ISP, and
App\Services\Hotspot\FupEnforcer falls back to stopping a customer when FUP_THROTTLE is
missing. Claiming one you cannot honour sells a feature that silently does nothing; leaving one
out only costs a warning in the UI. Implement applyFup() when the vendor can lower the rate of
a connected client (RadiusVendorDriver already does it through radreply), and leave the
AbstractRouterDriver default in place when it cannot.
For PPPoE there are two more: pppoeUsage(Device, array $usernames) returns per-subscriber
counters (Data\PppoeUsage, raw values — Usage\PppoeUsageRecorder turns them into period
totals) and is gated by Capability::PPPOE_USAGE, while applyFupToPppoe() lowers a connected
subscriber's rate. RadiusVendorDriver implements both from accounting / radreply; a vendor
that does not terminate PPPoE leaves the AbstractRouterDriver defaults ([] and
unsupported()), and pppoe:usage then leaves those subscribers at full speed with the reason
recorded instead of disconnecting a monthly subscription.