Secure, bidirectional communication between Meshtastic mesh networks and Telegram groups
Features β’ How It Works β’ Installation β’ Usage β’ Configuration
TMesh bridges the gap between Meshtastic off-grid mesh networks and Telegram groups, enabling seamless two-way communication. It creates a virtual Meshtastic node that connects via MQTT, encrypts messages using PKI (Public Key Infrastructure), and intelligently manages message delivery to prevent network congestion.
- π End-to-End Encryption: Uses Meshtastic PKI for secure device-to-device communication
- π± Telegram Integration: Full bot integration with webhook support and message threading
- β‘ Smart Queue Management: Rate-limiting and prioritization prevent mesh network overload
- π Delivery Tracking: Real-time message status updates via Telegram reactions
- π Bidirectional Sync: Messages flow seamlessly in both directions with reply support
- π Multi-Gateway Support: Intelligent routing across multiple MQTT gateways
- π Position Tracking: Track and display device locations on maps
- π³ Docker Ready: Easy deployment with Docker containers
- π TLS/SSL Support: Secure MQTT connections with certificate validation
- Message Threading: Reply to Telegram messages in Meshtastic app and vice versa
- Multiple Channels: Support for primary and secondary Meshtastic channels
- Position Sharing: Automatic position tracking with
/positioncommand and map display - Ping/Pong Handling: Configurable ping responses via direct message or public channel
- Customizable Messages: Configure ping replies and unregistered device responses
- Text Message Delivery: Bidirectional text message forwarding with sender identification
- Multi-Gateway Routing: Intelligent message routing across multiple MQTT gateways
- Dynamic Hop Limits: Automatically adjusts hop count based on last seen gateway
- Gateway Health Monitoring: HTTP endpoints for monitoring gateway and bot health
- TLS/SSL MQTT: Secure MQTT connections with optional certificate validation
- Gateway Bridging: Bridge direct messages between gateways for admin commands
- Trace Route Support: Display mesh network routes with SNR information
- Quick Registration: Add devices with
/add !deviceidor interactive flow - Device Removal: Remove devices with
/remove !deviceid - Public Key Pinning: Prevents MITM attacks by pinning device keys on registration
- Device Discovery: Automatic device discovery via MQTT node info broadcasts
- Position Tracking: Track device locations with accuracy and timestamp
- Filter Support: Filter device lists by name in
/statusand/positioncommands
- Admin Mode: Special
/admincommands for announcements and debugging - Password Protection: Admin commands require password authentication
- Key Verification: 6-digit verification codes sent through mesh network
- Rate Limiting: Configurable message rate limits with code attempt restrictions
- TLS Support: Encrypted MQTT connections with certificate validation
- Real-time delivery status with emoji reactions:
- βοΈ Message created
- π Queued for delivery
- ποΈ Acknowledged by mesh network
- π Delivered to target device
- π Delivery failed
- π€· Unknown status (no ACK after 2 minutes)
- Multi-device status tracking
- Queue delay estimation
- Reply message correlation
TMesh consists of two main components:
-
TBot (Main Service)
- Manages Telegram bot operations
- Handles MQTT connectivity to Meshtastic network with TLS support
- Processes message encryption/decryption with PKI
- Maintains SQLite database for registrations, device info, and positions
- Implements message queue and rate limiting
- Manages multi-gateway routing and health monitoring
- Handles admin commands and announcements
-
TProxy (Webhook Proxy)
- Receives Telegram webhook updates
- Publishes updates to MQTT for TBot consumption
- Validates webhook security tokens
- Provides health monitoring endpoints for gateway status
βββββββββββββββ ββββββββββββ βββββββββββββββ
β Telegram βββββββββββΊβ TProxy β β TBot β
β Users β β(Webhook) β β(Main Logic) β
βββββββββββββββ ββββββββββββ βββββββββββββββ
β β
ββββββββββββ¬βββββββββββ
β
ββββββΌβββββ
β MQTT β
β Broker β
β(TLS/SSL)β
ββββββ¬βββββ
β
ββββββββββββββββββββββΌβββββββββββββββββββββ
β β β
ββββββΌβββββ ββββββΌβββββ ββββββΌβββββ
βGateway 1β βGateway 2β βGateway 3β
ββββββ¬βββββ ββββββ¬βββββ ββββββ¬βββββ
β β β
ββββββββββββββββββββββΌβββββββββββββββββββββ
β
βββββββββββΌββββββββββ
β Meshtastic β
β Mesh Network β
β (Virtual Node) β
βββββββββββββββββββββ
Telegram β Meshtastic:
- User sends message in Telegram group (optionally replying to previous message)
- Telegram sends webhook to TProxy
- TProxy publishes to MQTT status topic
- TBot receives message, looks up registered devices
- Message queued with rate limiting and priority
- If replying: correlates original Meshtastic message for reply chain
- Selects optimal gateway based on device's last seen location
- Calculates dynamic hop limit based on distance to gateway
- Message encrypted with device's pinned public key (PKI)
- Encrypted packet sent to MQTT β selected gateway β Meshtastic mesh
- Delivery status updates sent back to Telegram as reactions
- If no ACK after 2 minutes: status changes to π€· (unknown)
Meshtastic β Telegram:
- Device sends encrypted message to virtual node via mesh
- Gateway receives and forwards to MQTT broker
- TBot decrypts using private key
- Checks device registration and validates pinned public key
- Updates device position if included in message
- If replying to message: preserves reply chain in Telegram
- Sends formatted message to registered Telegram chats
- Sends ACK back to Meshtastic device through optimal gateway
- PKI Encryption: Each Meshtastic device has a unique X25519 key pair
- Key Pinning: Device public keys are pinned on first registration to prevent MITM attacks
- End-to-End: All messages encrypted end-to-end using elliptic curve cryptography
- Device Verification: One-time codes sent through mesh network for registration
- Webhook Security: Telegram requests validated with secret tokens
- TLS/SSL Support: Optional encrypted MQTT connections with certificate validation
- Admin Authentication: Password-protected admin commands
- Rate Limiting: Prevents abuse with configurable message and verification limits
- OK to MQTT: Only devices with this flag enabled are accessible
- Tracks which gateway last saw each device
- Routes messages through the gateway with best path to destination
- Dynamic hop limit calculation based on device-gateway relationship
- Gateway health monitoring via HTTP endpoints
- Automatic failover between gateways
- Reply to Telegram messages in Meshtastic app
- Reply to Meshtastic messages in Telegram
- Maintains conversation threading across platforms
- Reply chains preserved in both directions
- Automatic position capture from Meshtastic broadcasts
- Location history with timestamps and accuracy
- Map display with horizontal accuracy visualization
/positioncommand to view device locations- Filter by device name:
/position MyDevice
- Displays complete message path through mesh network
- Shows Signal-to-Noise Ratio (SNR) between hops
- Resolves node IDs to friendly names
- Helps diagnose mesh network connectivity
- Configurable ping words (default: "ping")
- Option to reply via direct message or public channel
- Customizable pong response text
- Helps verify mesh connectivity
Protected by password, accessible via /admin <password> <command>:
announce <channel> <message>- Send announcement to specific channeltext <deviceId> <message>- Send direct message to any devicenodeinfo <deviceId>- Query device information from database
-
MQTT Broker (e.g., Mosquitto, HiveMQ, EMQX)
- Must be accessible from both TMesh and your Meshtastic MQTT gateway(s)
- Support for QoS 1 (at least once delivery)
- Recommended: TLS/SSL encryption enabled
- Recommended: Persistent sessions enabled
-
Telegram Bot
- Create via @BotFather
- Note your bot API token and username
- Bot must be added as admin to target groups
-
Meshtastic Gateway(s)
- One or more Meshtastic devices with MQTT enabled
- "OK to MQTT" setting enabled in LoRa configuration
- Connected to the same MQTT broker
- Primary channel must match TMesh configuration
- For multi-gateway: Note device IDs of gateway nodes
-
Public Webhook Endpoint (for TProxy)
- HTTPS endpoint accessible by Telegram servers
- Can use: ngrok, Cloudflare Tunnel, reverse proxy, or cloud hosting
-
Docker and Docker Compose (recommended)
OR
-
.NET 8.0 Runtime
-
SQLite support
Your Meshtastic devices must have:
- Primary Channel Name:
LongFast(or custom - must match config) - Primary Channel PSK:
AQ==(default) or your custom PSK in base64 - LoRa Settings: "OK to MQTT" must be enabled
- MQTT Module: Enabled and configured to your broker
- Position: Enable position broadcasts for location tracking
-
Clone the repository
git clone https://github.com/yourusername/TMesh.git cd TMesh/src/TMesh -
Generate PKI key pair for your virtual node
docker run --rm -v $(pwd)/config:/tbot/config \ -v $(pwd)/data:/tbot/data \ tmesh-tbot /generatekeys
Copy the PublicKey and PrivateKey from the output - you'll need them for configuration.
-
Create configuration files
For TBot (
config/appsettings.json):{ "TBot": { "MqttAddress": "your-mqtt-broker.com", "MqttPort": 8883, "MqttUser": "your-mqtt-user", "MqttPassword": "your-mqtt-password", "MqttUseTls": true, "MqttAllowUntrustedCertificates": false, "MqttTelegramTopic": "TProxy/prod/telegram/update", "MqttStatusTopic": "TBot/prod/status", "MqttMeshtasticTopicPrefix": "msh/US/2/e", "TelegramApiToken": "123456789:ABCdefGHIjklMNOpqrsTUVwxyz", "TelegramBotUserName": "your_bot_username", "TelegramWebhookSecret": "your-random-secret-string", "TelegramUpdateWebhookUrl": "https://your-domain.com/update", "TelegramBotMaxConnections": 5, "SQLiteConnectionString": "Data Source=/tbot/data/tbot.db", "MeshtasticNodeId": 123456789, "MeshtasticNodeNameShort": "TMSH", "MeshtasticNodeNameLong": "TMesh-Bot", "MeshtasticPrimaryChannelName": "LongFast", "MeshtasticPrimaryChannelPskBase64": "AQ==", "MeshtasticSecondayChannels": [ { "Name": "Services", "PskBase64": "AQ==" } ], "MeshtasticPublicKeyBase64": "paste-public-key-here", "MeshtasticPrivateKeyBase64": "paste-private-key-here", "OutgoingMessageHopLimit": 7, "OwnNodeInfoMessageHopLimit": 7, "MeshtasticMaxOutgoingMessagesPerMinute": 30, "SentTBotNodeInfoEverySeconds": 3600, "GatewayNodeIds": [123456789, 987654321], "DirectGatewayRoutingSeconds": 3600, "BridgeDirectMessagesToGateways": true, "AdminPassword": "your-secure-admin-password", "ReplyToPublicPingsViaDirectMessage": false, "PingWords": ["ping"], "TimeZone": "UTC", "Texts": { "PingReply": "pong", "NotRegisteredDeviceReply": "{nodeName} is not registered with {botName} (Telegram)" } } }For TProxy (
tproxy-config/appsettings.json):{ "TProxy": { "MqttAddress": "your-mqtt-broker.com", "MqttPort": 8883, "MqttUser": "your-mqtt-user", "MqttPassword": "your-mqtt-password", "MqttUseTls": true, "MqttAllowUntrustedCertificates": false, "MqttTelegramTopic": "TProxy/prod/telegram/update", "MqttStatusTopic": "TBot/prod/status", "TelegramWebhookSecret": "your-random-secret-string", "DisableTelegramTokenValidation": false } } -
Create
docker-compose.ymlversion: '3.8' services: tbot: build: context: ./TBot dockerfile: Dockerfile container_name: tmesh-tbot volumes: - ./config:/tbot/config:ro - ./data:/tbot/data environment: - TBOT_CONFIG_PATH=/tbot/config restart: unless-stopped networks: - tmesh-network tproxy: build: context: ./TProxy dockerfile: Dockerfile container_name: tmesh-tproxy volumes: - ./tproxy-config:/app/config:ro ports: - "5000:8080" # Adjust as needed restart: unless-stopped networks: - tmesh-network networks: tmesh-network: driver: bridge
-
Build and start services
docker-compose up -d
-
Initialize database
docker exec tmesh-tbot dotnet /tbot/app/TBot.dll /updatedb -
Install Telegram webhook
docker exec tmesh-tbot dotnet /tbot/app/TBot.dll /installwebhookVerify webhook installation:
docker exec tmesh-tbot dotnet /tbot/app/TBot.dll /checkinstallwebhook -
Configure your reverse proxy (nginx example)
server { listen 443 ssl; server_name your-domain.com; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; location /update { proxy_pass http://localhost:5000/update; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Telegram-Bot-Api-Secret-Token $http_x_telegram_bot_api_secret_token; } location /status { proxy_pass http://localhost:5000/status; proxy_set_header Host $host; } }
TProxy provides health check endpoints for monitoring:
-
Bot Health:
GET /status/bot/health- Returns 200 OK if bot is healthy
- Returns 503 if bot or gateways are offline
- Query parameters:
gatewayDeadMinutes: Minutes before gateway considered dead (default: 60)gatewayCheckMode: "all" or "any" (default: "all")
-
Individual Gateway:
GET /status/gateway/{gatewayNodeId}- Returns 200 OK if specific gateway is healthy
- Returns 503 if gateway is offline
- Returns 404 if gateway ID not found
-
Bot Status:
GET /status/bot- Returns JSON with detailed bot status and gateway information
Example health check:
curl https://your-domain.com/status/bot/health?gatewayDeadMinutes=30&gatewayCheckMode=any/add [deviceId]- Register a Meshtastic device (e.g.,/add !aabbcc11or/addfor interactive)/remove [deviceId]- Unregister a device (e.g.,/remove !aabbcc11or/removefor interactive)/status [filter]- List registered devices (e.g.,/statusor/status MyDevice)/position [filter]- Show device positions on map (e.g.,/positionor/position MyDevice)/admin <password> <command>- Execute admin commands (see Admin Mode below)/stop- Cancel ongoing registration/removal process
-
Add the bot to your Telegram group
- Make the bot an administrator (required for message reading)
-
Ensure your Meshtastic device is configured
- Set primary channel to match TMesh (default:
LongFastwith PSKAQ==) - Enable "OK to MQTT" in LoRa settings
- Ensure device is connected to the mesh network and has MQTT enabled
- Set primary channel to match TMesh (default:
-
Exchange node information
- On your Meshtastic device, find the TMesh virtual node in your node list
- Open the node and tap "Exchange user information"
- The TMesh node broadcasts its info every hour by default
-
Register your device
Quick method (if you know your device ID):
/add !75bcd15Or interactive method:
/addThen send your device ID when prompted.
You can find your device ID:
- In Meshtastic app: Settings β Device β Device ID
- Format: Can be decimal (e.g.,
123456789) or hex (e.g.,!75bcd15or#75bcd15)
-
Verify with code
- Bot sends a 6-digit code to your Meshtastic device
- You'll receive it as a message on your device screen
- Reply with the code in Telegram within 5 minutes
- Important: Device public key is pinned during registration to prevent MITM attacks
-
Start communicating!
- Messages sent in the Telegram group appear on registered devices
- Messages from devices appear in the Telegram group
- Reply to messages to maintain conversation threading
- Watch the emoji reactions for delivery status
Reply in Telegram to Meshtastic message:
- Long-press or click reply on any message from a Meshtastic device
- Type your reply
- The reply will be sent as a threaded message to the specific device
Reply in Meshtastic to Telegram message:
- Open the message from TMesh in your Meshtastic app
- Use the reply function in your app
- Your reply will appear as a threaded reply in Telegram
View all registered device positions:
/position
View specific device position:
/position MyDevice
This shows:
- Device location on map
- Horizontal accuracy
- Time since last position update
- Devices with unknown positions listed separately
Admin commands require authentication with the password configured in AdminPassword.
Send public announcement:
/admin <password> announce <channel> <message>
Example:
/admin mypass announce LongFast Network maintenance in 1 hour
Send direct message to any device:
/admin <password> text <deviceId> <message>
Example:
/admin mypass text !75bcd15 Testing direct message
Query device information:
/admin <password> nodeinfo <deviceId>
Example:
/admin mypass nodeinfo !75bcd15
Messages use emoji reactions to show delivery status:
| Emoji | Status | Meaning |
|---|---|---|
| βοΈ | Created | Message received, preparing to send |
| π | Queued | Waiting in queue due to rate limiting |
| ποΈ | Acknowledged | Received by mesh network |
| π | Delivered | Confirmed delivery to target device |
| π | Failed | Delivery failed |
| π€· | Unknown | No acknowledgment received after 2 minutes |
For messages sent to multiple devices, you'll see a status message with emoji for each device.
Status Timeline:
- Message created β βοΈ
- Queued for sending β π
- Sent to mesh network β waiting for ACK
- ACK received β π or ποΈ (depending on source)
- If no ACK after 2 minutes β π€·
- If send fails β π
-
Maximum message length: 233 bytes (after PKI overhead)
- English letters: ~1 byte each (β233 characters)
- Cyrillic letters: ~2 bytes each (β116 characters)
- Emoji: ~4 bytes each (β58 emoji)
- Mixed content: Calculate accordingly
-
Rate limiting: Maximum 30 messages per minute (configurable)
-
Queue delays: Shown in status messages when active
-
Gateway routing: Messages routed through optimal gateway automatically
When multiple gateways are configured:
- Automatic Routing: Messages sent through gateway that last saw the destination device
- Hop Limit Optimization: Dynamic hop limits based on gateway relationship
- Health Monitoring: Monitor individual gateway health via HTTP endpoints
- Failover: Automatic routing through alternate gateways if primary unavailable
- Direct Gateway: Routes direct within configured time window (default: 1 hour)
Configured via PingWords and ReplyToPublicPingsViaDirectMessage:
Default behavior (ReplyToPublicPingsViaDirectMessage: false):
- Ping sent on public channel β No automatic response
Direct message mode (ReplyToPublicPingsViaDirectMessage: true):
- Ping sent on public channel β Pong sent via direct message
- Helps verify connectivity without flooding public channels
Customize ping words and pong response in configuration:
"PingWords": ["ping", "hello"],
"Texts": {
"PingReply": "pong from TMesh"
}- Keep messages concise for faster mesh transmission
- Avoid sending messages in rapid succession (rate limiting applies)
- Monitor delivery status reactions
- Use reply threading to maintain conversation context
- If queue delays are high, wait before sending more messages
- Use
/statusto verify device registration - Use
/positionto check if devices are online and their locations - Monitor gateway health via
/status/bot/healthendpoint
| Setting | Type | Description | Example |
|---|---|---|---|
MqttAddress |
string | MQTT broker hostname or IP | mqtt.example.com |
MqttPort |
int | MQTT broker port | 8883 (TLS) or 1883 (plain) |
MqttUser |
string | MQTT username (empty if none) | meshtastic |
MqttPassword |
string | MQTT password (empty if none) | secret123 |
MqttUseTls |
bool | Enable TLS/SSL encryption | true |
MqttAllowUntrustedCertificates |
bool | Allow self-signed certificates | false |
MqttTelegramTopic |
string | Topic for Telegram updates | TProxy/prod/telegram/update |
MqttStatusTopic |
string | Topic for bot status updates | TBot/prod/status |
MqttMeshtasticTopicPrefix |
string | Meshtastic MQTT topic prefix | msh/US/2/e |
| Setting | Type | Description | Example |
|---|---|---|---|
TelegramApiToken |
string | Bot token from @BotFather | 123456:ABC-DEF... |
TelegramBotUserName |
string | Bot username | my_mesh_bot |
TelegramWebhookSecret |
string | Random secret for webhook validation | random-secret-123 |
TelegramUpdateWebhookUrl |
string | Public HTTPS URL for webhook | https://your-domain.com/update |
TelegramBotMaxConnections |
int | Max Telegram webhook connections | 5 |
| Setting | Type | Description | Example |
|---|---|---|---|
SQLiteConnectionString |
string | SQLite database path | Data Source=/tbot/data/tbot.db |
| Setting | Type | Description | Example |
|---|---|---|---|
MeshtasticNodeId |
long | Virtual node ID (must be unique) | 123456789 |
MeshtasticNodeNameShort |
string | Short node name (max 4 chars) | TMSH |
MeshtasticNodeNameLong |
string | Full node name | TMesh-Bot |
MeshtasticPublicKeyBase64 |
string | Virtual node public key | Generated via /generatekeys |
MeshtasticPrivateKeyBase64 |
string | Virtual node private key | Generated via /generatekeys |
OutgoingMessageHopLimit |
int | Default hop limit for outgoing messages | 7 |
OwnNodeInfoMessageHopLimit |
int | Hop limit for node info broadcasts | 7 |
SentTBotNodeInfoEverySeconds |
int | Node info broadcast interval | 3600 (1 hour) |
| Setting | Type | Description | Example |
|---|---|---|---|
MeshtasticPrimaryChannelName |
string | Primary channel name | LongFast |
MeshtasticPrimaryChannelPskBase64 |
string | Channel PSK in base64 | AQ== |
MeshtasticSecondayChannels |
array | Additional channels | See example below |
Example secondary channels:
"MeshtasticSecondayChannels": [
{
"Name": "Services",
"PskBase64": "AQ=="
},
{
"Name": "Admin",
"PskBase64": "different-key-here=="
}
]| Setting | Type | Description | Example |
|---|---|---|---|
GatewayNodeIds |
long[] | Array of gateway node IDs | [123456789, 987654321] |
DirectGatewayRoutingSeconds |
int | Time window for direct gateway routing | 3600 |
BridgeDirectMessagesToGateways |
bool | Bridge admin commands between gateways | true |
| Setting | Type | Description | Example |
|---|---|---|---|
MeshtasticMaxOutgoingMessagesPerMinute |
int | Rate limit for outgoing messages | 30 |
| Setting | Type | Description | Example |
|---|---|---|---|
AdminPassword |
string | Password for admin commands | secure-password-123 |
| Setting | Type | Description | Example |
|---|---|---|---|
ReplyToPublicPingsViaDirectMessage |
bool | Reply to public pings via DM | false |
PingWords |
string[] | Words that trigger ping response | ["ping"] |
| Setting | Type | Description | Example |
|---|---|---|---|
TimeZone |
string | Time zone for timestamps | UTC or America/New_York |
| Setting | Type | Description | Example |
|---|---|---|---|
Texts.PingReply |
string | Response to ping messages | pong |
Texts.NotRegisteredDeviceReply |
string | Message for unregistered devices | {nodeName} is not registered... |
Template variables for NotRegisteredDeviceReply:
{nodeName}- Name of the device{botName}- Telegram bot username
| Setting | Type | Description | Example |
|---|---|---|---|
MqttAddress |
string | MQTT broker hostname or IP | mqtt.example.com |
MqttPort |
int | MQTT broker port | 8883 |
MqttUser |
string | MQTT username | meshtastic |
MqttPassword |
string | MQTT password | secret123 |
MqttUseTls |
bool | Enable TLS/SSL encryption | true |
MqttAllowUntrustedCertificates |
bool | Allow self-signed certificates | false |
MqttTelegramTopic |
string | Topic to publish Telegram updates | TProxy/prod/telegram/update |
MqttStatusTopic |
string | Topic to subscribe for bot status | TBot/prod/status |
TelegramWebhookSecret |
string | Must match TBot secret | random-secret-123 |
DisableTelegramTokenValidation |
bool | Disable token check (NOT recommended) | false |
You can override configuration using environment variables:
export TBot__MqttAddress="mqtt.example.com"
export TBot__TelegramApiToken="123456:ABC..."
export TBot__MqttUseTls="true"
export TBot__AdminPassword="secure-pass"Note the double underscore __ for nested properties.
Your virtual node needs a unique ID in the Meshtastic network. To generate one:
# Pick any unused number, for example:
# Use last 8 digits of your phone number
# Or generate random: echo $((RANDOM * RANDOM))Important: This ID must not conflict with any real Meshtastic devices on your network.
Problem: "Device has not yet been seen by the MQTT node"
Solution:
- Verify device has "OK to MQTT" enabled
- Check primary channel name and PSK match TMesh config
- Ensure MQTT module is enabled on your device
- On your device, find TMesh node in node list and tap "Exchange user information"
- Wait a few minutes for node info to propagate via MQTT
- Check TBot logs:
docker logs tmesh-tbot - Verify gateway is connected and forwarding to MQTT
Checklist:
- MQTT broker is accessible from TMesh
- Meshtastic gateway is connected to MQTT
- Topic prefix matches: check
MqttMeshtasticTopicPrefix - Channel settings match (name and PSK)
- Gateway node IDs configured in
GatewayNodeIds - Check TBot logs:
docker logs tmesh-tbot - Verify gateway health:
curl https://your-domain.com/status/gateway/GATEWAY_ID
Checklist:
- TProxy is running and accessible
- HTTPS endpoint is valid and has valid certificate
- Webhook secret matches in both TBot and TProxy
- Verify webhook:
/checkinstallwebhook - Check TProxy logs:
docker logs tmesh-tproxy - Test webhook endpoint:
curl https://your-domain.com/update
Cause: Rate limiting to protect mesh network
Solutions:
- Wait for queue to clear (check estimated time in status)
- Reduce message frequency
- Increase
MeshtasticMaxOutgoingMessagesPerMinute(carefully!) - Check gateway health - slow gateways increase queue time
Problem: MQTT connection fails with TLS enabled
Solutions:
- Verify MQTT broker supports TLS on the configured port
- Check certificate validity
- For self-signed certificates, set
MqttAllowUntrustedCertificates: true - Check MQTT broker logs for connection attempts
- Test MQTT connection:
mosquitto_sub -h broker -p 8883 --cafile ca.crt -t "#" -v
Check gateway status:
# Check all gateways
curl https://your-domain.com/status/bot/health
# Check specific gateway
curl https://your-domain.com/status/gateway/123456789
# Get detailed status
curl https://your-domain.com/status/botCommon issues:
- Gateway offline: Verify gateway device is powered and connected
- No recent updates: Check MQTT connection from gateway
- All gateways offline: Check MQTT broker availability
Problem: /position shows "unknown position"
Solutions:
- Enable position broadcasts on Meshtastic device
- Verify device is sending position messages to MQTT
- Check that device is registered with TMesh
- Position updates require device to send location data
- Check TBot logs for position message processing
Problem: Replies not maintaining thread
Solutions:
- Ensure you're using Telegram's reply feature (not just mentioning)
- Original message must have delivery confirmation
- Check that device is still registered
- Reply must be sent within message status cache timeout
- Check TBot logs for reply correlation
Problem: Admin commands return error or no response
Solutions:
- Verify password matches
AdminPasswordin configuration - Ensure password is URL-safe (no special characters)
- Check command syntax:
/admin <password> <command> <args> - Check TBot logs for authentication attempts
- Verify bot is admin in the Telegram group
# Backup current database
cp data/tbot.db data/tbot.db.backup
# Apply migrations
docker exec tmesh-tbot dotnet /tbot/app/TBot.dll /updatedbTMesh stores the following data (see PRIVACY.md for details):
- Device registrations: Chat ID, Telegram user ID
- Device information: Node ID, pinned public keys, positions with timestamps
- Messages are NOT stored permanently
- Temporary verification codes (5-minute expiry)
- Device-gateway associations (for routing optimization)
- All Meshtastic messages use PKI encryption (X25519)
- Public key pinning prevents MITM attacks after initial registration
- Telegram webhooks protected by secret tokens
- MQTT connections support TLS/SSL with certificate validation
- Only devices with "OK to MQTT" flag are accessible
- Admin commands require password authentication
- Rate limiting prevents abuse
- Use TLS for MQTT: Set
MqttUseTls: truein production - Validate certificates: Keep
MqttAllowUntrustedCertificates: falseunless using self-signed - Strong passwords: Use long, random passwords for admin commands
- Gateway security: Physically secure your gateway devices
- Key protection: Protect private key in configuration with file permissions
- Regular updates: Keep TMesh Docker images updated
- Monitor access: Review TBot logs for suspicious activity
- Limit bot permissions: Only grant necessary Telegram group permissions
- Backup database: Regular backups of device registrations and keys
For production deployments with MQTT over TLS:
-
MQTT Broker Configuration (Mosquitto example):
listener 8883 certfile /path/to/server.crt keyfile /path/to/server.key cafile /path/to/ca.crt require_certificate false -
TMesh Configuration:
"MqttPort": 8883, "MqttUseTls": true, "MqttAllowUntrustedCertificates": false
-
For self-signed certificates:
"MqttAllowUntrustedCertificates": true
TMesh automatically pins device public keys during registration:
- First registration: Public key is stored and pinned
- Subsequent messages: Validated against pinned key
- Prevents man-in-the-middle attacks
- Key cannot be changed without re-registration
- Protects against compromised MQTT infrastructure
Q: Can multiple Telegram groups use the same Meshtastic device?
A: Yes! A single device can be registered to multiple Telegram chats. Messages from all chats will be sent to the device, and device messages will be broadcast to all registered chats.
Q: How does multi-gateway routing work?
A: TMesh tracks which gateway last saw each device and automatically routes messages through that gateway for optimal delivery. Hop limits are dynamically adjusted based on the device-gateway relationship.
Q: What happens if I lose the verification code?
A: Start the registration process again with /add. You're limited to 5 verification attempts per device per hour.
Q: Can I use TMesh with private MQTT brokers?
A: Absolutely! TMesh works with any MQTT broker that supports QoS 1. TLS/SSL is highly recommended for production.
Q: Does TMesh work with encrypted channels?
A: TMesh requires the primary channel to be configured (default LongFast). You can configure secondary channels in MeshtasticSecondayChannels for additional functionality.
Q: What's the difference between TBot and TProxy?
A: TProxy is a lightweight webhook receiver that forwards to MQTT. TBot contains all the logic for bot operations, Meshtastic communication, message handling, and gateway management. They can run on the same or different servers.
Q: Can I run multiple TMesh instances?
A: Not recommended on the same Meshtastic network - each creates a virtual node. Use one instance with multiple gateway nodes for redundancy.
Q: How do I backup my registrations?
A: Backup the SQLite database file: cp data/tbot.db data/tbot.db.backup. The database includes device registrations, pinned public keys, and position data.
Q: Why do messages sometimes take a while to deliver?
A: TMesh implements rate limiting to prevent mesh network congestion. Check the queue status in bot messages. Multi-gateway setups can improve delivery times.
Q: How accurate is position tracking?
A: Position accuracy depends on your Meshtastic device's GPS. TMesh displays the accuracy radius reported by the device (typically 5-50 meters for GPS).
Q: Can I use reply threading with multiple devices?
A: Yes! When replying in Telegram, the reply is sent only to the device that sent the original message, maintaining proper conversation threading.
Q: How do I monitor gateway health?
A: Use the health check endpoints at /status/bot/health and /status/gateway/{id}. These can be integrated with monitoring systems like Prometheus or Uptime Kuma.
Q: What's the π€· status?
A: "Unknown" status appears when no acknowledgment is received from the mesh network within 2 minutes. The message may still be delivered, but confirmation couldn't be obtained.
# Clone repository
git clone https://github.com/yourusername/TMesh.git
cd TMesh/src/TMesh
# Build TBot
cd TBot
dotnet restore
dotnet build
# Build TProxy
cd ../TProxy
dotnet restore
dotnet build- Copy
appsettings.sample.jsontoappsettings.jsonin each project - Configure with your settings
- Run from IDE or:
dotnet run --project TBot
dotnet run --project TProxyTMesh/
βββ TBot/ # Main service
β βββ BotService.cs # Telegram bot logic
β βββ MqttService.cs # MQTT connectivity with TLS
β βββ MeshtasticService.cs # Message encryption/queuing
β βββ RegistrationService.cs # Device management
β βββ Database/ # EF Core models
β β βββ Models/ # Device, Registration models
β βββ Models/ # Data structures
βββ TProxy/ # Webhook proxy
β βββ Controllers/ # HTTP endpoints
β β βββ TelegramController.cs # Webhook handler
β β βββ StatusController.cs # Health monitoring
β βββ MqttService.cs # MQTT publisher with status
βββ docker-compose.yml # Deployment config
- Backend: .NET 8.0, C#
- Database: SQLite with Entity Framework Core
- MQTT: MQTTnet library with TLS support
- Telegram: Telegram.Bot library
- Cryptography: BouncyCastle (X25519 for PKI)
- Messaging: Meshtastic Protobufs
- Containerization: Docker
Contributions are welcome! Here's how you can help:
- Use GitHub Issues for bug reports
- Include logs (with sensitive data removed)
- Describe steps to reproduce
- Specify your environment (OS, Docker version, MQTT broker, etc.)
- Include gateway configuration if relevant
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Follow existing code style
- Add comments for complex logic
- Update README if adding features
- Test with real Meshtastic devices if possible
- Ensure Docker builds work
- Test multi-gateway scenarios when relevant
- Device removal functionality (
/removecommand) - Message threading and replies between platforms
- TLS/SSL MQTT connections
- Multi-gateway support with intelligent routing
- Position tracking and mapping
- Trace route display
- Admin mode for announcements
- Public key pinning for security
- Health monitoring endpoints
- Ping/pong system
- Multiple channel support
- Unknown status handling (π€· emoji)
- Customizable text messages
- Web dashboard for monitoring and management
- Support for more Telegram bots per instance
- Message history/logging options (opt-in)
- Additional admin controls per chat
- File attachment bridging
- Multi-language support
- Metrics and Prometheus integration
- Automated testing suite
- Group management features
- Waypoint and navigation features
- Weather station integration
- Custom emoji mapping for status
- Meshtastic Project - For the amazing mesh networking platform
- MQTTnet - Excellent .NET MQTT library with TLS support
- Telegram.Bot - .NET Telegram Bot API library
- Bouncy Castle - Cryptography library for PKI
- All contributors and testers who helped improve TMesh
This project is licensed under the MIT License - see the LICENSE file for details.
MIT License
Copyright (c) 2025 Alexander Shakhov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Meshtastic Forum: meshtastic.discourse.group
TMesh is an independent project and is not officially affiliated with or endorsed by the Meshtastic project. Use at your own risk. Always test thoroughly before deploying in critical scenarios.
Meshtastic networks operate on shared radio frequencies. TMesh implements rate limiting and intelligent routing to be a good neighbor, but users are responsible for ensuring their usage complies with local regulations and community guidelines.
Security Notice: TMesh implements public key pinning to prevent MITM attacks, but the initial key exchange during registration relies on the security of your MQTT infrastructure. Use TLS/SSL for MQTT in production environments.
Health & Safety: Do not rely on TMesh for emergency communications. Always have backup communication methods available.
Made with β€οΈ for the Meshtastic and Telegram communities
β Star this repo if you find it useful!
Report Bug β’ Request Feature β’ Contribute