MeshSat

September 5, 2026 ยท View on GitHub

Stars Release License: GPL v3 Go 1.24+ Tests Docker

Keep a message moving when the network is gone.

MeshSat is an open source gateway that gives an off-grid mesh a way out. It takes a message off a Meshtastic LoRa network and finds a route to the outside world over whatever bearer is still alive: Iridium satellite, cellular SMS, amateur packet radio, ZigBee, Bluetooth or a plain TCP link. A Reticulum routing layer sits underneath and picks the path by cost, so traffic stays on the free bearers and only reaches for the metered ones when nothing free is up.

It runs as a single Docker container on a Raspberry Pi. No cloud service, no account, no subscription beyond whatever you already pay your satellite or cellular carrier.

Status: pre-release. This is a working prototype under active development, not a finished product. It has never been deployed to a real user and has never been used in an actual emergency. See What is proven, and what is not for an honest breakdown before you rely on it for anything.

Quick start

Save this as docker-compose.yml:

services:
  meshsat:
    image: ghcr.io/meshsat/meshsat:latest
    container_name: meshsat
    restart: unless-stopped
    privileged: true
    network_mode: host
    environment:
      - MESHSAT_MODE=direct
      - MESHSAT_PORT=6050
      - MESHSAT_DB_PATH=/data/meshsat.db
    volumes:
      - meshsat-data:/data
      - /dev:/dev
      - /sys:/sys:ro

volumes:
  meshsat-data:
docker compose up -d

Plug in a Meshtastic radio, a satellite modem, a cellular modem, or all three. MeshSat scans USB on startup, identifies each device by VID:PID and protocol probe, and brings up whatever it finds. Open http://<your-ip>:6050 for the dashboard. A single device is enough to start; missing hardware is logged as a warning, not an error.

The image is published to GHCR and is pullable anonymously. To build from source instead, see below.

MeshSat Dashboard The built-in dashboard: satellite modem status, mesh nodes, delivery queue, positioning and per-channel health scores.

If MeshSat is useful to you, a star helps other people find it.

Why this exists

When a storm, an outage or a disaster takes the network down, the radios people already own mostly cannot help. A Meshtastic mesh keeps a village talking to itself, but it has no way out of the valley. A satellite modem can reach the far side of the planet, but it costs real money per message and nobody carries one. A phone is useless without a tower.

MeshSat is the piece in between. It treats every radio you have as one possible route, and moves the message along whichever one is up. The reason the routing layer exists at all is cost: satellite bytes are expensive, so the design goal is to spend them only when there is nothing free left.

What it bridges

Eight transport bearers, reachable across nine wired Reticulum interfaces.

BearerHardwareNotes
Meshtastic LoRaESP32 / nRF52 radiosFull protocol via the official buf.build protobuf bindings
Iridium SBDRockBLOCK 9603N340 byte messages, AT commands, pass-aware scheduling
Iridium IMTRockBLOCK 9704100 KB messages, JSPR protocol
Cellular SMSA7670E, SIM7600G-H, and similarAT commands, SMS and data
APRS / AX.25Any KISS TNC, Direwolf bundledDirewolf runs supervised in-container on loopback
ZigBeeCC2652PZ-Stack ZNP binary protocol
BLEHost BluetoothGATT peripheral via BlueZ, with segmentation
TCPAny IP linkInteroperates with upstream Python RNS

Plus TAK (CoT XML), MQTT and webhooks as routing destinations, and multi-instance gateways so two modems of the same type can run side by side with independent config and workers.

What is proven, and what is not

Most projects skip this section. If you are deciding whether to trust this with anything that matters, it is probably the most useful thing on the page.

State
Meshtastic serial, full protocolWorking on hardware, both kits
Iridium 9704 IMT, mobile-originated and mobile-terminatedVerified over a real satellite link, March 2026
Iridium 9603 SBDWorking on hardware
Cellular SMS both directionsWorking on hardware
APRS / AX.25 via bundled DirewolfWorking on hardware
Reticulum interoperabilityPasses against upstream Python RNS 1.1.4
HeMB bonding across LoRa, TCP and SMSThree-bearer field test, April 2026, zero failures
HeMB over a paid satellite bearerNot validated. Outstanding work
HeMB mixed free and paid allocationUndefined. See the allocator note below
RTL-SDR jamming detectionImplemented and tested against ambient noise only, never against a real jammer
ZigBee gatewayCode complete, light field exposure
OOB management frames (remote commands over SMS, APRS, mesh, Iridium, Hub)Request leg proven kit to kit over SMS and through the Hub, September 2026. Reply leg over SMS and APRS not yet observed (carrier and radio receive issues on the test kits, not the protocol)
Deployment to a real end userNever. No emergency service has used this
Use in an actual disasterNever

