Radio Site Controller
In testing with MJARS
A remote site controller for radio sites, built on a CompuLab IoT-DIN gateway - a fanless DIN-rail box on an i.MX8M Plus. It is the router, the out-of-band access path, and the monitoring point for a site, so that a repeater or a link does not need someone to drive out to it.
There are three parts: a custom Yocto Linux image for the hardware, a renderer that turns one site description into every daemon's configuration, and an agent that reports the site's health.
All of it is my own work, currently in testing with MJARS.
Yocto · Rust · i.MX8M Plus · RAUC · FRR · WireGuard
The image
A Yocto image built from an application layer of my own on top of the upstream Poky, OpenEmbedded, NXP i.MX, and CompuLab layers. It carries cellular WAN, FRR for routing, WireGuard, nftables, SNMP with a bridge out to RS232, and RAUC for A/B updates, so an update that fails to boot rolls back to the previous slot rather than bricking a site.
The whole build runs in a container and every layer is a git submodule pinned to a commit, so a checkout builds the same image on any machine. No per-site configuration is baked in - one image serves every site, and the site's settings are rendered onto a persistent partition at runtime.
config-renderer
A small static Rust binary that reads one versioned site.yaml and writes out the configuration for everything else: NetworkManager keyfiles, FRR, WireGuard, snmpd, nftables, RAUC, cloudflared, and the watchdogs. It then drives systemd to bring the result into effect.
The config describes the site rather than the daemons. An interface is an interface whether it is ethernet, a VLAN, a cellular modem, or a WireGuard tunnel, and an uplink just says that a link is an egress path and at what priority. That keeps the file readable and lets the daemon behind a feature change without changing what an operator writes.
Field engineers run the validate command, not schema authors, so errors point at a line and column and say what is wrong in plain words, down to suggesting the key you probably meant when one is misspelled. Generated files that contain secrets are written 0600.
It builds as a static musl binary tuned for size, with no unwinding and no C dependencies, because it runs early at boot on an embedded box.
SiteMonitor
A small always-on Rust agent that collects the site's health and posts it as JSON to a backend collector over TLS. It gathers CPU, memory, network, thermals, storage, eMMC wear, the state of the systemd units that matter, and the CompuLab hardware's own inputs.
It identifies itself by the device serial read from the device tree and authenticates with a per-device bearer token that can be rotated server-side. It writes nothing to disk and keeps no retry queue - if a report fails to send, the next one supersedes it.