Skip to content

OpenWrt

The OpenWrt adapter speaks ubus/UCI to OpenWrt routers and access points. It is a Preview / Foundation tier adapter - the client code exists and the UCI/ubus communication layer is plausibly complete, but the adapter has not been through the gold-standard contract pass. Use it for development and evaluation; do not deploy it against production OpenWrt fleets.

FactDetail
MaturityPreview / Foundation
Gold-standard contractUnverified
Frontend UINone
Backend routesThree routers active - see below

The adapter client speaks ubus (the OpenWrt IPC bus) and UCI (Unified Configuration Interface):

  • ubus call dispatch - invoke any ubus namespace/method
  • UCI config read / write / commit / revert
  • Basic system info (system.board, system.info)
  • Interface list (network.interface.dump) These are the building blocks. What higher-level operations are reliably safe has not been empirically determined by a structured review.

OpenWrt routers are imported and mounted in app/api/v1/__init__.py:

RouterPrefixEndpoints
adapter_openwrt/api/v1/gateway-openwrt/{controller_id}/8 read-only: device-info, interfaces, firewall-rules, port-forwards, dhcp-leases, dhcp-static-mappings, arp-table, summary
adapter_openwrt_firewall/api/v1/gateway-openwrt-firewall/{controller_id}/POST /changes/{feature} (stage), GET /changes (list pending) - openwrt.firewall.* features only
adapter_openwrt_dhcp/api/v1/gateway-openwrt-dhcp/{controller_id}/POST /changes/{feature} (stage), GET /changes (list pending) - openwrt.dhcp.* / openwrt.dns.* features only

The staged-apply dispatcher (/gateway-vpn/changes/{id}/apply) handles the actual push to the device; the write endpoints above only enqueue changes.

  • OpenWrt is not listed in adapter_factory.get_available_adapter_types() (the static allowlist used by the setup wizard), so it cannot be added through the standard controller-creation UI flow. It IS registered by the adapter registry at startup (registry.py discover_adapters()) and CAN be reached through the /api/v1/gateway-openwrt/* routes if you create the controller record directly via the API.
  • No frontend pages.
  • The following gold-standard contract items are not verified: dual-gate writes, circuit breaker, secret redaction, SSRF guard, and role gates.
  • No test coverage comparable to the Production adapters.

OpenWrt’s rpcd daemon enforces an ACL for every ubus call. The default luci-base ACL does not grant the permissions that FreeSDN needs for write operations. Without the correct ACL, writes appear to succeed but the changes are silently dropped and never persisted.

At minimum, the FreeSDN service account on the OpenWrt device needs:

PermissionWhy
uci.commitPersist UCI writes to disk
rc.execRestart services after config changes

To add these, create or edit /usr/share/rpcd/acl.d/freesdn.json on the OpenWrt device:

{
"freesdn": {
"description": "FreeSDN adapter access",
"read": {
"ubus": {
"uci": ["get", "configs", "changes"],
"network.interface": ["dump", "status"],
"system": ["board", "info"]
}
},
"write": {
"ubus": {
"uci": ["set", "delete", "rename", "commit", "revert"],
"rc": ["exec"]
}
}
}
}

After adding the file, restart rpcd:

Terminal window
service rpcd restart

Then create a dedicated user in /etc/config/rpcd with the freesdn ACL group and a strong password.

Because the adapter is not registered in the factory, you must instantiate it directly in Python:

from app.adapters.openwrt.adapter import OpenWRTAdapter
async with OpenWRTAdapter(
host="192.168.1.1", # bare IP or hostname - no scheme
username="freesdn",
password="<password>",
port=80, # OpenWrt default HTTP; omit for HTTPS on 443
verify_ssl=False,
) as adapter:
result = await adapter.get_system_info()
if result.success:
print(result.data)

Do not use this pattern in production code paths. It bypasses tenant scoping, circuit breaker, rate limiting, and all other contract requirements.

  • Adapter Overview - maturity tiers, the full vendor matrix, and what the gold-standard contract requires
  • MikroTik RouterOS - production-grade alternative for RouterOS-based deployments