Reticulum Bridge Deployment β
MeshMonitor can attach to a [Reticulum Network Stack](οΏ½83οΏ½ (RNS) deployment through a small sidecar image, meshmonitor-rns-bridge. This page covers deploying that sidecar with Docker Compose and Kubernetes/Helm. For the bridge's own architecture, wire protocol, and full environment-variable reference, see bridge/README.md in the repository, this page is the deployment-focused companion to it.
Why a separate image β
rns/lxmf are distributed under the Reticulum License, a source-available license that is not OSI-approved (it restricts certain uses and carries redistribution terms incompatible with MeshMonitor's BSD-3 codebase). To keep that license entirely out of the main application:
meshmonitor-rns-bridgeships as its own image, built frombridge/Dockerfile, published from its own CI workflow (.github/workflows/reticulum-bridge.yml) never from the main image's build/publish workflow.- RNS/LXMF are never added to
package.jsonand never imported fromsrc/**. - The bridge talks to MeshMonitor's Node backend only over a local WebSocket (JSON protocol), no RNS/LXMF code or license obligation crosses that boundary.
Full disclosure: bridge/NOTICE.
Two connection modes β
attach(recommended if you already run Reticulum), the bridge attaches to your existingrnsd/shared Reticulum instance as a client. Needs a readable RNS config directory (typically~/.reticulum) and, because the shared-instance RPC socket binds to127.0.0.1, host networking. Safe by design: client instances never open interfaces themselves, so attaching cannot double-open a serial port or otherwise disturb your runningrnsd.tcp_peer, the bridge runs its own standalone RNS instance and dials out to one or moreTCPClientInterfacepeers. No localrnsd, no config-dir mount, no host networking β but you lose the interface-stats/RSSI data that only a shared instance's local interfaces expose.
Docker Compose β
An opt-in overlay, docker-compose.reticulum.yml, adds the meshmonitor-rns-bridge service alongside your existing docker-compose.yml:
BRIDGE_TOKEN=changeme docker compose -f docker-compose.yml -f docker-compose.reticulum.yml up -d(Or set BRIDGE_TOKEN in your .env file instead of the shell, the overlay requires it and fails fast with a clear message if it's unset.)
The overlay ships pre-configured for attach mode:
services:
meshmonitor-rns-bridge:
image: ghcr.io/yeraze/meshmonitor-rns-bridge:latest
network_mode: host
volumes:
- ${HOME}/.reticulum:/rns:ro
environment:
RNS_MODE: attach
RNS_CONFIG_DIR: /rns
BRIDGE_HOST: 127.0.0.1
BRIDGE_PORT: "8765"
BRIDGE_TOKEN: ${BRIDGE_TOKEN:?set a token}To switch to tcp_peer mode, edit the overlay:
- Remove
network_mode: hostand the${HOME}/.reticulum:/rns:rovolume. - Publish a port instead:
ports: ["8765:8765"]. - Change
RNS_MODEtotcp_peer, dropRNS_CONFIG_DIR, and addRNS_TCP_PEERS: host:port[,host:port...](e.g.amsterdam.connect.reticulum.network:4965).
Then, in MeshMonitor (Dashboard β Sources), add a Reticulum source pointing its bridge URL at ws://127.0.0.1:8765 (attach mode) with the same token as BRIDGE_TOKEN.
Kubernetes (Helm) β
The chart gates an optional second container in the MeshMonitor pod behind reticulum.enabled (default false, a default install renders identically with or without this feature present in the chart). Add to your custom-values.yaml:
reticulum:
enabled: true
image: "ghcr.io/yeraze/meshmonitor-rns-bridge:latest"
mode: "attach" # attach | tcp_peer
configDir: "/root/.reticulum" # host path to the RNS config dir (attach mode)
token: "changeme" # REQUIRED when enabled β shared secret (BRIDGE_TOKEN)
bind: "127.0.0.1:8765" # bridge's own WebSocket listen addresshelm upgrade --install meshmonitor ./helm/meshmonitor -f custom-values.yamlNotes specific to the Kubernetes deployment:
attachmode setshostNetwork: trueon the whole pod, not just the bridge container, Kubernetes has no per-container network-namespace setting, and the shared- instance RPC socket the bridge attaches to only listens on127.0.0.1. This means the main MeshMonitor container is on host networking too when Reticulum attach mode is enabled. If that's undesirable, usetcp_peermode instead (nohostNetworkneeded) or run the bridge as a separate Deployment/DaemonSet outside this chart.configDiris ahostPathmount, so the node the pod schedules onto must actually have that path, pin the pod there with anodeSelector/affinity(both exposed by the chart) if your cluster has more than one node.tokenis set directly invalues.yamlfor a quick start. For anything beyond local testing, prefer a KubernetesSecretand reference it viaextraEnv'svalueFromon your own values overlay instead of committing the token in plaintext.- See the Helm chart README and Kubernetes/Helm Guide for the rest of the chart (ingress, persistence, resource limits, etc.), the
reticulumblock composes with all of it.
Troubleshooting rpc_key / RPC_AUTH_FAILED β
The single most likely misconfiguration in attach mode. Two RNS instances must agree on an rpc_key to exchange interface stats, path-table, and signal data over the shared- instance RPC channel. By default this key is derived from the config directory's transport identity, which means:
If the bridge is pointed at the same config directory as your
rnsd, the setup both the Compose overlay and the HelmconfigDirabove use by default (bind-mounting~/.reticulum/ hostPath-mounting it read-only), the keys match automatically. No action needed.If you deliberately keep the bridge on a separate config directory from
rnsd(e.g. you don't want to share your main config dir, or the bridge andrnsdrun on different hosts), you must set an explicit, matchingrpc_keyin both config files, under[reticulum]:ini[reticulum] rpc_key = <same 64-char hex string in both configs>There is no bridge-side environment variable for this, it is an RNS config-file setting, not a bridge setting, because it has to match on both sides of the RPC channel. Mounting nothing extra is required beyond each side's own config file carrying the matching key.
What you'll observe when this is wrong: the initial attach itself succeeds, no error on connect, because packet forwarding only depends on the interface access-control (ifac) key, not rpc_key. The mismatch only surfaces on the first interface-stats poll, which opens a separate authenticated RPC channel and fails with RPC_AUTH_FAILED. If MeshMonitor's Reticulum Info view shows the bridge attached but interface/destination data never appears, check this first.
See bridge/README.md for the complete failure-code reference (CONFIGDIR_UNREADABLE, NO_SHARED_INSTANCE, TCP_PEER_UNREACHABLE, PROTOCOL_VERSION_MISMATCH, AUTH_FAILED, and more).
Image publishing β
meshmonitor-rns-bridge is built and pushed to ghcr.io/yeraze/meshmonitor-rns-bridge for linux/amd64 and linux/arm64 by the publish job in .github/workflows/reticulum-bridge.yml, triggered on a published GitHub release or a manual workflow_dispatch. It runs after and depends on, the bridge's pytest suite passing, so a broken bridge is never published. :latest only moves on a stable (non-prerelease) release, matching the main image's tagging convention.