← Notes

Moving community.openwrt from shell to ucode

· openwrt, ansible, ucode

I manage a small fleet of OpenWrt routers with Ansible: cloud routers, a VPS and the boxes at home. The tool for that is community.openwrt, the successor of the old gekmihesg.openwrt role. Its trick is that the router needs no Python: the modules used to be shell scripts that the controller copied over SSH and ran with the router’s own BusyBox.

That works, but it is slow, and speed is what pushed me to rewrite it. In the shell uci module every option meant a few more forks of uci and jsonfilter, and my mesh needs a lot of options on every router — one task, one round of forks each. On top of that, JSON is assembled from shell strings, quoting is a minefield, and the modules are hard to test.

Why ucode

OpenWrt already has a better runtime on every device: ucode, the small JavaScript-like language firewall4 is written in. It has native JSON and ships bindings for fs, uci and ubus, so a module can read and write configuration directly instead of shelling out for every value.

What happened

Next: WireGuard and AmneziaWG

My homelab mesh runs on WireGuard and AmneziaWG tunnels, and until now the wiring lived in a big Ansible role. The new wg_interface and wg_peer modules (#281, in review) take over the device-side logic: key generation, shared PSKs, peers, and cleanup of interfaces that are no longer wanted. I already run them from my fork, with Molecule scenarios on real router firmware in VMs as integration tests.

A few ucode gotchas