- Windows Executable Release Flow: Fixed the GitHub Actions workflow to correctly attach the Windows
.exestandalone application to immutable releases.
This release fixes a critical bug where Virtual Channel packets injected into the local radio (via EXTRA_MQTT_ROOTS) could be decrypted and rebroadcast over RF to nearby nodes, effectively bridging two independent MQTT server regions against the user's intent.
- Fix: Virtual Channel RF Crosstalk (PR #41)
- Root Cause: The radio firmware uses
packet.channel— a PSK hash integer embedded in theServiceEnvelopeprotobuf payload — to look up its decryption key. Because the topic-only rewrite (NC-LongFast) left the original PSK hash intact, the radio could still match its local channel key and decrypt the packet, causing it to rebroadcast over RF. - Fix: The proxy now mutates the
packet.channelfield in the protobuf payload before injecting it into the radio:packet.channel— replaced with a synthetic hash unique to the virtual channel name that no local radio will ever have configured
- The
packet.encryptedbytes and originalchannel_idstring are left completely untouched. MeshMonitor natively decrypts the message using its standard Channel Database. Known Defect: Because the PSK hash is faked, MeshMonitor's "Enforce Channel Name Validation" cannot be used. Consequently, if multiple channels share the exact same key, MeshMonitor will decode and merge their traffic into a single channel display. - Added
INFO-level log entry on every virtual channel rewrite for easy discovery of exact virtual channel names.
- Root Cause: The radio firmware uses
- Updated
CONFIG.mdVirtual Channels section to accurately describe the payload mutation mechanism and added MeshMonitor Channel Database setup instructions explaining which channel name and key to configure for each virtual channel.
This release introduces two major new features to handle advanced broker routing and to improve fidelity with Meshtastic node configurations: Virtual Channels (Multi-Root) and Per-Channel Uplink/Downlink Filtering.
EXTRA_MQTT_ROOTSConfiguration: You can now configure the proxy to listen to multiple MQTT root topics simultaneously. This allows you to monitor and interact with additional roots seamlessly without complex external broker forwarding rules.- Topic Rewriting: Traffic from extra MQTT roots is automatically rewritten to appear as local "Virtual Channels" (e.g., matching the root name like
NC-LongFast), preventing cross-talk while making remote region traffic accessible in standard clients such as MeshMonitor.
- Respect Node Configuration: The proxy now fully parses and respects the per-channel
uplink_enabledanddownlink_enabledsettings configured directly on your physical Meshtastic node. - Selective Forwarding: Only packets on channels explicitly permitted for uplink or downlink will be forwarded between the MQTT broker and the local radio queue, saving serial bandwidth and perfectly matching the firmware's intended behavior.
- Documented the new channel-based filtering and extra MQTT roots features.
- Updated configuration documentation (
CONFIG.mdandREADME.md) to clearly explainEXTRA_MQTT_ROOTS.
- Zero-Setup Windows Deployment: Added an automated PyInstaller build pipeline that compiles
mqtt-proxyinto a standalone, click-and-run.exefor Windows users.- No Python installation or virtual environment is required.
- Windows AMD64 binaries are now automatically generated via GitHub Actions and attached directly to Releases.
- Subprocess-Ready: The executable is specially configured to force line-buffered
stdoutandstderrstreams usingutf-8encoding. This prevents buffering lags andcp1252encoding crashes when embedded and executed natively by external tools or desktop apps on Windows. - Updated configuration documentation to recommend this execution method for easiest integration with the MeshMonitor Desktop App.
- Single Source of Truth: Introduced
version.pyto manage the application version globally. - Improved Visibility:
- The binary version is now printed in the startup logs:
🚀 MQTT Proxy v1.5.3 starting.... - Added a
--versionflag to the CLI for quick verification. - Added a version tag to the
README.mdfor consistent project tracking.
- The binary version is now printed in the startup logs:
- Argparse Support: The proxy now fully supports standard command-line flags to configure connection settings directly on launch.
- Supported execution flags:
--interface,--tcp-host,--tcp-port,--serial-port,--log-level, and--version. - Backwards-compatible: Existing environment variables and
.envfiles still function normally as fallbacks for any omitted flags.
- Supported execution flags:
- Bump Meshtastic to 2.7.8: Upgraded python
meshtasticdependency to the latest2.7.8version for compatibility with the newest firmware module configs (e.g. Traffic Management and StatusMessage).
- Docker Build Fixes:
- Fixed a missing build context issue where
version.pywas not copied into the container, preventing the proxy from launching. - Enabled multi-arch (AMD64/Arm64/Armv7) Docker image pushes for internal Pull Requests, allowing real-time testing on devices like Raspberry Pi.
- Fixed a missing build context issue where
- Workflow Optimizations:
- Fixed a "double build" issue in CI by removing redundant triggers on feature branches.
- Resolved
WinError 10106by setting the PyInstaller runtime temporary directory to%LOCALAPPDATA%\Temp. - Added detailed "Releasing New Versions" documentation to guide developers through PR-safe tagging workflows on protected branches.
- Simplify Echo Bypass: Cached prefixed node ID and cleaned up the
_on_messageboolean logic structure. Thank you @NearlCrews!
- Narrowed Implicit ACK Echo Bypass: Fixed an issue where the loop prevention bypass was too broad, causing unencrypted routing, position, and telemetry packets to echo back to the node unnecessarily.
- Instead of bypassing loop protection for all packets from the local gateway, the proxy now explicitly checks if the packet is
encryptedor has a validrequest_id. - Administrative/plain packets are properly dropped by the loop tracker to minimize unnecessary RF traffic.
- Instead of bypassing loop protection for all packets from the local gateway, the proxy now explicitly checks if the packet is
- Added new test cases in
tests/test_echo_bypass.pyto ensure only correct packet types (encrypted, request_id) explicitly bypass loop prevention.
Full Changelog: https://github.com/LN4CY/mqtt-proxy/compare/v1.4.1...v1.4.2
- Implicit ACK Echoes: Fixed a critical bug where
mqtt-proxy's strict loop protection dropped echoed messages from the MQTT Broker.- When the Meshtastic firmware has
proxy_to_client_enabled: trueset, it stops generating automatic "Implicit ACKs" for transmissions. - Instead, the firmware expects the MQTT broker to echo the message back so it knows it was successfully delivered.
- By bypassing the deduplicator specifically for echoed messages (where
gateway_idmatches the local node ID), the firmware now receives the delivery confirmation it needs. - This prevents
MAX_RETRANSMITtimeouts and fixes the "Red X" (Failed delivery status) issue in MeshMonitor for both DMs and Channel Broadcasts.
- When the Meshtastic firmware has
- CONFIG.md: Added warnings and explanations around the
proxy_to_client_enabledsetting and how it alters firmware timeout behavior. - README.md: Added "Implicit ACK Restoration" to the core features list and clarified the Architecture section's "How It Works".
Full Changelog: https://github.com/LN4CY/mqtt-proxy/compare/v1.3.0...v1.4.0
- Ignored Implicit ACKs from Self/Local: Fixed an issue where local routing confirmation packets were incorrectly interpreted as successful delivery acknowledgments from remote nodes.
- The proxy now explicitly checks the
senderofROUTING_APPpackets. - Packets where
sender == 0(local routing confirmation) are ignored. - Packets where
sender == myNodeNum(echoes from self) are ignored. - This ensures
meshtastic.ackevents are only emitted for high-confidence confirmations, improving message reliability logic.
- The proxy now explicitly checks the
- Added new unit tests in
tests/test_meshtastic_extended.pyto verify implicit ACK suppression for self and sender 0.
Full Changelog: https://github.com/LN4CY/mqtt-proxy/compare/v1.2.0...v1.3.0
- Locked Radio Access: Implemented thread-safe
_sendToRadiousing the interface's internal locking mechanism.- Prevents potential race conditions when multiple handlers attempt to send data to the radio simultaneously.
- Added fallback with warning if
_sendToRadiois missing (e.g., in oldermeshtasticlibrary versions).
- Improved Deduplication: Refined the deduplication logic to better handle packet IDs and sender tracking.
- Prevents message loops when proxying traffic between MQTT and the mesh network.
- Configuration Race Condition Fix: Addressed a race condition in MQTT handler initialization to ensure reliable startup and state management.
- Improved Health Checks: Enhanced watchdog mechanisms for better reporting of proxy status.
- Detailed Message Queue Stats: Added precise timing logs for message processing.
- New Log Format:
Message processed. Queue: N msgs, Wait: X.XXXs, Send: X.XXXs - Helps track transit times and identify bottlenecks in radio transmissions.
- New Log Format:
- Queue Handler Coverage: Increased test coverage for
handlers/queue.pyto ensure reliable buffering and rate limiting. - Thread Safety Validation: Added tests to verify thread-safe usage of the radio interface.
- Build Trigger Cleanup: Fixed duplicate Docker image builds by refining push event wildcard triggers.
- Cleanup Workflow Fix: Resolved
Invalid Cut-offerrors in image retention policy by updating to properly formatted duration strings (14d).
None - all changes are backward compatible.
All tests passing with improved coverage in core message handling modules.
Full Changelog: https://github.com/LN4CY/mqtt-proxy/compare/v1.1.2...v1.2.0
- 50x faster MQTT-to-node forwarding: Reduced default
MESH_TRANSMIT_DELAYfrom 500ms to 10ms- Typical transmission is ~1ms, 10ms provides safe spacing while maintaining responsiveness
- Configurable via
MESH_TRANSMIT_DELAYenvironment variable
- Prevents startup floods: Skip retained MQTT messages by default
- Eliminates 4+ second queue backlogs when connecting to broker
- Retained messages are historical state, not live mesh traffic
- Opt-in via
MQTT_FORWARD_RETAINED=trueif needed
- Queue depth logging: Added current queue size to message processing logs
- Format:
Queue: N msgs, Wait: X.XXXs, Send: X.XXXs - Helps identify backlogs and performance issues
- Format:
- 40 tests → 42 tests: Added extensive coverage for critical paths
- 87% total coverage across all handlers
handlers/mqtt.py: 90% coveragehandlers/meshtastic.py: 77% coveragehandlers/node_tracker.py: 86% coveragehandlers/queue.py: 92% coverage
test_mqtt_extended.py: TLS configuration, error handling, edge casestest_meshtastic_extended.py: Byte parsing, implicit ACKs, DecodeError handlingtest_proxy_health.py: Health checks, watchdog mechanisms, state managementtest_retained_messages.py: Retained message filtering validation
-
Critical: Fixed
fromIdAttributeError in packet tracking- MeshPacket protobuf only has
fromfield, notfromId - Was causing "Failed to track node/packet: fromId" errors
- MeshPacket protobuf only has
-
CI/CD: Fixed module import errors in GitHub Actions
- Added
sys.path.append()to test files for proper module resolution
- Added
-
Pytest: Renamed
TestInterfacetoMixinTestHelper- Eliminates pytest warning about test classes with
__init__constructors
- Eliminates pytest warning about test classes with
MESH_TRANSMIT_DELAY: Delay between messages (default:0.01seconds)MQTT_FORWARD_RETAINED: Forward retained messages (default:false)
None - all changes are backward compatible
All 42 tests passing with 87% coverage across handlers
Thank you to everyone who helped test and validate these improvements!
Full Changelog: https://github.com/LN4CY/mqtt-proxy/compare/v1.1.1...v1.1.2