mtsh is a command-line tool that provides a remote shell over Meshtastic text messages. It pairs with a Meshtastic device (serial, BLE, or HTTP), runs shell commands received over the mesh, and streams the output back in chunked text responses.
- Remote execution server: Accept Meshtastic messages containing shell commands and return their output.
- Terminal proxy client: Send commands to a remote node and print responses in your local terminal.
- Chunked messaging: Splits large outputs into multiple text message chunks with optional acknowledgements, retries, and progress reporting.
- Adaptive compression: Compresses payloads when it reduces size to fit more data per message.
- Batched acknowledgements: Confirms multiple chunks together for reliable transfers with fewer round trips.
- Filtering and safety controls: Configure channel usage, direct-message-only mode, and whitelist allowed node IDs.
- Go 1.24 or later (see
go.mod). - Access to a Meshtastic device via serial port, BLE, or HTTP API.
go build -o mtsh main.goThis produces the mtsh binary in the current directory.
Download the binary for your platform from the latest release: https://github.com/playmean/mtsh/releases/latest
Example:
curl -LO https://github.com/playmean/mtsh/releases/latest/download/mtsh-X.Y.Z-linux-amd64
chmod +x mtsh-X.Y.Z-linux-amd64
sudo mv mtsh-X.Y.Z-linux-amd64 /usr/local/bin/mtsh
mtsh versionMost commands require exactly one of --channel or --to; using both together is not supported. The nodes command needs neither. Common flags include --port, --chunk-bytes, --hop-limit, and verbosity controls. If --port is omitted, mtsh attaches to the first compatible serial device. Use -h for full command help.
To connect over the Meshtastic HTTP API, pass --port http://host:port (or https://...).
Run on the device that should execute shell commands.
Recommended direct-message-only startup (restrict execution to known nodes):
./mtsh server --dm-only --dm-whitelist 0x12345678,0x23456789Optional broadcast-on-channel startup (for users who understand the risks):
./mtsh server --port /dev/ttyUSB0 --channel 1 --cmd-timeout 20sUseful flags for either mode:
--no-chunk-ackto disable chunk acknowledgements if you prefer timed delays.--allow-plainto accept plain text commands (see below).
Use --allow-plain when integrating with tools or apps that sends plain text messages. In this mode:
- Incoming non-empty text packets are treated as shell commands without extra flags or prefixes.
- Responses are returned as plain text chunks instead of proto-formatted messages.
- When an output spans multiple chunks, each chunk starts with
N/M(for example1/5) so receivers can reassemble the stream. - Chunk delays follow
--chunk-delay, and no acknowledgements or promos are sent; the server simply transmits the chunks with the configured pause.
Run the server in a container with a USB serial device passed through:
docker run --rm -it --device /dev/ttyUSB0 ghcr.io/playmean/mtsh:latest server -v --dm-only --dm-whitelist 0x12345678Use from a node that should send commands and collect output:
./mtsh client --port /dev/ttyUSB1 --to 0x12345678 --wait-timeout 2m "uname -a"Optional flags:
--stream-chunksto print each chunk as it arrives (disables buffering/progress).--no-progressto hide buffered download progress.
Send a local file to the remote shell host:
./mtsh cp ./bin/tool /tmp/tool --port /dev/ttyUSB1 --to 0x12345678Notes:
- Respects
--chunk-bytesand--no-chunk-ackto tune transfer behaviour. - Compresses files automatically when it shrinks the payload.
Broadcast or direct-message a node without running the shell:
./mtsh msg "hello mesh!" --port /dev/ttyUSB0 --to 0xabcdef01Display nodes stored on the connected device, including names and last-heard info:
./mtsh nodes --port /dev/ttyUSB0Enable verbose debug logging with -v or --verbose on any command to inspect device connection details, chunking behaviour, and acknowledgements.
Standard Go tooling works with this repository. Run go fmt ./... and go test ./... before committing changes where applicable.