Router-to-router radio backhaul over an open 802.11s mesh interface, with FIPS providing all encryption (Noise IK), authentication, and routing on top of bare L2 neighbor links. The mesh runs OPEN with mesh_fwding 0 — SAE would duplicate the Noise layer and force ath10k raw mode, and FIPS is the routing layer — so the Noise handshake is the real auth/encryption boundary and FIPS's spanning tree does the routing. fips-mesh-setup: an opt-in UCI helper that creates a per-radio mesh-point interface (radio0 -> fips-mesh0, radio1 -> fips-mesh1; trailing-digit derivation with a free-index fallback and a collision guard). Radio setup stays opt-in — a package must not commandeer radios on install. 'remove' takes an optional radio and otherwise removes all instances. Dual-band routers get one instance per radio; FIPS treats the two backhaul paths as failover, not multipath: it keeps one active link per peer (cross-connection resolution picks a single winner), and the second band stands by, re-establishing the peer after keepalive timeout — traffic never uses both bands at once. fips.yaml ships the mesh0/mesh1 Ethernet-transport entries commented out, so a stock install that never creates fips-mesh* logs no per-boot "interface missing" bind warning. fips-mesh-setup uncomments the matching meshN block when it creates the interface and re-comments it on remove, so the flash-and-drop-in flow needs no manual config edit. The file is rewritten 0600-first (it may hold an inline nsec) via an atomic replace. Two field-found silent non-peering causes are surfaced by the helper and the guide: - Same channel: mesh points only peer on a shared channel, and 'auto' lets each radio pick its own. The helper prints the radio's band/channel and warns loudly on 'auto' with the exact uci command to pin one; the how-to gains an ordered no-peers triage (channel mismatch, on-air scan check, DFS CAC wait, regdomain). - STA channel capture: a client (sta) interface drags the whole radio to its upstream AP's channel, so a mesh pinned elsewhere never joins and does not recover until the STA disconnects. The helper warns when the target radio carries a STA; the guide documents the incompatibility of a roaming uplink with a fixed-channel mesh on the same radio. Both the create and remove paths run 'wifi reload', which briefly drops every client AP on all radios; the how-to sets that expectation. Regression test: the shipped OpenWrt fips.yaml must parse via the real Config deserializer in both states — as shipped (mesh inactive) and after the uncomment the helper performs. Packaging: the helper is installed across the ipk/apk/buildroot paths (three synced copies), with the CI structural checks and shellcheck targets extended to cover it. Full guide in docs/how-to/set-up-80211s-mesh-backhaul.md.
FIPS Packaging
This directory contains packaging for all supported target platforms.
All build outputs go to deploy/ at the project root.
Quick Start
make deb # Debian/Ubuntu .deb
make tarball # systemd install tarball
make ipk # OpenWrt .ipk (opkg, OpenWrt 24.x and earlier)
make apk # OpenWrt .apk (apk-tools, mandatory on OpenWrt 25+)
make aur # Arch Linux AUR package (fips-git, local build + namcap)
make pkg # macOS .pkg installer
make zip # Windows .zip package
make all # deb + tarball (default)
Build Prerequisites
These targets build FIPS from source, so the host needs a build
environment in addition to a Rust toolchain (the version pinned in
rust-toolchain.toml is auto-installed by rustup).
On Linux, libclang is required: the LAN gateway's nftables
bindings are generated by bindgen at build time, which needs
libclang.so on the build host. Without it the build fails inside the
rustables crate with an "Unable to find libclang" error.
sudo apt install libclang-dev # Debian / Ubuntu
This is a build-time prerequisite only — it is not a runtime
dependency, so hosts installing a pre-built .deb do not need it.
BLE support is optional and, when building with it, additionally needs
bluez, libdbus-1-dev, and pkg-config; the build picks up BLE if
those are present and skips it cleanly if not.
Directory Structure
packaging/
aur/ Arch Linux AUR packaging (PKGBUILD, supporting files)
common/ Shared assets (default config, hosts file)
debian/ Debian/Ubuntu .deb packaging via cargo-deb
macos/ macOS .pkg installer via pkgbuild
systemd/ Generic Linux systemd tarball packaging
openwrt-ipk/ OpenWrt .ipk packaging via cargo-zigbuild (opkg)
openwrt-apk/ OpenWrt .apk packaging via cargo-zigbuild + apk mkpkg
windows/ Windows .zip package with service scripts
Formats
Debian/Ubuntu (.deb)
Built with cargo-deb. Installs
fips, fipsctl, and fipstop to /usr/bin/, and enables the systemd
service.
The default configuration ships as an example at
/usr/share/fips/fips.yaml.example and is not a dpkg conf-file.
(It is deliberately not under /usr/share/doc, which minimal and
container installs path-exclude, since the postinst reads it at install
time.)
On install, postinst seeds /etc/fips/fips.yaml (mode 600) from the
example only if it does not already exist, so a configuration that
was rendered by configuration management or edited by an operator is
never prompted for or clobbered on upgrade. To reset to defaults, remove
/etc/fips/fips.yaml and reinstall, or copy the example back manually.
# Build
make deb
# Install
sudo dpkg -i deploy/fips_<version>_<arch>.deb
# Remove (preserves config and keys)
sudo dpkg -r fips
# Purge (removes config and identity keys)
sudo dpkg -P fips
systemd Tarball
A self-contained tarball with binaries and an install.sh script for
any systemd-based Linux distribution.
# Build
make tarball
# Install (on target host)
tar -xzf deploy/fips-<version>-linux-<arch>.tar.gz
sudo ./fips-<version>-linux-<arch>/install.sh
See systemd/README.install.md for full installation and configuration instructions.
OpenWrt (.ipk, opkg — OpenWrt 24.x and earlier)
Cross-compiled with cargo-zigbuild and assembled as a standard .ipk
archive. Supports aarch64, mipsel, mips, arm, and x86_64 targets.
# Build (default: aarch64)
make ipk
# Build for a specific architecture
bash packaging/openwrt-ipk/build-ipk.sh --arch mipsel
See openwrt-ipk/README.md for router-specific installation instructions.
OpenWrt (.apk, apk-tools — mandatory on OpenWrt 25+)
OpenWrt 25 makes apk-tools the mandatory package manager (it is opt-in on
24.10). Same SDK-free approach
(cargo-zigbuild), but the .apk container is assembled by apk mkpkg
rather than hand-rolled, so the build additionally needs an apk-tools v3
apk binary built from source. The installed-filesystem payload is shared
with the .ipk package.
# Build (default: aarch64; also x86_64)
make apk
# Build for a specific architecture
bash packaging/openwrt-apk/build-apk.sh --arch x86_64
Packages are unsigned; install with apk add --allow-untrusted. See
openwrt-apk/README.md for building apk-tools and
router-specific installation.
macOS (.pkg)
Built with pkgbuild (included with Xcode command-line tools). Installs
binaries to /usr/local/bin/, config to /usr/local/etc/fips/, sets up
the /etc/resolver/fips DNS resolver for .fips domains, and loads a
launchd daemon. The TUN device is named utun<N> (kernel-assigned)
rather than fips0.
# Build
make pkg
# Install
sudo installer -pkg deploy/fips-<version>-macos-<arch>.pkg -target /
# Remove
sudo packaging/macos/uninstall.sh
Windows (.zip)
A ZIP archive containing binaries, default config, and PowerShell service helper scripts. Requires the wintun driver for TUN support.
# Build
make zip
# Or directly
powershell -File packaging/windows/build-zip.ps1
# Extract and install as service (requires Administrator)
Expand-Archive deploy\fips-<version>-windows-x86_64.zip -DestinationPath fips
cd fips
powershell -File install-service.ps1
# Uninstall (preserves config)
powershell -File uninstall-service.ps1
# Uninstall and remove config
powershell -File uninstall-service.ps1 -RemoveAll
Arch Linux (AUR)
Two AUR packages are maintained: fips (release, builds from tagged
tarball) and fips-git (development, builds from latest git master).
# Build and validate locally (git variant)
make aur
# Install from AUR
yay -S fips-git # development build from master
yay -S fips # release build from latest tag
See aur/README.md for AUR publication instructions and maintainer guide.
Nix / NixOS (flake)
A flake at the project root builds all four binaries
(fips, fipsctl, fips-gateway, fipstop) from source. It pins the
exact toolchain from rust-toolchain.toml via
fenix and wires up the
build-time native dependencies (libclang for bindgen, plus dbus
and pkg-config for BLE), so it needs no system setup beyond Nix with
flakes enabled.
nix build .#fips # build the package (all four binaries)
nix run .#fips -- --help # run a binary directly
nix run .#fipsctl -- status
nix develop # dev shell with the pinned toolchain + cargo-edit
nix flake check # build + validate the flake
Add to a NixOS configuration via the flake's packages.<system>.fips
output, e.g. environment.systemPackages = [ fips.packages.${system}.default ];.
Shared Assets
common/ contains assets used across packaging formats:
fips.yaml— default configuration (ephemeral identity, UDP/TCP/TUN/DNS)hosts— static hostname-to-npub mappings for.fipsDNS resolution