Network Management
The Network Management module is the core module - no dependencies - that gives you a unified interface for switch port management, VLAN operations, wireless networks, topology visualization, firmware lifecycle, and controller config backup across all connected network controllers.
The reference adapter is Omada (TP-Link) - production, gold-standard, tested against real hardware. UniFi is beta. Every configuration write is staged by default and requires an explicit apply step before touching a live device. This is not optional during initial setup - see Write safety and staging.
Supported adapters
Section titled “Supported adapters”| Adapter | Tier | Notes |
|---|---|---|
| Omada (TP-Link) | Production / gold standard | write-capable with staging gate; full VLAN/WiFi/port/AP/hotspot/802.1X surface; deepest adapter in the codebase |
| UniFi (Ubiquiti) | Beta | Read paths + dual-gated writes; not yet field-tested against real Ubiquiti hardware |
For firewall/router adapters - OPNsense, pfSense, MikroTik, OpenWRT - see Firewall.
Write safety and staging
Section titled “Write safety and staging”Every configuration write routes through the staged dual-gate:
- Read-only gate -
ADAPTER_READ_ONLY(andOMADA_READ_ONLY) both default totrueon new installs (safe by default). While either istrue, the Omada client layer raisesOmadaApiErroron any mutating HTTP verb, and the standard network endpoint router (_push_vlan_to_controller,_push_wifi_to_controller) catches that error silently - the database change commits, but the controller is not written. No row is queued inadapter_pending_changes; the push is simply skipped with a warning log. - Apply gate - the formal staged-change pipeline (
adapter_pending_changes/AdapterStagingService) is used by the shared gateway-vpn apply dispatcher (POST /api/v1/gateway-vpn/changes/{change_id}/applywith{"force": true}) and by all firewall/gateway adapter endpoint families - OPNsense, pfSense, MikroTik, OpenWrt, Proxmox, UniFi, and Omada write endpoints all stage throughAdapterStagingServiceand apply via this dispatcher. The standard VLAN/WiFi/port paths in the network module do not use this pipeline - they push directly and live to the controller.
When ADAPTER_READ_ONLY is disabled (false), standard VLAN/WiFi writes push directly and live to the controller. There is no pending-changes queue for these paths; the controller sync or next scheduled poll reconciles any drift.
The Omada raw passthrough (/api/v1/gateway-raw/{controller_id}/call) also enforces this gate
and blocks sensitive paths regardless of force - /admin, /users, /roles,
/cmd/backup, /maintenance/, /setting/system/sslcert, and others - so the escape hatch
cannot bypass the staging audit trail.
Connecting a controller
Section titled “Connecting a controller”1. Register the controller
Section titled “1. Register the controller”ControllerCreate accepts inline credentials (username / password). There is no
credential_id field and no separate credential-store step for controller registration.
Required fields: name, controller_type, host, port, site_id.
Optional: username, password, use_ssl, verify_ssl, sync_enabled,
sync_interval_seconds, config, site_mappings.
Omada:
curl -s -X POST https://<freesdn>/api/v1/controllers/ \ -H "Cookie: freesdn_access=<token>" \ -H "X-CSRF-Token: <csrf>" \ -H "Content-Type: application/json" \ -d '{ "name": "omada-1", "controller_type": "omada", "host": "https://omada.lan", "port": 8043, "site_id": "<site_uuid>", "username": "admin", "password": "<pass>" }'UniFi:
curl -s -X POST https://<freesdn>/api/v1/controllers/ \ -H "Cookie: freesdn_access=<token>" \ -H "X-CSRF-Token: <csrf>" \ -H "Content-Type: application/json" \ -d '{ "name": "unifi-1", "controller_type": "unifi", "host": "https://unifi.lan", "port": 8443, "site_id": "<site_uuid>", "username": "admin", "password": "<pass>" }'2. Test and sync
Section titled “2. Test and sync”# Test connectivity against stored credentialscurl -s -X POST https://<freesdn>/api/v1/controllers/<controller_id>/test \ -H "Cookie: freesdn_access=<token>" -H "X-CSRF-Token: <csrf>"
# Pull inventory into FreeSDNcurl -s -X POST https://<freesdn>/api/v1/controllers/<controller_id>/sync \ -H "Cookie: freesdn_access=<token>" -H "X-CSRF-Token: <csrf>"3. Probe remote sites (multi-site controllers)
Section titled “3. Probe remote sites (multi-site controllers)”Omada and UniFi controllers can manage multiple sites. Map their remote sites to FreeSDN sites before you start syncing:
# List remote sites visible on the controllerGET /api/v1/controllers/<controller_id>/remote-sites
# Map remote-site IDs to FreeSDN site UUIDsPUT /api/v1/controllers/<controller_id>/site-mappingsSwitches
Section titled “Switches”The switch detail view (/switches/:deviceId/:tab) has twelve tabs driven by the URL.
Switch inventory endpoints
Section titled “Switch inventory endpoints”| Method | Path | Permission | Purpose |
|---|---|---|---|
GET | /api/v1/switches/ | network:read | Paginated list with aggregated port/PoE stats |
GET | /api/v1/switches/{id} | network:read | Detailed single-switch info |
POST | /api/v1/switches/{id}/refresh | network:write | Pull live state from controller into DB |
Port control
Section titled “Port control”| Method | Path | Permission | Purpose |
|---|---|---|---|
GET | /api/v1/switches/{id}/ports | network:read | List all ports (SwitchPortOut) |
GET | /api/v1/switches/{id}/ports/{n} | network:read | Single port detail |
PATCH | /api/v1/switches/{id}/ports/{n} | network:write | Full port config (see fields below) |
POST | /api/v1/switches/{id}/ports/{n}/toggle | network:write | Enable / disable port |
POST | /api/v1/switches/{id}/ports/{n}/poe | network:write | Enable / disable PoE |
POST | /api/v1/switches/{id}/ports/{n}/poe/cycle | network:write | Power-cycle PoE (disable → wait → enable) |
POST | /api/v1/switches/{id}/ports/bulk | network:write | Bulk-update multiple ports |
The PATCH port body accepts: name, enabled, speed, duplex, mtu, flow_control,
vlan_config (native/tagged), poe_config, and stp_config (mode, guard, BPDU protection).
It also accepts security_config for 802.1X mode (auto / force_auth / force_unauth /
disable), MAC limit, and violation action.
VLAN port matrix
Section titled “VLAN port matrix”The interactive U (untagged) / T (tagged) / empty matrix lets you assign VLAN membership to every port in one operation.
| Method | Path | Permission | Purpose |
|---|---|---|---|
GET | /api/v1/switches/{id}/vlans | network:read | VLANs on this switch’s controller |
PUT | /api/v1/switches/{id}/vlans/port-assignments | network:write | Bulk-assign VLAN memberships to ports |
Large switches (48+ ports × 20+ VLANs) render the matrix with TanStack Virtual to avoid DOM overhead.
LAG (Link Aggregation Groups)
Section titled “LAG (Link Aggregation Groups)”| Method | Path | Permission | Purpose |
|---|---|---|---|
GET | /api/v1/switches/{id}/lags | network:read | List LAG groups |
POST | /api/v1/switches/{id}/lags | network:write | Create LAG |
PUT | /api/v1/switches/{id}/lags/{lag_id} | network:write | Update LAG (member ports, LACP/static) |
DELETE | /api/v1/switches/{id}/lags/{lag_id} | network:write | Delete LAG |
Port mirroring
Section titled “Port mirroring”| Method | Path | Permission | Purpose |
|---|---|---|---|
GET | /api/v1/switches/{id}/mirror | network:read | Port mirror config |
PUT | /api/v1/switches/{id}/mirror | network:write | Set mirror session, source, and destination |
STP, ACL, and IGMP
Section titled “STP, ACL, and IGMP”| Method | Path | Permission | Purpose |
|---|---|---|---|
GET / PUT | /api/v1/switches/{id}/stp | network:read / network:write | STP/RSTP global config (mode, priority, hello, max-age) |
GET / POST | /api/v1/switches/{id}/acl | network:read / network:write | ACL rules |
PUT / DELETE | /api/v1/switches/{id}/acl/{rule_id} | network:write | Update / delete ACL rule |
GET / PUT | /api/v1/switches/{id}/igmp | network:read / network:write | IGMP snooping config |
POST | /api/v1/switches/{id}/ports/{n}/isolation | network:write | Port isolation toggle |
Advanced per-port controls
Section titled “Advanced per-port controls”| Method | Path | Purpose |
|---|---|---|
POST | .../ports/{n}/flow-control | 802.3x flow control |
POST | .../ports/{n}/speed | Link speed / duplex |
POST | .../ports/{n}/loopback-detect | Loopback detection |
POST | .../ports/{n}/bandwidth | Bandwidth / rate limiting |
POST | .../ports/{n}/storm-control | Storm-control thresholds |
POST | .../ports/{n}/lldp | Enable / disable LLDP-MED |
All require network:write.
PoE schedules
Section titled “PoE schedules”Requires the network.advanced_poe feature flag (default OFF).
| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/switches/{id}/poe-schedules | List PoE schedules |
POST | /api/v1/switches/{id}/poe-schedules | Create schedule |
PUT | /api/v1/switches/{id}/poe-schedules/{schedule_id} | Update schedule |
DELETE | /api/v1/switches/{id}/poe-schedules/{schedule_id} | Delete schedule |
Switch diagnostics
Section titled “Switch diagnostics”| Method | Path | Purpose |
|---|---|---|
POST | /api/v1/switches/{id}/diagnostics/cable-test | Cable diagnostic on a port |
POST | /api/v1/switches/{id}/diagnostics/ping | Ping from device to target |
POST | /api/v1/switches/{id}/diagnostics/traceroute | Traceroute from device |
Switch profiles
Section titled “Switch profiles”There are two distinct kinds of profiles - be precise about which you mean:
| Kind | Prefix | Notes |
|---|---|---|
| DB-backed port profiles | /api/v1/switches/profiles* | Stored in FreeSDN DB; GET/POST/PUT/PATCH/DELETE |
| Controller-side port profiles | /api/v1/switches/{id}/port-profiles* | Pushed to the controller; GET/POST/PUT/DELETE |
You can also apply a batch CLI configuration profile to multiple ports with
POST /api/v1/switches/{id}/cli-profile/apply.
Visibility: MAC table, LLDP, events, clients
Section titled “Visibility: MAC table, LLDP, events, clients”| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/switches/{id}/mac-table | MAC address table |
GET | /api/v1/switches/{id}/lldp | LLDP neighbors (from port metadata) |
GET | /api/v1/switches/{id}/events | Recent controller events for this device |
GET | /api/v1/switches/{id}/alerts | Active controller alerts |
GET | /api/v1/switches/{id}/clients | Connected clients |
Routing, DHCP snooping, QoS (read-heavy)
Section titled “Routing, DHCP snooping, QoS (read-heavy)”These endpoints read site-level config from the controller; DHCP snooping and QoS have
corresponding PUT write paths:
GET /api/v1/switches/{id}/routes - static routes (read-only)
GET /api/v1/switches/{id}/dhcp - site DHCP config
GET|PUT /api/v1/switches/{id}/dhcp/snooping - DHCP snooping
GET|PUT /api/v1/switches/{id}/qos - site QoS
OUI-VLAN auto-assignment
Section titled “OUI-VLAN auto-assignment”POST /api/v1/switches/{id}/oui-vlan/apply matches connected-client MAC OUIs to VLAN
mappings and configures each client port’s native VLAN automatically. Body:
{ "mappings": [ { "oui_prefix": "AA:BB:CC", "vlan_id": 20, "description": "Printers" } ]}Running config and config history
Section titled “Running config and config history”GET /api/v1/switches/{id}/running-config pulls the running config for backup or diff
comparison. The Config History tab in the switch detail view shows a diff viewer across
snapshots.
VLANs are controller-scoped in the network.vlans table (Network model). This is the
Layer 0 truth - what a specific controller currently knows. Site-wide desired state
(gateway.gw_canonical_vlans) is a Layer 2 concept managed by the Firewall module.
VLAN CRUD
Section titled “VLAN CRUD”| Method | Path | Permission | Purpose |
|---|---|---|---|
GET | /api/v1/network/vlans | authenticated | List VLANs; site_id?, skip, limit |
GET | /api/v1/network/vlans/{id} | authenticated | Get one VLAN |
POST | /api/v1/network/vlans | config:write | Create VLAN (201) |
PATCH | /api/v1/network/vlans/{id} | config:write | Update + best-effort controller push |
DELETE | /api/v1/network/vlans/{id} | config:write | Soft-delete + push delete to controller |
Create body fields: vlan_id (1-4094), name (1-128 chars), description?, site_id?,
dhcp_enabled, dhcp_start, dhcp_end, gateway, subnet_mask.
Cross-controller alignment (Layer 1)
Section titled “Cross-controller alignment (Layer 1)”When a site has multiple controllers, VLANs can drift out of sync. Use the alignment endpoints to detect and fix this:
# See alignment matrix - always safe, never mutatesGET /api/v1/network/vlans/alignment?site_id=<uuid>Response includes a per-controller present/absent/differs flag and an alignment_score (0-100).
The matrix only provides meaningful data with two or more controllers at the same site.
# Push a VLAN from a source controller to target controllersPOST /api/v1/network/vlans/distribute{ "source_network_id": "<uuid>", "target_controller_ids": ["<uuid>", "<uuid>"]}This is a direct write subject to the adapter read-only gate. If ADAPTER_READ_ONLY=true, the push to each target controller is blocked and no DB record is written for that target. Per-target success/fail results are returned; a partial failure does not roll back successful targets.
The VlansPage alignment tab shows the VLAN × controller matrix with per-cell “Copy” buttons
that call distribute on a single target.
Access points and WiFi
Section titled “Access points and WiFi”AP inventory and detail
Section titled “AP inventory and detail”| Method | Path | Permission | Purpose |
|---|---|---|---|
GET | /api/v1/access-points/ | network:read | Paginated AP list with radio/client summary |
GET | /api/v1/access-points/{id} | network:read | AP detail including live controller data |
GET | /api/v1/access-points/{id}/clients | network:read | Clients connected to this AP |
GET | /api/v1/access-points/{id}/firmware | network:read | Firmware update status |
PATCH | /api/v1/access-points/{id}/name | network:write | Update display name (pushes to controller) |
Radio configuration
Section titled “Radio configuration”| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/access-points/{id}/radios | Radio config for all bands |
PATCH | /api/v1/access-points/{id}/radios/{band} | Update radio settings for band ∈ {2g, 5g, 5g2, 6g} |
Radio settings include channel, TX power, channel width, DFS, and band steering.
SSIDs (per-AP overrides)
Section titled “SSIDs (per-AP overrides)”| Method | Path | Permission | Purpose |
|---|---|---|---|
GET | /api/v1/network/wifi | authenticated | List SSIDs; site_id?, enabled?, paging |
POST | /api/v1/network/wifi | config:write | Create SSID; requires site_id |
PATCH | /api/v1/network/wifi/{id} | config:write | Update + controller push |
DELETE | /api/v1/network/wifi/{id} | config:write | Soft-delete + controller push |
POST | /api/v1/network/wifi/{id}/toggle | config:write | Enable / disable SSID; body {"enabled": true} |
GET | /api/v1/access-points/{id}/ssid-overrides | network:read | Per-AP SSID enable/disable overrides |
PUT | /api/v1/access-points/{id}/ssid-overrides | network:write | Set per-AP SSID overrides |
SSID create body: ssid (1-32 chars), security (default wpa2_personal; valid values:
open, wep, wpa_personal, wpa2_personal, wpa3_personal, wpa_wpa2_personal,
wpa2_wpa3_personal, wpa2_enterprise, wpa3_enterprise - all underscore-separated;
sending an unrecognised value returns 422), password?
(max 63 chars; WPA2 requires ≥ 8 chars as a protocol constraint, but the API schema does not enforce a minimum - short passwords are forwarded to the adapter unchanged), vlan_id? (1-4094), hidden, enabled, band (default both),
client_isolation, band_steering, fast_roaming, rate_limit_enabled/up/down.
LAN port, mesh, and LED
Section titled “LAN port, mesh, and LED”| Method | Path | Purpose |
|---|---|---|
GET / PATCH | /api/v1/access-points/{id}/lan-port | LAN port VLAN tagging and PoE passthrough |
PATCH | /api/v1/access-points/{id}/mesh | Enable / disable mesh; body {"enabled": true} |
PATCH | /api/v1/access-points/{id}/led | LED mode: 0=off, 1=on, 2=site_settings |
Adopt, forget, and firmware
Section titled “Adopt, forget, and firmware”| Method | Path | Purpose |
|---|---|---|
POST | /api/v1/access-points/{id}/adopt | Adopt a pending AP; sets status ADOPTING |
POST | /api/v1/access-points/{id}/forget | Forget / remove AP; sets status UNKNOWN |
POST | /api/v1/access-points/{id}/upgrade | Trigger firmware upgrade |
POST | /api/v1/access-points/{id}/reboot | Reboot AP |
POST | /api/v1/access-points/{id}/locate | Flash LEDs to locate AP |
GET | /api/v1/access-points/{id}/rf-scan | RF scan results |
All AP write endpoints require network:write.
Controllers
Section titled “Controllers”Controllers are the upstream systems FreeSDN reads from and pushes to. The controller detail page and these endpoints cover advanced wireless functions, hotspot, 802.1X, batch ops, firmware lifecycle, and config backup.
Controller CRUD
Section titled “Controller CRUD”| Method | Path | Permission | Purpose |
|---|---|---|---|
GET | /api/v1/controllers/ | controller:read | Paginated list |
GET | /api/v1/controllers/{id} | controller:read | Single controller with stats |
POST | /api/v1/controllers/ | controller:create | Add controller (201) |
PATCH | /api/v1/controllers/{id} | controller:update | Update controller |
DELETE | /api/v1/controllers/{id} | controller:delete | Delete controller (204) |
POST | /api/v1/controllers/{id}/test | controller:update | Test stored-credential connection |
POST | /api/v1/controllers/test-connection | controller:create | Test connection pre-creation (raw creds) |
POST | /api/v1/controllers/{id}/sync | controller:update | Sync inventory from controller |
Hotspot and captive portal
Section titled “Hotspot and captive portal”| Method | Path | Purpose |
|---|---|---|
GET / PUT | /api/v1/controllers/{id}/hotspot | Hotspot config |
GET / PUT | /api/v1/controllers/{id}/captive-portal | Captive portal config |
GET | /api/v1/controllers/{id}/hotspot/vouchers | List vouchers |
POST | /api/v1/controllers/{id}/hotspot/vouchers | Create vouchers (201) |
DELETE | /api/v1/controllers/{id}/hotspot/vouchers/{voucher_id} | Delete voucher |
All require controller:update.
802.1X / RADIUS
Section titled “802.1X / RADIUS”| Method | Path | Purpose |
|---|---|---|
GET / PUT | /api/v1/controllers/{id}/dot1x | 802.1X site-wide config |
GET | /api/v1/controllers/{id}/dot1x/events | Recent authentication events |
GET / PUT | /api/v1/controllers/{id}/dot1x/radius | RADIUS auth + accounting servers (primary/secondary, ports, shared secrets) |
GET | /api/v1/controllers/{id}/dot1x/stats | Success / failure counts |
RF health and rogue-AP detection
Section titled “RF health and rogue-AP detection”| Method | Path | Purpose |
|---|---|---|
GET / PUT | /api/v1/controllers/{id}/wifi/radio-settings | Site-wide channel plan, TX power, DFS, band steering |
GET | /api/v1/controllers/{id}/wifi/channel-utilization | Per-AP/band utilization (RF Health tab) |
GET | /api/v1/controllers/{id}/wifi/rogue-aps | Rogue-AP detection results |
Batch operations
Section titled “Batch operations”| Method | Path | Permission | Purpose |
|---|---|---|---|
POST | /api/v1/controllers/{id}/batch/reboot | controller:update + site_admin role | Reboot multiple devices; logged to freesdn.security.batch_op |
POST | /api/v1/controllers/{id}/batch/firmware-upgrade | controller:update + site_admin role | Bulk firmware upgrade |
POST | /api/v1/controllers/{id}/batch/firmware-check | controller:update | Check available updates fleet-wide |
Firmware lifecycle
Section titled “Firmware lifecycle”| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/controllers/{id}/firmware | Available firmware versions |
GET | /api/v1/controllers/{id}/firmware/overview | Counts per version + update availability |
GET | /api/v1/controllers/{id}/firmware/history | Upgrade history log |
Health, logs, and config backup
Section titled “Health, logs, and config backup”| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/controllers/{id}/health | Controller health |
GET | /api/v1/controllers/{id}/logs | Controller system logs |
POST | /api/v1/controllers/{id}/backup | Trigger controller config backup |
GET | /api/v1/controllers/{id}/topology | Topology data from the controller |
Topology
Section titled “Topology”The topology graph is a React Flow canvas at /topology. It has three distinct API surfaces -
know which one the frontend is calling:
| Router | Path prefix | Notes |
|---|---|---|
| Module router | GET /api/v1/network/topology | Nodes + links, site_id? |
| Endpoint router | GET /api/v1/network/topology | Also nodes + links (UI fallback) |
| Topology router | /api/v1/topology/* | Frontend TopologyPage uses this one; includes saved layouts |
Topology router endpoints
Section titled “Topology router endpoints”| Method | Path | Permission | Purpose |
|---|---|---|---|
GET | /api/v1/topology/graph | device:read | Full topology graph (TopologyGraphResponse) |
GET | /api/v1/topology/layout/{site_id} | device:read | Saved node positions for a site (nullable) |
PUT | /api/v1/topology/layout/{site_id} | device:write | Save / update node positions |
POST | /api/v1/topology/auto-layout/{site_id} | device:write | Auto-arrange layout |
DELETE | /api/v1/topology/layout/{site_id} | device:write | Delete saved layout (204) |
Discovery
Section titled “Discovery”| Method | Path | Permission | Purpose |
|---|---|---|---|
POST | /api/v1/network/discovery/start | device:update | Start device discovery Celery task; site_id?, subnet? (202) |
GET | /api/v1/network/discovery/status | device:read | Poll Celery progress; task_id required |
POST | /api/v1/network/topology/refresh | device:update | Dispatch discover_all_devices Celery task (202) |
Clients
Section titled “Clients”| Method | Path | Permission | Purpose |
|---|---|---|---|
GET | /api/v1/network/clients | authenticated | List clients; site_id?, paging |
GET | /api/v1/network/clients/{id} | authenticated | Single client |
GET | /api/v1/network/clients/stats/summary | device:read | Summary counts; site_id? |
POST | /api/v1/network/clients/{id}/block | device:update | Block client; sets blocked=True + calls adapter.block_client(mac) best-effort |
POST | /api/v1/network/clients/{id}/unblock | device:update | Unblock client; clears flag + calls adapter.unblock_client(mac) |
Cross-cutting actions
Section titled “Cross-cutting actions”The actions router provides a small set of imperative device operations:
| Method | Path | Permission | Purpose |
|---|---|---|---|
POST | /api/v1/actions/poe/cycle | device:action | Cycle PoE on a switch port; body {device_id, port (1-48), duration (1-30s)} |
POST | /api/v1/actions/wifi/ssid/toggle | controller:action | Enable / disable SSID; body {controller_id, ssid_name, enabled} |
POST | /api/v1/actions/reboot | device:admin | Reboot a device; body {device_id} |
POST /api/v1/actions/poe/cycle also publishes an action.poe_cycle event to the event bus.
Permissions reference
Section titled “Permissions reference”Different routers use different permission strings. This is a real implementation seam, not a documentation error.
| Router | Read permission | Write permission | Admin/delete |
|---|---|---|---|
Module api.py (/network/*) | device:read | device:update | device:admin |
Endpoint network.py - VLAN/WiFi/port-VLAN | authenticated | config:write | config:write |
Endpoint network.py - port PoE/patch, client block | authenticated | device:update | - |
switches.py | network:read | network:write | - |
access_points.py | network:read | network:write | - |
controllers.py | controller:read | controller:update | controller:delete; batch ops also require site_admin role |
actions.py - PoE cycle / SSID toggle | - | device:action / controller:action | device:admin (reboot) |
topology.py | device:read | device:write | - |
The module manifest declares network.view, network.manage, network.vlan.manage,
network.wifi.manage, network.poe.control, and network.firmware.upgrade as its public
permission names, which map onto the role hierarchy.
Omada passthrough routers
Section titled “Omada passthrough routers”These routers mount directly at /api/v1/ and provide typed or raw access to Omada-specific
API surfaces. They are Omada-only and not vendor-neutral FreeSDN features.
| Path prefix | Source file | Coverage |
|---|---|---|
/api/v1/gateway-wifi/* | adapter_omada_wifi.py | WLAN groups, SSIDs, surveillance-VLAN, walled-garden, voucher templates, WIDS/WIPS events |
/api/v1/gateway-switch-advanced/* | adapter_omada_switch_advanced.py | Switch configs, mirror sessions, per-port jumbo frame |
/api/v1/gateway-hotspot/* | adapter_omada_hotspot.py | Operators, SMS gateway, portal form fields, free-auth policies |
/api/v1/gateway-firmware/* | adapter_omada_firmware.py | Firmware lists, checks |
/api/v1/gateway-bulk/* | adapter_omada_bulk.py | Bulk read + bulk action |
/api/v1/gateway-diagnostics/* | adapter_omada_diagnostics.py | Gateway speed-test, sessions |
/api/v1/gateway-raw/{controller_id}/call | adapter_omada_raw.py | Power-user raw passthrough (see below) |
The raw passthrough requires controller:write. Write methods are blocked when ADAPTER_READ_ONLY
is true unless force=true is passed in the body. A hardcoded blocklist prevents bypassing the
staging audit trail regardless of force:
/admin /users /roles /cmd/backup /maintenance/ /setting/system/sslcert …Frontend routes
Section titled “Frontend routes”| Route | Page | Notes |
|---|---|---|
/network | NetworkDashboardPage | Overview widgets |
/topology | TopologyPage (enterprise) | React Flow canvas; uses /topology/* API |
/switches | SwitchesPage | Paginated list; select → detail |
/switches/:deviceId/:tab | SwitchesPage detail | Tabs: overview, ports, vlans, lags, profiles, network, config, config-history, logs, clients, diagnostics, advanced |
/access-points | AccessPointsPage | Paginated list; select → detail |
/access-points/:deviceId/:tab | AccessPointsPage detail | Tabs: overview, radios, ssids, clients, config, rf-health, rogue-aps, radio-settings |
/vlans or /network/vlans | VlansPage | Tabs: vlans (CRUD), alignment (VLAN × controller matrix) |
/wifi or /network/wifi | WifiNetworksPage | SSID list + create/edit |
/network/clients | NetworkClientsPage | Client table with block/unblock |
/poe | PoEPage | PoE budget + scheduling |
Fabric integration
Section titled “Fabric integration”The Network module participates in the Fabric universal app-interconnect:
- Operation
network.client.list- read connected clients (org-scoped, optionalsite_idandis_onlinefilter, up to 200 results); permissionnetwork.view. Use as a mid-chain step in Fabric Connections. - Events emitted -
network.vlan.created,network.vlan.updated,network.vlan.deleted,network.wifi.created,network.wifi.updated,network.wifi.deleted(NATIVE tier). Wire these as triggers in the Fabric Connections builder.
Module settings and feature flags
Section titled “Module settings and feature flags”| Setting | Default | Notes |
|---|---|---|
discovery_interval | 300 s | Configures the interval used when discovery is manually triggered; background auto-polling is not currently active (tasks are commented out) |
topology_refresh | 600 s | Configures the refresh interval used when topology refresh is manually triggered; automatic periodic rebuild is not currently active (tasks are commented out) |
traffic_retention_days | 30 | Days of port traffic data kept in TimescaleDB |
auto_backup_enabled | true | Currently not wired to any background task. Use POST /api/v1/controllers/{id}/backup to trigger backups manually. |
backup_retention_count | 5 | Number of config snapshots to retain per device |
poe_budget_warning_threshold | 80 % | PoE budget warning level |
| Feature flag | Default | Notes |
|---|---|---|
network.advanced_poe | OFF | Enables PoE scheduling and budget endpoint |
network.topology_auto | ON | Auto topology discovery |
network.traffic_analytics | ON | Per-port traffic metrics to TimescaleDB |
network.bulk_firmware | OFF | Multi-device firmware upgrade |
Common gotchas
Section titled “Common gotchas”-
Two port-write paths with different behavior.
/api/v1/switches/{id}/ports/{n}pushes to the controller via the adapter./api/v1/network/devices/{id}/ports/{n}is DB-only. If a port change does not appear on hardware, confirm which path your client is calling. -
Rogue-AP 501. If the controller adapter does not implement rogue-AP detection, the endpoint returns 501. This is the expected behavior, not a server error.
-
VLAN distribute is not transactional across controllers. If distributing to three controllers and the second fails, the first succeeded and the third may or may not have run. Check per-target results in the response.
-
UniFi is beta. It has not been field-tested against real Ubiquiti hardware. Use Omada for production deployments that require write capability.
-
Batch ops are not staged.
batch/rebootandbatch/firmware-upgradehit live devices directly (subject to the read-only env flag). They requiresite_adminand are audit-logged, but they do not go through Pending Changes. -
Module background tasks do not auto-run. Discovery and topology refresh run only when triggered via the API or a Celery task. The
health_checkreports their state but they are never spawned automatically by the module lifecycle.
Next steps
Section titled “Next steps”- Firewall - gateway orchestration, VPN, NAT, IDS/IPS, and the
gw_canonical_vlansLayer 2 desired-state model - Configuration Reference -
ADAPTER_READ_ONLY, discovery intervals, and all environment variables - Fabric - wire network events into cross-module automations
- Supported Vendors - honest adapter maturity matrix