Internet-over-LoRa: Update your blog from a can on a string from the smoldering rubble.
Part of the Deadlight ecosystem secure, performant, privacy-focused tools for resilient connectivity on mesh/satellite/spotty networks.
Project Blog · Why This Exists · Getting Started · Hardware · Dashboard · Usage · Configuration · How It Works · Real-World Use Cases · Performance · Roadmap · License
deadmesh transforms LoRa mesh networks into practical Internet gateways. Built on the proxy.deadlight foundation, it adds transparent mesh networking that lets any device on a Meshtastic mesh access standard Internet protocols HTTP/HTTPS, email, DNS, FTP, as if they had normal connectivity.
What makes this different from other mesh solutions:
- Standard protocols work unchanged: browse websites, send email, use apps
- Transparent to applications, no special client software needed
- Automatic fragmentation and reassembly for mesh transport
- Full MITM proxy capabilities for traffic inspection and caching
- Works with existing Meshtastic hardware and networks
- Truly off-grid: solar-powered nodes can provide connectivity across kilometers
- Real-time gateway dashboard with SSE streaming, embedded in the binary
- Live mesh visibility: see every node, position, telemetry, and text message on your network
Think of it as giving your Meshtastic network the capabilities of a satellite terminal, running on $30 hardware with zero monthly fees.
Meshtastic networks are incredible for messaging and telemetry, but they weren't designed for general Internet access. Each protocol (HTTP, SMTP, DNS) would need custom mesh-aware implementations, a chicken-and-egg problem where applications won't add mesh support without users, and users won't adopt mesh without applications.
deadmesh sits in the middle:
- Mesh side: Speaks fluent Meshtastic (protobuf over LoRa serial with proper API handshake)
- Internet side: Speaks every protocol your applications already use
- Bridges transparently: Fragments outgoing requests, reassembles incoming responses
Result: Your mesh network works with everything: email clients, web browsers, update tools, API services, without modifying a single line of application code.
- Disaster Response: Coordinate rescue operations when cell towers are down
- Rural Connectivity: Share one satellite uplink across dozens of kilometers
- Censorship Resistance: Maintain communication during Internet blackouts
- Off-Grid Networks: Festival/protest/research networks that disappear when powered off
- Development Projects: Bring Internet services to areas with zero infrastructure
- Universal Protocol Support: HTTP/HTTPS, SMTP/IMAP, SOCKS4/5, WebSocket, FTP, if it runs over TCP/IP, it works
- Transparent TLS Interception: Inspect and cache HTTPS traffic with HTTP/1.1 ALPN negotiation to minimize mesh bandwidth
- Intelligent Fragmentation: Automatically chunks large requests/responses into ~220-byte Meshtastic packets
- Serial API Handshake: Proper
want_configinitialization — auto-discovers node ID, receives full mesh state on startup - Live Mesh Visibility: Decodes all Meshtastic packet types — text messages, positions, telemetry, node info, routing
- Store-and-Forward: Delay-tolerant networking handles intermittent mesh connectivity
- Connection Pooling: Reuses upstream connections aggressively with TLS session reuse to reduce LoRa airtime cost
- Plugin Extensibility: Ad blocking, rate limiting, compression, caching, custom protocol handlers
- Hardware Flexibility: USB serial, Bluetooth, or TCP-connected radios
- Auto-Detection: Auto-discovers Meshtastic devices on serial ports and auto-detects local node ID from device
- Embedded Dashboard: Real-time gateway monitor with SSE streaming, self-contained in the binary, no external assets
Software:
- Linux (Raspberry Pi, x86 server, or similar)
- GLib 2.0+, OpenSSL 1.1+, json-glib-1.0
- GCC or Clang
- Python 3 + pip (for nanopb protobuf generation during build)
Optional (for Meshtastic CLI testing):
- Python meshtastic package (
pip install meshtastic)
Hardware (see Hardware for details):
- Meshtastic-compatible LoRa radio (ESP32-based recommended)
- Gateway node: Raspberry Pi or similar with Internet connection
- Client nodes: any Meshtastic device (phone, handheld, custom)
-
Clone and build:
git clone https://github.com/gnarzilla/meshtastic.deadlight.git cd meshtastic.deadlight make clean && make UI=1
-
Connect your Meshtastic radio:
# Most devices appear as /dev/ttyACM0 or /dev/ttyUSB0 ls -l /dev/ttyACM0 /dev/ttyUSB0 2>/dev/null # Add yourself to the dialout group sudo usermod -a -G dialout $USER # Log out and back in for group change to take effect
-
Configure — create
deadmesh.conf:[core] port = 8080 max_connections = 50 log_level = info [meshtastic] enabled = true serial_port = /dev/ttyACM0 baud_rate = 115200 mesh_node_id = 0 ; 0 = auto-detect from device fragment_size = 220 ack_timeout = 30000 max_retries = 3 hop_limit = 3 [ssl] enabled = true ca_cert_file = /home/youruser/.deadlight/ca.crt ca_key_file = /home/youruser/.deadlight/ca.key [network] connection_pool_size = 5 connection_pool_timeout = 600 upstream_timeout = 120
Note: Use absolute paths for
ca_cert_fileandca_key_file. The~shortcut expands to/root/when running withsudo, which is likely not where your certs are. -
Install CA certificate (for HTTPS interception):
sudo cp /home/youruser/.deadlight/ca.crt /usr/local/share/ca-certificates/deadmesh.crt sudo update-ca-certificates
-
Run the gateway:
./bin/deadmesh -c deadmesh.conf -v
You should see:
Meshtastic: sent want_config handshake— serial API initializedMeshtastic: auto-detected local node ID: XXXXXXXX— device recognizedMeshtastic: NodeInfo update for XXXXXXXX— mesh nodes populating- Live packet stream:
POSITION,TELEMETRY,TEXT,NODEINFOfrom mesh
-
Open the dashboard at
http://localhost:8081to monitor gateway activity. -
Test the proxy:
# HTTP curl -x http://localhost:8080 http://example.com # HTTPS curl --cacert /home/youruser/.deadlight/ca.crt -x http://localhost:8080 https://example.com # SOCKS5 curl --socks5 localhost:8080 http://example.com
deadmesh works under WSL2 with USB/IP passthrough for the Meshtastic radio:
# PowerShell (Admin) — install and attach USB device
winget install usbipd
usbipd list # Find your radio's BUSID
usbipd bind --busid <BUSID> # One-time bind
usbipd attach --wsl --busid <BUSID> # Attach to WSL (repeat after replug)# WSL — verify device
ls -l /dev/ttyACM0
sudo usermod -a -G dialout $USERNote: You must re-run
usbipd attachfrom PowerShell each time the radio is unplugged, the PC sleeps, or WSL restarts.
| Device | Chip | Connection | Status |
|---|---|---|---|
| Seeed Wio Tracker L1 | nRF52840 + SX1262 | USB CDC (/dev/ttyACM0) |
✅ Verified |
| RAK WisBlock (RAK4631) | nRF52840 + SX1262 | USB CDC | Expected to work |
| Heltec LoRa 32 V3 | ESP32-S3 + SX1262 | USB CDC (CH9102) | Expected to work |
| Heltec V4 | ESP32-S3 | USB CDC | Expected to work |
| Lilygo T-Beam | ESP32 + SX1276/8 | USB UART (CP2104) | Expected to work |
| Lilygo T-Echo | nRF52840 + SX1262 | USB CDC | Expected to work |
| Station G2 | ESP32-S3 | USB CDC | Expected to work |
Option 1: Raspberry Pi Gateway (most versatile)
- Raspberry Pi 4/5 (2GB+ RAM)
- Any Meshtastic radio from table above
- Connection: USB serial
- Power: 5V/3A supply or 12V solar panel + battery
Option 2: x86/ARM Server (development / high-throughput)
- Any Linux box with USB port
- Works under WSL2 with USB/IP passthrough
- Meshtastic radio via USB
Option 3: Industrial/Outdoor
- Weatherproof enclosure with Raspberry Pi
- High-gain directional antenna (5-8 dBi)
- Solar panel + LiFePO4 battery for 24/7 operation
Any Meshtastic-compatible device works:
- Android/iOS: Meshtastic app on phone (Bluetooth to radio)
- Handheld: RAK WisBlock, Lilygo T-Echo, Heltec LoRa 32
- Custom: ESP32 + LoRa module + deadmesh client build
For best Internet gateway performance:
# In Meshtastic app or CLI
meshtastic --set lora.region US --set lora.modem_preset LONG_FAST
meshtastic --set lora.tx_power 30 # Check local regulations
meshtastic --set lora.hop_limit 3 # Adjust for network sizedeadmesh ships with a real-time gateway dashboard embedded directly in the binary — no external files, no dependencies, nothing to serve separately.
Access: http://localhost:8081 (configurable via plugin.stats.web_port)
Features:
- Live stats: active links, packets relayed, bytes bridged, gateway uptime
- Gateway log stream via SSE (Server-Sent Events) — zero polling
- Mesh links panel showing active protocol connections
- Green RF terminal aesthetic with antenna favicon in browser tab
- Auto-scroll log with toggle
Build with dashboard support:
make clean && make UI=1The dashboard uses the same green-on-black theme as the project identity. Mesh-layer data (node positions, telemetry, text messages, RSSI, hop counts) is decoded by the gateway and will populate the Mesh Links panel as the UI evolves.
Create deadmesh.conf:
[core]
port = 8080
max_connections = 50
log_level = info
[meshtastic]
enabled = true
serial_port = /dev/ttyACM0
baud_rate = 115200
mesh_node_id = 0 ; 0 = auto-detect from device on startup
custom_port = 100 ; Meshtastic portnum for deadmesh traffic
fragment_size = 220 ; max payload bytes per LoRa packet
ack_timeout = 30000 ; ms — 30s for mesh ACKs
max_retries = 3
hop_limit = 3
[ssl]
enabled = true
ca_cert_file = /home/youruser/.deadlight/ca.crt
ca_key_file = /home/youruser/.deadlight/ca.key
[network]
connection_pool_size = 5
connection_pool_timeout = 600
upstream_timeout = 120Important: Use absolute paths for cert files. Running with
sudochanges~to/root/.
On mesh client devices, configure proxy settings:
# Linux/Mac
export http_proxy=http://gateway-ip:8080
export https_proxy=http://gateway-ip:8080
# Or point any application at:
# HTTP Proxy: gateway-ip port 8080
# SOCKS5: gateway-ip port 8080On Android (Meshtastic app + ProxyDroid):
- Install ProxyDroid
- Set proxy host to gateway IP, port 8080
- Connect Meshtastic app via Bluetooth to your radio
# HTTP through gateway
curl -x http://localhost:8080 http://example.com
# HTTPS (with CA cert — use absolute path)
curl --cacert /home/youruser/.deadlight/ca.crt -x http://localhost:8080 https://example.com
# SOCKS5
curl --socks5 localhost:8080 http://example.com
# SOCKS4
curl --socks4 localhost:8080 http://example.com
# Send email via mesh relay
curl -x http://localhost:8080 \
--mail-from sender@example.com \
--mail-rcpt recipient@example.com \
--upload-file message.txt \
smtp://smtp.gmail.com:587
# SSH over mesh (SOCKS5 proxy)
ssh -o ProxyCommand="nc -X 5 -x localhost:8080 %h %p" user@remote-serverThe Python meshtastic CLI is useful for verifying radio connectivity before running deadmesh:
pip install meshtastic
meshtastic --info --port /dev/ttyACM0Note: Stop deadmesh before using the Meshtastic CLI — they cannot share the serial port simultaneously.
[core] — port, bind address, max connections, log level, worker threads
[meshtastic] — serial port, baud rate, node ID (0=auto-detect), custom portnum, fragment size, ACK timeout, retries, hop limit, gateway announcement
[ssl] — CA cert/key paths (use absolute paths), cipher suites, certificate cache
[network] — connection pool size/timeout, upstream timeout, DNS, keepalive
[vpn] — optional TUN/TAP gateway for routing entire device traffic through mesh
[plugins] — enable/disable individual plugins, plugin directory, autoload list
[plugin.compressor] — compression algorithms, minimum size threshold (strongly recommended over LoRa)
[plugin.cache] — cache directory, max size, TTL (reduces repeat mesh traffic significantly)
[plugin.ratelimiter] — priority queuing and rate limiting
[plugin.stats] — dashboard port, update interval, history size
Bandwidth conservation:
[plugin.compressor]
enabled = true
min_size = 512
algorithms = gzip,brotli
[plugin.cache]
enabled = true
max_size_mb = 500
ttl_hours = 24Latency tolerance (multi-hop paths):
[meshtastic]
ack_timeout = 60000
max_retries = 5
[network]
upstream_timeout = 300
connection_pool_timeout = 600For redundancy across a large mesh:
[meshtastic]
gateway_mode = true
announce_interval = 300Multiple deadmesh gateways on the same channel will announce themselves, allowing clients to route via the nearest available gateway.
┌─────────────┐ ┌──────────────┐ ┌──────────┐
│ Mesh Client │ LoRa Packets │ deadmesh │ TCP/IP │ Internet │
│ (Phone / ├─────────────────>│ Gateway ├───────────────>│ Services │
│ Handheld) │ (868/915 MHz) │ │ │ │
│ │ │ - Fragment │ │ HTTP │
│ Meshtastic │ │ - Reassemble │ │ SMTP │
│ App │<─────────────────┤ - TLS Proxy │<───────────────┤ IMAP │
└─────────────┘ │ - Cache │ └──────────┘
│ - Compress │
└──────┬───────┘
│
┌──────┴───────┐
│ Serial API │
│ 0x94 0xC3 │
│ protobuf │
│ want_config │
└──────┬───────┘
│ USB
┌──────┴───────┐
│ Meshtastic │
│ Radio │
│ (LoRa) │
└──────────────┘
1. Open serial port (/dev/ttyACM0) at 115200 baud
2. Send want_config handshake (ToRadio protobuf)
3. Receive MyNodeInfo → auto-detect local node ID
4. Receive device config, module config, channel config
5. Receive NodeInfo for all known mesh nodes
6. Begin receiving live mesh packets (position, telemetry, text, etc.)
7. Filter for custom portnum (100) for proxy session traffic
8. All other packets logged for mesh visibility
Request (client → Internet):
HTTP GET request (1500 bytes)
└─> Split into 7 LoRa packets (~220 bytes each)
└─> Each tagged with sequence number + session ID
└─> Sent hop-by-hop through mesh to gateway on portnum 100
└─> Gateway reassembles → proxies to Internet
Response (Internet → client):
HTTP response (50KB HTML)
└─> Compressed if plugin.compressor enabled (~5-10KB)
└─> Cached if cacheable (saves future airtime)
└─> Fragmented into LoRa packets with flow control
└─> Client reassembles → delivers to application
Meshtastic uses a length-prefixed binary protocol over serial:
┌────────┬────────┬───────────┬───────────┬─────────────────┐
│ 0x94 │ 0xC3 │ len_hi │ len_lo │ protobuf payload│
│ magic0 │ magic1 │ (MSB) │ (LSB) │ (FromRadio/ │
│ │ │ │ │ ToRadio) │
└────────┴────────┴───────────┴───────────┴─────────────────┘
The framing layer handles sync recovery, if magic bytes are lost mid-stream, the state machine re-synchronizes automatically.
deadmesh auto-detects protocols by inspecting initial bytes, no configuration needed:
| Initial bytes | Protocol | Handler |
|---|---|---|
GET / HTTP/1.1 |
HTTP | Forward to upstream |
CONNECT host:443 |
HTTPS tunnel | TLS interception (HTTP/1.1 ALPN) |
EHLO / HELO |
SMTP | Email relay |
A001 NOOP |
IMAP | Mail client support |
\x05 |
SOCKS5 | Transparent tunneling |
\x04 |
SOCKS4 | Legacy tunneling |
| Port | Type | Handling |
|---|---|---|
| 1 | TEXT_MESSAGE | Logged for mesh visibility |
| 3 | POSITION | Logged (lat/lon/alt) |
| 4 | NODEINFO | Logged (node database) |
| 5 | ROUTING | Logged |
| 67 | TELEMETRY | Logged (battery, airtime, etc.) |
| 100 | DEADMESH (custom) | Routed to proxy session manager |
Encryption layers:
- LoRa PHY: AES-256 at the Meshtastic layer (channel PSK)
- TLS: End-to-end between client and final destination (HTTP/1.1 negotiated via ALPN)
- Proxy MITM (optional): deadmesh terminates TLS for caching/inspection — requires clients to trust the gateway CA
Trust model: Gateway holds the root CA. Mesh uses Meshtastic channel encryption. Clients trust the gateway CA by installing ca.crt.
Privacy: Mesh node IDs are pseudonymous. For operational security in sensitive deployments, rotate node IDs and channel keys regularly, and avoid PII in mesh metadata.
Scenario: Earthquake destroys cell infrastructure
Setup: Solar-powered deadmesh gateway at field hospital (satellite uplink). Rescue teams carry Meshtastic handhelds (10km range per hop). Coordinate via email, share maps, update databases.
Result: Teams stay connected across 50+ km² with zero functioning infrastructure.
Scenario: Village 30km from nearest fiber
Setup: One gateway at village center (WiMAX or satellite backhaul). Residents install Meshtastic radios on roofs. Multi-hop mesh covers entire valley.
Result: 100+ households share a single Internet connection. Hardware cost ~$50/household, no monthly fees.
Scenario: Large gathering needs coordination without government-controlled infrastructure
Setup: Organizers carry deadmesh gateways with LTE failover. Attendees use Meshtastic app on phones. Network disappears forensically when powered down.
Result: Thousands communicate freely. No persistent logs, no fixed infrastructure to seize.
Scenario: Government shuts down Internet during protests
Setup: Journalist has Meshtastic radio + deadmesh on laptop. Connects to gateway run by colleague 15km away (who has connectivity). Files stories via mesh SMTP relay.
Result: Censorship bypassed. Reports reach editors despite blackout.
LoRa physical layer (LONG_FAST preset):
- Raw bitrate: ~5.5 kbps
- Effective throughput: ~3-4 kbps after protocol overhead
- Latency: 500ms–5s per hop
Real-world application performance:
- Email: 10-20 messages/minute (text)
- Web browsing: 30-60 seconds per page (with caching)
- DNS: ~2 seconds per lookup (cache aggressively)
- API calls: 5-10 seconds per request
- File transfer: ~400 bytes/sec (~1.4 MB/hour)
Optimization tips:
- Enable compression — 3-10x improvement for text content
- Enable caching — repeat requests cost zero airtime
- Use image proxies — reduce image sizes before they hit the mesh
- Batch requests — avoid chatty protocols
Single gateway: 10-20 concurrent mesh clients comfortably. EU duty cycle regulations (1% airtime) are typically the binding constraint.
Multi-gateway: Horizontally scalable. Gateways announce availability; clients route via nearest. Adding gateways directly adds capacity.
Bottlenecks in order: LoRa duty cycle → mesh hop count (>4 hops = diminishing returns) → gateway uplink bandwidth.
- HTTP/HTTPS/SOCKS4/SOCKS5 proxy — verified working
- TLS interception with upstream certificate mimicry
- Connection pooling with TLS session reuse
- Serial API handshake (
want_config) for Meshtastic 2.x firmware - Auto-detect local node ID from device
- Full mesh packet decoding — text, position, telemetry, nodeinfo, routing
- Embedded SSE dashboard
- Plugin system — ad blocker, rate limiter, meshtastic transport
- Mesh simulator for development (
tools/mesh-sim) - WSL2 support via USB/IP passthrough
- Mesh node table in dashboard UI (positions, telemetry, last heard)
- SSE streaming of mesh packet events to dashboard
- Adaptive fragmentation based on live mesh conditions
- Exponential backoff retry
- Pre-fetching for common resources
- Android client app (native deadmesh on-device)
- Node topology visualization in dashboard
- Multi-gateway coordination protocol
- Offline message queue (store-and-forward when gateway unreachable)
- Per-client/protocol bandwidth shaping
- WebRTC signaling over mesh (peer-to-peer voice/video)
- Per-node RSSI, hop count, LoRa stats in dashboard
- Full IPv6 support
- Meshtastic firmware integration (run deadmesh directly on ESP32)
- Satellite backhaul optimization (Starlink, Iridium)
- Mesh route prediction
# Debian/Ubuntu
sudo apt-get install build-essential pkg-config libglib2.0-dev \
libssl-dev libjson-glib-dev
# Python (for nanopb protobuf generation)
pip install protobuf grpcio-toolsmake clean && make UI=1 # Full build with dashboard
make clean && make # Build without dashboard
make # Incremental buildbin/deadmesh # Main binary
bin/plugins/adblocker.so # Ad blocker plugin
bin/plugins/meshtastic.so # Meshtastic transport plugin
bin/plugins/ratelimiter.so # Rate limiter plugin
tools/mesh-sim # Mesh network simulator
deadmesh is a specialized component of the Deadlight ecosystem, built on proxy.deadlight. Contributions welcome:
- Protocol optimizations: Improve mesh efficiency
- Hardware testing: Validate on different radio platforms
- Real-world deployments: Share use cases and lessons learned
- Dashboard improvements: Mesh visualization, node maps, telemetry charts
- Documentation: Non-English guides especially valuable for global deployments
See CONTRIBUTING.md for guidelines.
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Matrix:
#deadlight-mesh:matrix.org - Blog: meshtastic.deadlight.boo
- Support development: ko-fi/gnarzilla
deadmesh is one layer of a modular stack:
| Project | Lang | Role |
|---|---|---|
| proxy.deadlight | C | SMTP/SOCKS/HTTP/VPN proxy foundation |
| deadmesh (this) | C | LoRa-to-Internet mesh gateway |
| blog.deadlight | JS | <10KB pages, email posting, edge-first |
| vault.deadlight | C | Offline credential store, proxy integration |
| deadlight-bootstrap | JS | Cloudflare Workers + D1 framework |
Each component works standalone but the stack is designed to thrive together — blog.deadlight posting over deadmesh via proxy.deadlight with vault.deadlight managing credentials, all running on solar-powered hardware in a field somewhere.
Radio regulations: LoRa operates in license-free ISM bands, but transmission power and duty cycle are regulated. Check your local rules (FCC Part 15 in US, ETSI EN 300-220 in EU).
Encryption export: This software includes strong cryptography. Check export restrictions before deploying internationally.
Responsible use: This tool can bypass censorship and enable communication in emergencies. It can also be misused. Use ethically and legally. The authors are not responsible for misuse.
Privacy: Meshtastic mesh networks are pseudonymous, not anonymous. For operational security in high-risk environments: rotate node IDs, use ephemeral channel keys, avoid PII in mesh metadata.
MIT License — see LICENSE
Includes:
- Meshtastic Protobufs (GPL v3)
- nanopb (zlib license)
Status: v1.0.1 — proxy verified, mesh serial active, 80+ nodes visible | Maintained by: @gnarzilla | deadlight.boo
## Key Changes from Original
| Section | What Changed |
|---|---|
| **Overview** | Added "live mesh visibility" bullet |
| **Features** | Added serial API handshake, live mesh visibility, auto-detect node ID, HTTP/1.1 ALPN |
| **Prerequisites** | Added json-glib-1.0, Python/nanopb, meshtastic CLI as optional |
| **Getting Started** | Rewrote with absolute cert paths, added expected startup output, added WSL section |
| **Config** | Added `custom_port = 100`, `mesh_node_id = 0` for auto-detect, absolute path warning |
| **How It Works** | Added startup sequence, serial framing diagram, packet type table |
| **Hardware** | Added tested hardware table with verified Wio Tracker |
| **Roadmap** | Added v1.0.1 section with everything verified tonight, reorganized v1.1 |
| **Build Details** | New section with dependencies and build output |
| **Status line** | Updated to "v1.0.1 — proxy verified, mesh serial active, 80+ nodes visible" |