The test suite is 1,333 test functions across 129 files, and it gates every deploy. That says the code does what the authors expect. It does not say the radio link will hold at 3am in the rain.

On HeMB specifically: the current symbol allocator is free-first. While any free bearer is healthy, all source symbols go to the largest-MTU free bearer and paid bearers receive none. There is no capacity model yet. Capacity-aware paid activation is specification work, not a shipped feature, and the 1:900 latency ratio quoted in the design is a target, not a measured result.

Hardware

MeshSat Field Kit Field kit: a self-contained multi-transport gateway in a waterproof case. Meshtastic and cellular over USB, the RockBLOCK 9603 wired to the Pi 5 GPIO UART. Everything is auto-detected on startup.

#ComponentRole
1Heltec LoRa V4 (ESP32-S3 + SX1262 + GPS)Meshtastic mesh radio, 868/915 MHz
2RockBLOCK 9603 (Iridium 9603N)Satellite, SBD, 340 byte messages, UART
3LILYGO T-Call A7670 (ESP32 + A7670E)4G LTE / 2G, AT commands, SMS and data
4INIU 25000mAh (100W USB-C PD)Power for the whole kit
5Raspberry Pi 5 (8 GB)Host, standalone mode

The hardware has its own repository, meshsat-fieldkit: the two kits built so far (tesseract and parallax) with their parts lists, wiring tables and a build guide, and the V2 carrier PCB set for a Peli 1520 case (KiCad sources, the scripts that generate the boards, the case CAD, the Rev A release files). Rev A is designed and prepared for fabrication, not built yet.

MeshSat Compact Kit Compact kit: mesh plus satellite only, in a pocket-sized waterproof case.

#ComponentRole
1XIAO ESP32-S3 + SX1262Meshtastic mesh radio, very small
2RockBLOCK 9704 (Iridium IMT)Satellite, JSPR, 100 KB messages, USB
3Anker Prime 20,000mAh (200W)Power
4Raspberry Pi 5 (8 GB)Host, standalone mode

Supported devices

CategoryDeviceStatusNotes
MeshtasticHeltec LoRa V4 (ESP32-S3 + SX1262 + GPS)Tested868/915 MHz, OLED, 2 MB PSRAM
XIAO ESP32-S3 + SX1262Tested868/915 MHz, WiFi + BLE, very compact
Lilygo T-Echo (nRF52840)Tested915 MHz, USB-C, e-ink
Lilygo T-DeckTestedESP32-S3, keyboard, screen
Espressif / CH340 / CP2102 / NordicShould workAuto-detected by USB VID:PID
SatelliteRockBLOCK 9603 (Iridium 9603N)TestedSBD, 340 byte MO, 19200 baud, UART or RS-232
RockBLOCK 9704 (Iridium IMT)TestedJSPR, 100 KB messages, 230400 baud, USB
CellularLILYGO T-Call A7670 (A7670E)Tested4G LTE / 2G, SMS and data
SIM7600G-HTestedUSB modem, AT commands
Huawei E220 (3G)TestedUSB modem, AT commands
ZigBeeSONOFF ZigBee 3.0 Dongle Plus (CC2652P)Code completeZ-Stack ZNP, VID:PID plus ZNP probe
HostRaspberry Pi 5 (8 GB)TestedARM64
Raspberry Pi 4 (4 GB)TestedARM64
BananaPi BPI-M4 ZeroNot recommendedAllwinner H618 USB proved unreliable
Any x86_64 / ARM64 LinuxShould workDocker plus USB serial

Features

