Routing fixes (2026-07-14):
- Add missing ip rule 'from 10.11.13.0/24 lookup 100' to hotplug ifup case
- Add UCI network rule persistence so netifd restores it on boot
- Verify ip rule exists in zt-gateway-switch do_force/do_graceful
- Fix BRIDGE_PORTS auto-detect: use /proc/net/dev instead of broken
awk-over-ip pipeline (busybox awk mishandles exit in compound if)
- Validate bridge port candidate exists as network interface
- Fix setup-routing: use dev br-zt not dev ztX (ZT iface has no IP
when enslaved to bridge, causing 'Nexthop has invalid gateway')
- Replace ip rule replace (GNU-only) with del+add for busybox
Infrastructure:
- Fix deploy:install stdin starvation: ssh/scp consume pipe data in
find|while loop; add </dev/null to prevent truncation
- Move luci-dev skill from root/ to skills/ with .agents/skills/ symlink
- Add policy routing and busybox gotcha sections to SKILL.md
- Add diagnostics doc for the routing fix session
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)