Skip to content

v2.3.0

Choose a tag to compare

@github-actions github-actions released this 24 Jun 12:42
· 172 commits to main since this release
v2.3.0

Surfaces the known scope tree through a sanctioned interface so external consumers (the upcoming luci-app-uapi LuCI frontend, fleet inventory tools, anything that wants to render a scope picker) can enumerate valid scopes without parsing src/lib/scope.uc or hardcoding a copy. Closes openwrt-iac/uapi#5. Also plumbs per-token rate / burst overrides through the mint surfaces, closing a "planned for v2.x" gap that has been carried in docs/tokens.md since 2.0.

Commit-confirmed apply (the ?confirm / /confirm surface) was present in the 2.3.0-rc1 pre-release and has since been deferred; it is not part of the 2.3.0 stable surface. The confirm wire contract is intentionally not frozen into v2 until its authz model is settled and a consumer needs it, so it does not lock in a contract that could only be changed with a major bump. See docs/commit-confirm.md and docs/roadmap.md.

Added

  • New CLI subcommand uapi-token scopes printing one scope path per line (sorted, greppable). Pair with --json for a JSON array suitable for piping into jq or any other consumer. The CLI is the durable cross-package interface; it works from any shell, Ansible playbook, or fleet inventory tool that can ssh to the router.

  • New scope.known_paths() module export returning a sorted array of scope paths. ucode consumers on the same box (the LuCI frontend in particular) require('scope') and call this directly, matching how uapi-token itself imports the module.

Both surfaces enumerate the same internal KNOWN_PATHS const; the accessor (rather than a direct const export) lets the underlying representation change without breaking consumers.

  • POST /tokens accepts optional rate and burst integer fields; uapi-token create accepts --rate <N> and --burst <N>. Both write option rate '<N>' / option burst '<N>' on the token's uci section. The request-path rate limiter (src/lib/ratelimit.uc's effective_limits()) has been reading these uci options since 2.0.0; only the mint side was missing. uapi-token show <name> also now surfaces the configured rate/burst when set.

The OpenAPI vendor-extension option from the issue thread is deferred until a code-generated client surfaces with a concrete need; premature spec annotations without consumer evidence is what 2.2.2 / 2.2.3 corrected.

Fixed

  • PATCH no longer deletes uci options the resource does not model. Previously every write verb shared one diff_apply that deleted any existing uci option not re-emitted by toUci, so a partial PATCH (e.g. changing unbound verbosity, or any field on firewall.rules) silently wiped hand-set or stock options the curated model omits (dns64_prefix, icmp_type, synflood_protect, cert key_type/ec_curve, ttylogin, and many more). PATCH now preserves them (RFC 7396 merge-patch: only options inside the resource's own modeled footprint are touched). PUT keeps full-replace semantics: it normalizes the section to the modeled set, so unmodeled options are intentionally dropped on a PUT. An audit of all curated resources against their stock OpenWrt configs found this affected 13 resources.

  • A JSON Patch (RFC 6902) that does not touch a write-only secret no longer drops it. The JSON Patch post-image is built from the masked read view (which exposes has_key, not key), so a patch that left the secret alone previously produced a post-image without it and tripped conditional-required validation ("key is required when encryption is ..."). The write path now carries forward any writeOnly field the patch did not set (key, preshared_key, private_key, tls_auth, pkcs12), matching what the merge-patch path already did; a patch that explicitly sets a new secret is left intact.

  • Three validation rules relaxed to match what stock OpenWrt ships (found by the same audit): wireless.devices accepts an empty country (read back as null) and the "00" world regulatory domain (stock 6 GHz default); wireless.interfaces accepts encryption='owe' (Opportunistic Wireless Encryption, keyless, the stock 6 GHz default) without demanding a key; network.devices no longer requires type, so a config device options-override section (name + macaddr, no type, as config_generate emits on some targets) round-trips.

Internal

  • New integration test tests/integration/44_stock_config_test.sh round-trips every curated CRUD resource and singleton whose package ships in the bare OpenWrt 25.12.4 image (firewall, network, dhcp, dropbear, system): GET the section, adopt if unmanaged, PUT-self (or PATCH-self for singletons), then GET again and assert the persistable shape did not drift. A 422 or a diff surfaces a regression where uapi rejects (or silently mutates) what the platform itself ships. Resources from optional packages (snmpd, lldpd, vnstat, mwan3, etc.) are deferred to a follow-up that wires their install at VM-setup time. First run forced four schema relaxations to bring API rules in line with the platform: igmp accepted by firewall.rules/firewall.redirects; */any wildcards accepted by firewall.rules zone refs; dhcp.servers no longer requires the referenced network interface to exist (stock ships dhcp.wan against an absent network.wan on x86); is_valid_cidr_any accepts IPv6 CIDR (used by mwan3.rules once mwan3 coverage lands).