OpenWRT package feed for Harmony mesh networking.
| Package | Description |
|---|---|
harmony-node |
Harmony decentralized mesh node (~33 MB static binary) |
- Rust toolchain with the musl target for your architecture:
rustup target add aarch64-unknown-linux-musl # ARM64 routers rustup target add x86_64-unknown-linux-musl # x86_64 devices/VMs
- OpenWRT buildroot (or SDK) for your target device
Add this feed to your OpenWRT buildroot:
echo "src-git harmony https://github.com/zeblithic/harmony-openwrt.git" >> feeds.conf
./scripts/feeds update harmony
./scripts/feeds install harmony-nodeThen select the package in menuconfig:
make menuconfig
# Navigate: Network → harmony-node → <*> (built-in) or <M> (module)Build:
make package/harmony-node/compileThe output .ipk is in bin/packages/<arch>/harmony/.
To cross-compile harmony-node directly (replace target as needed):
git clone https://github.com/zeblithic/harmony.git
cd harmony
# For aarch64 (ARM64 routers):
cargo build -p harmony-node --features no-neon,rawlink \
--target aarch64-unknown-linux-musl \
--profile release-cross \
--locked
# For x86_64 (VMs, x86 devices — requires musl C cross-compiler for SSE/AVX):
cargo build -p harmony-node --features rawlink \
--target x86_64-unknown-linux-musl \
--profile release-cross \
--lockedThe binary is at target/<target-triple>/release-cross/harmony.
Copy it to /usr/bin/harmony on your router via scp.
The service is managed via UCI and procd.
uci set harmony-node.main.enabled=1
uci commit harmony-node
/etc/init.d/harmony-node enable
/etc/init.d/harmony-node start| Option | Default | Description |
|---|---|---|
enabled |
1 |
Enable/disable the service |
identity_file |
/etc/harmony/identity.key |
Path to the node identity key file |
listen_address |
0.0.0.0:4242 |
UDP listen address for Reticulum mesh packets |
cache_capacity |
256 |
W-TinyLFU cache items (CLI default: 1024) |
compute_budget |
100000 |
WASM compute fuel per tick |
filter_broadcast_ticks |
30 |
Bloom filter broadcast interval |
filter_mutation_threshold |
100 |
Bloom filter mutation threshold |
encrypted_durable_persist |
0 |
Accept encrypted durable content |
encrypted_durable_announce |
0 |
Announce encrypted durable content |
no_public_ephemeral_announce |
0 |
Disable public ephemeral announcements |
disk_quota |
(empty) | Cap CAS book storage (e.g. 10 GiB). Empty = unbounded. |
logging_level |
info |
Log verbosity — maps to TOML [logging] level. Values: error, warn, info, debug, trace. |
rawlink_interface |
(empty) | Linux interface name for raw Ethernet (AF_PACKET) transport — enables IP-less Zenoh/Reticulum over 802.11s mesh. When set, the service is automatically granted CAP_NET_RAW via procd capabilities. |
relay_url |
(empty) | iroh relay URL for NAT-traversal tunnels (enables tunnel accept) |
tunnel_peer |
(list) | Tunnel peer entries (repeatable). Format: node_id_hex or node_id_hex:friendly-name. The optional :name suffix maps to the TOML name field in [[tunnels]]. |
Edit with uci:
uci set harmony-node.main.cache_capacity=512
uci commit harmony-node
# Service restarts automatically via procd triggerOr edit /etc/config/harmony-node directly and restart:
/etc/init.d/harmony-node restartThe init script generates /tmp/harmony-node.toml from UCI options on each service
start and passes --config /tmp/harmony-node.toml to the binary. Always configure
via UCI — do not edit the generated TOML directly (it is overwritten on restart).
On first install, the package automatically configures an 802.11s mesh interface:
- SSID:
HARMONY-MESH— well-known, communal mesh network - PSK:
ZEBLITHIC— intentionally public (Harmony provides E2E encryption) - Radio: Auto-detected 5GHz (override with
uci set harmony-node.mesh.radio='radioN') - Tunings:
mesh_fwding=0(Reticulum handles routing),mcast_rate=24000(24Mbps broadcast floor),multicast_to_unicast=0(true broadcast),powersave=0(no DTIM delay),mesh_rssi_threshold=-70(cull weak peers),he_bss_color=37(spatial reuse)
Any Harmony device within WiFi range automatically joins the mesh. No manual setup needed.
Network isolation: The mesh VIF runs on a dedicated br-harmony bridge
(10.73.0.0/24), isolated from br-lan. Mesh peers can reach harmony-node
on the router (UDP 4242, 7446, 4434-4435) but cannot access LAN devices,
the router admin UI, or forward traffic to WAN.
For advanced configuration, troubleshooting, and band planning, see docs/mesh-wifi-setup.md.
The rawlink_interface UCI option enables an AF_PACKET bridge that sends Zenoh
and Reticulum frames directly over raw Ethernet (EtherType 0x88B5), bypassing
the Linux IP stack entirely. Peer discovery uses L2 Scout broadcasts instead of
UDP multicast.
Frame batching: Outgoing Zenoh and Reticulum frames are automatically packed
into batch frames (type 0x03) to reduce broadcast airtime overhead. WiFi broadcast
cannot use 802.11 aggregation, so Harmony batches at the application layer instead.
Transmission jitter: When inbound frames are received, outbound batch flushes are delayed by a random 100-500ms to break timing correlation between queries and responses. This prevents passive observers from mapping cause-and-effect packet sequences across the mesh.
To enable L2 transport on the mesh bridge:
uci set harmony-node.main.rawlink_interface='br-harmony'
uci commit harmony-node
/etc/init.d/harmony-node restartWhen rawlink_interface is set, the service is automatically granted CAP_NET_RAW
via procd capabilities — no manual permission changes needed.
Which interface name? Use the mesh bridge (br-harmony), not the raw wireless
interface (e.g. wlan1-1). The bridge provides a stable name across reboots and
aggregates all mesh VIFs.
Parallel operation: IP-based peering (UDP 4242, 7446, 4434-4435) continues to work alongside L2 transport. WAN and LAN peers connect over IP; intra-mesh peers use L2 frames. The node accepts traffic from both transports simultaneously.
For a step-by-step setup guide, see docs/mesh-wifi-setup.md.
The package installs a firewall include (/etc/harmony-node.firewall) that automatically
opens Harmony protocol ports on both LAN and WAN:
- UDP 4242 — Reticulum mesh packets
- UDP 7446 — Zenoh multicast scouting / peer discovery
- UDP 4434-4435 — iroh-net QUIC tunnels (4433 excluded — IANA HTTPS-alt overlap)
Harmony provides its own E2E encryption (Reticulum + ML-KEM + ML-DSA), so there is no meaningful trust difference between LAN and WAN for Harmony traffic. Non-Harmony traffic remains firewalled on WAN as before.
These rules are applied automatically on package install and on every firewall reload. No manual configuration is needed. Both fw3 (iptables, OpenWRT <22.03) and fw4 (nftables, OpenWRT >=22.03) are supported — the script auto-detects which is active.
To verify the rules are active:
# fw4 (nftables, OpenWRT 22.03+):
nft list chain inet fw4 input_lan | grep 4242
nft list chain inet fw4 input_wan | grep 4242
# fw3 (iptables, older OpenWRT):
iptables -L input_lan_rule -n | grep 4242
iptables -L input_wan_rule -n | grep 4242To restrict Harmony traffic to LAN only (not recommended — breaks WAN peering):
# Edit the firewall include to change WAN ACCEPT rules to DROP
vi /etc/harmony-node.firewall
/etc/init.d/firewall reloadImportant: Always apply changes via /etc/init.d/firewall reload — do not
run the firewall script directly, as rules will accumulate without the flush
that the firewall framework performs before sourcing includes.
Upgrade note: /etc/harmony-node.firewall is a conffile — local edits are
preserved across opkg upgrade. If a future package version ships changes to
the firewall script (e.g., new chain names), opkg will keep your version and
save the new one as .opkg-new. Review and merge manually if prompted.
Note: Removing the harmony-node package also removes the identity key at
/etc/harmony/identity.key. Back up the key before uninstalling if you plan to
reinstall on the same device.
BLAKE3 uses architecture-specific SIMD assembly for performance. The build behavior differs by target:
-
aarch64: Uses
--features no-neonwhich disables NEON assembly, falling back to pure Rust. This avoids needing a C cross-compiler for standalone builds. ~3x slower but negligible for mesh node workloads. The OpenWRT SDK build always has$(TARGET_CC)available, but the feature flag is used for consistency with standalone builds. -
x86_64: No feature flag — SSE2/SSE4.1/AVX2 assembly compiles using
$(TARGET_CC)from the OpenWRT SDK (always available). For standalone builds without an SDK, a musl C cross-compiler is required.
To enable NEON on aarch64 standalone builds (requires a musl cross-compiler):
CC_aarch64_unknown_linux_musl=$(which aarch64-linux-gnu-gcc) \
cargo build -p harmony-node \
--target aarch64-unknown-linux-musl \
--profile release-cross \
--lockedNote: omit --features no-neon when using a cross-compiler — NEON is enabled by default.
Run the init script TOML generation tests (requires Python 3.11+):
sh tests/test_toml_gen.shApache-2.0 OR MIT — same as harmony core.