uapi

openwrt-iac / uapi

The OpenWrt REST API

A native, zero-footprint HTTP control plane for OpenWrt 25.12+. Translates standard REST verbs into ubus calls so modern edge routers become first-class targets for Infrastructure-as-Code.

v2.1 - signed APK feed - MIT licensed - OpenWrt 25.12 +

Why uapi

Native, not a sidecar

Runs inside the existing uhttpd process via ucode. No daemon to supervise, no socket to wire, no extra memory at idle. Talks to ubus directly; never touches /etc/config/ behind uci's back.

Atomic writes

Every HTTP write is one transaction: snapshot, validate, commit, reload. If the daemon rejects the new config, uapi restores the snapshot and returns 500 reload_failed_restored. POST /batch extends this across N packages with per-package locks acquired deadlock-free.

IaC-shaped

Stable resource IDs survive uci rewrites. Dependency-aware ETags + If-Match give Terraform-style providers the optimistic-concurrency story they need. Conditional GET cuts refresh bandwidth; cursor pagination keeps collections sane.

Production-grade

Bearer auth with hierarchical scopes, token expiry, source-IP scoping, per-token rate limit, Prometheus /metrics, syslog audit log, idempotency keys for safe retries, signed-tag releases with SPDX SBOM.

Drive it from Terraform

The reference client is the openwrt-iac/uapi Terraform provider. uapi's wire surface (stable IDs, per-resource ETags, optimistic If-Match concurrency, idempotency keys for safe POST retries, atomic /batch across packages) was designed around what an IaC provider needs.

terraform {
  required_providers {
    uapi = {
      source  = "openwrt-iac/uapi"
      version = "~> 2.1"
    }
  }
}

provider "uapi" {
  endpoint = "https://router.example.com/api/v2"
  token    = var.uapi_token       # mint with `uapi-token create`
}

resource "uapi_firewall_rule" "ssh_mgmt" {
  name   = "allow-ssh-mgmt"
  target = "ACCEPT"
  match {
    src_zone  = "lan"
    proto     = ["tcp"]
    dest_port = [22]
  }
}

The provider is a separate project; see its registry page for the full resource catalog and version compatibility matrix.

Quickstart (raw HTTP)

  1. Install from the signed APK feed (full guide at /uapi/install/):
    apk add uapi
  2. Mint a token on the router:
    uapi-token create --name ci_bot --scope '*:rw' --expires-in 90d
  3. Use it from anywhere with HTTPS:
    curl -H "Authorization: Bearer $TOKEN" \
      https://router/api/v2/firewall/rules
    
    curl -H "Authorization: Bearer $TOKEN" \
         -H 'Content-Type: application/json' \
         -X POST https://router/api/v2/firewall/rules \
         -d '{"name":"allow-mgmt","target":"ACCEPT",
              "match":{"src_zone":"lan","dest_port":[22]}}'

What ships in 2.1

34 curated resources

  • network/ interfaces, devices, routes, rules, bridge-vlans, wireguard-peers
  • firewall/ zones, rules, redirects, forwardings, defaults
  • wireless/ devices, interfaces
  • dhcp/ hosts, leases, leases6, servers, dnsmasq, odhcpd
  • system/ + timeservers + password + authorized_keys
  • dropbear, uhttpd, unbound (server + srv + ext), sqm, snmpd, lldpd, vnstat, prometheus-node-exporter
  • packages/ installed + feeds (apk passthrough)

Generic raw passthrough

The long tail of OpenWrt config under /api/v2/raw/<package>/<section>. Same atomic-transaction recipe, same auth + scope model, payloads pass through uci field names directly.

System endpoints

  • GET /healthz - subsystem status
  • GET /openapi.json - full spec
  • GET /schema/<pkg>/<res> - one resource's schema
  • GET /auth/whoami - token introspection
  • GET /tokens, POST /tokens - HTTP token rotation
  • GET /metrics - Prometheus text
  • GET /diagnostics - lock state + uptime
  • POST /batch - multi-package atomic transactions

Documentation