Routing

  • Reticulum-compatible routing with Ed25519 identity, announce relay, link manager, keepalive, bandwidth tracking and chunked reliable resource transfer
  • Nine wired Reticulum interfaces: LoRa mesh, TCP/HDLC (RNS interop), Iridium SBD, Iridium IMT, AX.25/APRS, MQTT, SMS, ZigBee, BLE
  • TransportNode with cost-aware cross-interface forwarding, PathFinder route discovery and a 30 minute route TTL
  • Dispatcher with failover groups, a delivery ledger, per-channel workers and visited-set loop prevention
  • HeMB (Heterogeneous Media Bonding): RLNC-coded bonding across heterogeneous bearer adapters, reassembling from any K of N coded symbols regardless of which bearer delivered them. Bearers do not need a shared IP stack. See the allocator caveat above.

Compression and transforms

  • Three compression tiers: SMAZ2 (lossless, sub-millisecond), llama-zip (LLM-based lossless, around 200 ms), MSVQ-SC (lossy semantic, rate-adaptive)
  • Per-interface transform pipelines: compress (zstd, SMAZ2), encrypt (AES-256-GCM), encode (base64)

Security

  • AES-256-GCM per channel, with key exchange by QR bundle (meshsat://key/ URI scheme)
  • v2 key bundles carrying an Ed25519 signing public key for Trust On First Use. Android pins the bridge key on first scan; later scans are verified cryptographically
  • Master key envelope encryption (HKDF plus AES-256-GCM wrapping), device-derived or passphrase
  • Ed25519 signing service with a hash-chain audit log for tamper detection
  • Credential management: ZIP/PEM upload, encrypted storage, expiry monitoring

Field intelligence

  • Dead Man's Switch, geofence alerts, per-channel health scores, satellite burst queue, mesh topology visualisation
  • Access rules engine with object groups (node, portnum, sender, contact), rate limiting and implicit deny
  • DeviceSupervisor with USB hotplug, a VID:PID identification cascade and claim-based port management
  • Satellite pass prediction by SGP4/TLE propagation with signal correlation
  • RTL-SDR spectrum monitoring across five bands with a multi-feature jamming classifier, feeding automatic transport failover
  • OOB management frames: authenticated, optionally encrypted single-message commands (PING, REBOOT, RESTART, RESET per interface and level, BEARER, LOG, STATUS-NET) that ride any bearer, replay-protected, with host actions behind an allowlisted agent. A kit that has lost its WiFi can still be reached over SMS or APRS, and a RESET at level 3 cuts a wedged device's USB port power when the kit has a per-port switchable hub. Spec: docs/OOB_MANAGEMENT_PROTOCOL.md
  • Config export and import in YAML, in the style of show running-config, with diff preview

Dashboard and API

  • Vue 3 dashboard, 13 views, embedded in the binary
  • REST API, over 300 endpoints, fully annotated for Swagger
  • Server-sent events for live updates
  • Prometheus metrics at /metrics

Other ways to install

From source

git clone https://github.com/meshsat/meshsat.git
cd meshsat
make build-with-web    # Vue SPA plus Go binary

Setup guide

1. Plug in your devices. Meshtastic radio, satellite modem, cellular modem, in any combination. Detection is automatic at startup by USB VID:PID table and protocol probe, using pure Go serial (go.bug.st/serial), so there is no CGO anywhere in the build.

2. Start the container. MeshSat scans USB, probes each device (Meshtastic protobuf, Iridium AT, ZNP for ZigBee), and starts the dashboard on port 6050.

3. Open the dashboard at http://<your-ip>:6050.

4. Set up access rules in the Interfaces tab to route between transports. Rules filter on source and destination interface, direction, node, portnum, keyword and object group, and support SMS contact selection, failover groups, transform overrides and rate limiting.

5. Verify end to end. Send a test message from your Meshtastic device. With rules configured it should land on the destination interface, for example appearing in the RockBLOCK portal or arriving as an SMS.

Configuration

Everything is set by environment variable. Environment values are first-boot defaults; the dashboard can override them afterwards.

Core

VariableDefaultDescription
MESHSAT_MODEcubeosSet to direct for standalone USB access
MESHSAT_PORT6050HTTP port for dashboard and API
MESHSAT_DB_PATH/data/meshsat.dbSQLite database path
MESHSAT_RETENTION_DAYS30Days of history to keep
MESHSAT_WEB_DIR(empty)Override the embedded SPA path (development only)

Serial ports (auto scans USB by VID:PID and protocol probe)

VariableDefaultDescription
MESHSAT_MESHTASTIC_PORTautoMeshtastic radio
MESHSAT_IRIDIUM_PORTautoIridium 9603N (SBD)
MESHSAT_IMT_PORTautoRockBLOCK 9704 (IMT/JSPR)
MESHSAT_CELLULAR_PORTautoCellular modem
MESHSAT_ZIGBEE_PORTautoZigBee coordinator

Iridium 9603N

VariableDefaultDescription
MESHSAT_GPIO_CHIPgpiochip4libgpiod chardev (Pi 5 is 4, Pi 4 is 0)
MESHSAT_IRIDIUM_SLEEP_PIN0Sleep/wake GPIO, BCM numbering, 0 disables
MESHSAT_IRIDIUM_NETAV_PIN0NetAv input, high means satellite visible
MESHSAT_IRIDIUM_RI_PIN0Ring indicator input, active low
IRIDIUM_SBDIX_TIMEOUT90SBDIX timeout in seconds

Rate limiting and routing

VariableDefaultDescription
MESHSAT_PAID_RATE_LIMIT60Minimum seconds between paid satellite sends
MESHSAT_MAX_HOPS8Maximum interfaces a message may traverse
MESHSAT_MESH_WATCHDOG_MIN10Minutes of silence before a Meshtastic reconnect
MESHSAT_MESH_MTU100HeMB mesh bearer MTU in bytes, range 1 to 237
MESHSAT_OOB_ENABLEDfalseOOB management frames: accept authenticated commands over any bearer (first-boot default, UI-managed afterwards)
MESHSAT_OOB_REPLY_BUDGET12OOB replies per peer per hour
MESHSAT_OOB_HOST_SOCKET/run/meshsat-oob/agent.sockOOB host agent socket (see scripts/install-oob-agent.sh)

Compression sidecars

VariableDefaultDescription
MESHSAT_LLAMAZIP_ADDR(empty)llama-zip gRPC sidecar address
MESHSAT_LLAMAZIP_TIMEOUT30RPC timeout in seconds
MESHSAT_MSVQSC_ADDR(empty)MSVQ-SC gRPC sidecar address
MESHSAT_MSVQSC_TIMEOUT30RPC timeout in seconds
MESHSAT_MSVQSC_CODEBOOK(empty)Codebook path, enables pure-Go decode

Reticulum interfaces

VariableDefaultDescription
MESHSAT_TCP_LISTEN(empty)TCP listen address for RNS interop, e.g. :4242
MESHSAT_ANNOUNCE_INTERVAL300Announce broadcast interval in seconds
MESHSAT_AX25_KISS_ADDR(empty)KISS TNC address. Bundled Direwolf binds localhost:8001
MESHSAT_AX25_CALLSIGN(empty)AX.25 source callsign, e.g. MESHSAT-1
MESHSAT_AIOC_CAPTURE94%AIOC capture gain applied at every Direwolf start (mixer scale: 94 % = -5.8 dB, 90 % = -9.6 dB); lower it if Direwolf reports the audio input level as too high
MESHSAT_APRS_RX_WATCHDOG_MIN5APRS receive watchdog: minutes without a decoded frame, after the channel was heard within two hours, before the recovery ladder runs (Direwolf restart, AIOC port power cycle, bridge restart); a deaf receiver scores 0 for failover. 0 disables
MESHSAT_MQTT_RETICULUM_BROKER(empty)MQTT broker for Reticulum packets
MESHSAT_MQTT_RETICULUM_PREFIXreticulum/meshsatMQTT topic prefix

Key exchange

VariableDefaultDescription
MESHSAT_KEY_PASSPHRASE(empty)Master key passphrase, empty means device-derived
MESHSAT_BUNDLE_VERSIONv2Key bundle format, v2 with signing pubkey or v1 legacy

Key bundles and the TOFU trust model

QR-based key bundles (meshsat://key/...) carry AES-256 channel keys between the bridge and the Android app. The bridge emits a signed binary bundle, which the user scans on the phone.

v2 bundles embed the bridge Ed25519 signing public key, which enables Trust On First Use:

  1. First scan: Android pins the key and marks the bundle NEW_TRUSTED.
  2. Later scans: the signature is verified against the pinned key. Valid gives EXISTING_TRUSTED; a changed key gives KeyMismatch and the user must accept it explicitly.
  3. v1 bundles from older bridges import as UNVERIFIED_V1 with a warning.
Version(1)=0x02 | BridgeHash(16) | Timestamp(4) | EntryCount(1) | SigningPubkey(32) | Signature(64) | Entries...

The signature covers every byte except the signature itself, so the public key cannot be swapped without invalidating it. The key fingerprint is shown in Settings > About for visual comparison.

Deployment modes

StandaloneCubeOS
Set byMESHSAT_MODE=directMESHSAT_MODE=cubeos (default)
Serial accessDirect to /dev/ttyACM0, /dev/ttyUSB0Through the HAL REST API
Deploy withdocker-compose.standalone.ymlCubeOS orchestrator
Who it is forAny Linux machineCubeOS installations

Architecture

USB / UART / TCP       MeshSat Container                              Clients
------------------     -----------------------------------------------  ----------------
                       |                                             |
/dev/ttyACM0 -------->-|  DirectMeshTransport (Meshtastic)            |
  (Meshtastic)         |    Protobuf binary framing (buf.build)       |->  Web Dashboard
                       |                                             |    (Vue 3 SPA,
/dev/ttyUSB0 -------->-|  DirectSatTransport (Iridium 9603N)          |     13 views)
  (Iridium SBD)        |    AT commands, SBDIX/SBDSX, sleep/wake GPIO |
                       |                                             |->  REST API
Pi UART GPIO -------->-|  DirectIMTTransport (RockBLOCK 9704)         |    (300+ endpoints)
  (Iridium IMT)        |    JSPR protocol, 230400 baud, 100 KB msgs   |
                       |                                             |->  SSE Events
/dev/ttyUSB1 -------->-|  DirectCellTransport (A7670E / SIM7600G)     |    (real-time)
  (Cellular)           |    AT commands, SMS, data                    |
                       |                                             |->  Prometheus
/dev/ttyUSB2 -------->-|  DirectZigBeeTransport (CC2652P)             |    (/metrics)
  (ZigBee)             |    Z-Stack ZNP binary protocol               |
                       |                                             |
                       |  DeviceSupervisor                            |
                       |    USB hotplug, VID:PID cascade, port claims |
                       |                                             |
                       |  Compression Pipeline                        |
                       |    SMAZ2 | llama-zip | MSVQ-SC               |
                       |                                             |
                       |  Reticulum Routing (9 interfaces)            |
                       |    Ed25519 identity, announce relay, links   |
                       |    TransportNode, PathFinder, cost-aware     |
                       |    mesh|tcp|sbd|imt|ax25|mqtt|sms|zigbee|ble |
                       |                                             |
                       |         InterfaceManager                     |
                       |           (state machine, bind/unbind)       |
                       |              |                               |
                       |         AccessEvaluator                      |
                       |           (rules, object groups, rates)      |
                       |              |                               |
                       |         Dispatcher                           |
                       |           (delivery workers per iface)       |
                       |              |                               |
                       |         HeMB Bonder (bond groups)            |
                       |        (RLNC coding, free-first allocation)  |
                       |              |                               |
                       |      TransformPipeline                       |
                       |        (zstd, smaz2, aes-256-gcm, b64)       |
                       |              |                               |
                       |  +--------+--------+--------+------+------+  |
                       |  |SBD     |IMT     |Cell    |MQTT  |APRS  |  |
                       |  |Gateway |Gateway |Gateway |GW    |GW    |  |
                       |  +--------+--------+--------+------+------+  |
                       |  |ZigBee  |TAK     |Wbook   |Fail- |      |  |
                       |  |Gateway |Gateway |GW      |over  |      |  |
                       |  +--------+--------+--------+------+------+  |
                       |                                             |
                       |  Field Intelligence                          |
                       |    Dead Man's Switch, Geofence Alerts,       |
                       |    Health Scores, Burst Queue, Topology      |
                       |                                             |
                       |  SpectrumMonitor (RTL-SDR jamming detection) |
                       |  KeyStore (QR bundles, master key envelope)  |
                       |  SigningService (Ed25519 hash chain)         |
                       |  CredentialManager (certs, expiry, mTLS)     |
                       |  Delivery Ledger (SQLite tracking)           |
                       |  SQLite DB (/data/meshsat.db, schema v53)    |
                       -----------------------------------------------

Troubleshooting

No devices detected on startup. Check the devices are visible with ls /dev/ttyACM* /dev/ttyUSB*. Try a different cable or port; USB cables are the usual culprit.

Meshtastic connects but shows 0 nodes. The config handshake takes 5 to 10 seconds. Wait for the config complete line in the log.

Iridium shows 0 bars. Check the antenna connection. It needs a genuinely clear view of the sky, not a window.

ZigBee dongle detected as Meshtastic. The SONOFF dongle shares a VID:PID with some Meshtastic boards. Pin it explicitly with MESHSAT_ZIGBEE_PORT.

A serial device goes silent and a reboot does not fix it. On some hosts USB VBUS stays powered across a warm reboot, so the device firmware never resets. Cut power properly.

Roadmap

v0.1.x Iridium SBD plus Meshtastic bridging, rules engine, MQTT gateway, pass-aware scheduler, dead letter queue with ISU-aware backoff, device management, SOS mode, dashboard.

v0.2.0 Any-to-any routing fabric. Channel registry, unified rules engine, structured dispatcher, cellular integration, SMAZ2 compression, ZigBee gateway, InterfaceManager with USB hotplug, object groups, failover groups, transform pipelines, Ed25519 audit log, config export and import.

v0.3.0 Three-tier compression. Reticulum-compatible routing with Ed25519 identity, announce relay, link manager, keepalive, bandwidth tracking and TCP/HDLC RNS interop across nine wired interfaces. RockBLOCK 9704 IMT transport. SBD and IMT decoupled into separate gateway types. APRS and TAK gateways. DeviceSupervisor with USB hotplug and gateway lifecycle wiring. Full Meshtastic protocol via the official protobuf bindings. Cross-platform key exchange. Credential management. Multi-instance gateways. Field intelligence. Android companion app.

Next, protocol work

  • DTN concepts: custody transfer, bundle fragmentation and late binding in the delivery ledger, taking cues from RFC 9171
  • Forward error correction: a Reed-Solomon codec in the transform pipeline for noisy channels
  • GPS-denied time synchronisation: mesh clock consensus with stratum tracking, Iridium MSSTM and an NTP fallback
  • HeMB: formalising capacity-aware cost semantics and paid activation, then validating at larger latency ratios. RLNC encoding across bonded groups was confirmed on hardware in March 2026, and a three-bearer field test over LoRa, TCP and SMS completed in April 2026. Validation over a paid satellite bearer is still outstanding. An RFC submission through the Independent Submission Stream (draft-papadopoulos-hemb-00) is planned to follow that specification work and measurement.

Next, spectrum

  • Offline characterisation of captured IQ, raw-signal evidence capture with SigMF sidecars, and panoramic survey sweeps. The classifier has not yet been validated against a real jammer.

Future Federated mesh-of-meshes, HF radio transport (NVIS/ALE), DMR, cognitive radio with dynamic spectrum access, edge inference for message triage, HSM integration.

  • MeshSat Android, a standalone mobile gateway with BLE mesh, SPP Iridium and SMS: github.com/meshsat/meshsat-android
  • MeshSat Field Kit, the hardware: the built kits, the V2 carrier boards and the build guides: github.com/meshsat/meshsat-fieldkit
  • MeshSat Hub, multi-tenant fleet management. Currently a private beta on our own infrastructure with no public sign-up; access for demos and testing is arranged on request at beta-access-hub@meshsat.net
  • CubeOS, a self-hosted OS for single-board computers: cubeos.app

Contributing

Issues and pull requests are welcome. Good places to start:

  • Try it with hardware we have not tested and tell us what broke. The supported-devices table above is short because it only lists what we have physically run.
  • The ZigBee and BLE paths have had far less field exposure than the rest.
  • Documentation, especially anything you had to work out the hard way during setup.

Open an issue before a large change so we can agree the shape of it first.

Funding

This project is supported by SIDN fonds.

License

Copyright 2026 Elli and Kyriakos. Licensed under the GNU General Public License v3.0.