Setup script fixes:
- _cidr_to_mask: pad to 4 octets (/24 -> 255.255.255.0)
- UCI quoting: remove embedded shell quotes from uci set calls
- Bridge ports: auto-detect zt* interface instead of hardcoding ztabc0
- Bridge netmask: default to /23 (255.255.254.0) for ZT+WIBLAN
- DHCP/WiFi AP: reference interface name (zt_wiblan) not device name (br_zt)
- Firewall zone: add zt_wiblan to LAN zone for nftables fw4
- ZT IP persistence: ensure ZT-assigned IP stays on interface for ARP
- Exit gateway routing: table 100/101 route via exit gateway, not self
- New setup-wifi-ap subcommand for WIBLAN WiFi AP
UBUS handler:
- Add setup-wifi-ap to validation regex and error message
Deploy task:
- Auto-discover files from root/ and htdocs/ instead of hardcoded list
- Clear LuCI cache before restarting services
Documentation:
- New docs/SETUP-GATEWAY.md with architecture, config, pitfalls, checklist
- Updated docs/INSTALL.md with deploy task and setup wizard sections
- Updated docs/PROGRESS.md with session log and learnings
New zt-gateway-setup script with subcommands for configuring this
router as a ZeroTier exit gateway: bridge, routing (tables 100/101),
DHCP, hotplug, and persistence. Exposes setup ubus method via backend
with ACL and LuCI panel in overview.
Also fixes two pre-existing test issues:
- overview.spec.ts: active gateway radio is intentionally enabled
(users need to re-trigger switch for the already-selected gateway)
- drain.spec.ts: stubs drain status via page.route() since Docker
has no real conntrack entries; fixed Playwright glob pattern bug
where **/ubus* fails to match /ubus/?timestamp (regex needed)