# MeshTerm **A full-screen terminal companion for [MeshCore](https://meshcore.io/) LoRa mesh devices** — tune your radio, chat across the mesh, watch the network live, and keep a longitudinal record of everything you overhear, all from a connected serial, Bluetooth, or network (TCP) companion. MeshTerm is interactive by default: a modern, keyboard-driven TUI built on [Rich](https://github.com/Textualize/rich) and [prompt_toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit). It is also fully scriptable — **every screen has a mirror `meshterm` subcommand** — so the same capabilities drive both an evening of exploring the mesh and a cron job. Every packet the radio overhears is recorded to a local SQLite database, so the longer you run it, the more your mesh's history is worth.
> MeshTerm is a side project, not a product. Bug reports are very welcome — but please > open an issue before writing a pull request. [More on how it's run](#how-this-project-is-run). --- ## Why MeshTerm - **One tool, two faces.** Launch `meshterm` for a full-screen menu; pass a subcommand for a scripted one-shot. The registry that builds the menu builds the CLI, so they never drift apart. - **It remembers.** A passive monitor logs every overheard advert and telemetry frame — SNR, RSSI, shared location — to SQLite from the moment the radio opens. Maps, contact lists, charts, and the Time Machine all read back that history. - **It measures.** Live trace with per-hop reliability, a coarse→refine→verify TX-power sweep, and a walkable graph of how the mesh actually hangs together. - **It talks.** Live channel and direct messaging, store-and-forward courier delivery for contacts that aren't there yet, and shareable channels as QR codes and `meshcore://` links. - **It connects however your companion does.** Serial (USB), Bluetooth LE, or TCP (a WiFi board or a network proxy) — the discoverable transports are auto-found and picked interactively, a network device is named by hand, and every kind reconnects live when a link drops. No radio? A built-in simulator covers development. - **It works offline.** The street map caches OpenStreetMap tiles to disk and falls back to a blank grid when there's no network — it never needs the internet to run. ## Install ### Download a build **[⬇ Latest release](https://github.com/jpmartineau/MeshTerm/releases/latest)** — one file, no Python needed. Windows, macOS (Intel and Apple silicon), and Linux (x64 and ARM64, so the uConsole is covered). MeshTerm is a terminal program, so **open a terminal and run it from there.** You can double-click it and it will work, but you'll get whatever console your system picks, and if anything goes wrong at startup the window closes before you can read why. **Windows** — open Windows Terminal or PowerShell, `cd` to your downloads, then: ```powershell .\meshterm-0.2.7-windows-x64.exe ``` **macOS and Linux** — open Terminal, `cd` to your downloads, then: ```bash chmod +x meshterm-0.2.7-* ./meshterm-0.2.7-* ``` ### A word about terminals, on Windows MeshTerm is drawn with emoji icons, braille charts and powerline path chips. Whether you see them is up to your *terminal*, not really your font: a modern terminal, asked for a character its font doesn't have, quietly borrows it from another font on the machine. That's why the app looks right in Windows Terminal, in VS Code's terminal, and on macOS and Linux — usually with a font that contains almost none of it. The classic Windows console — the black `cmd.exe` window you get from a double-click — doesn't borrow. It draws what its one font holds and empty boxes for everything else, and no font fixes the icons there: the only two fonts on a Windows machine with emoji in them are proportional, and a console won't take a proportional font. So when MeshTerm lands in that console it just **moves to Windows Terminal** — it says so, and opens there. Nothing is installed and nothing is changed; it's one window instead of another, and it's the whole app exactly as the screenshots show it. Windows Terminal is already on every Windows 11 machine and is a free install on Windows 10. Where there's no Windows Terminal to move to, MeshTerm offers the next best thing instead — the charts and marks, without the icons. It ships [Cascadia Mono PL](https://github.com/microsoft/cascadia-code), Microsoft's own console font and one of the very few monospace faces that carries braille at all, and will install it just for you: no administrator rights, nothing downloaded. If you already have Cascadia it simply switches to it. Preferences → Display → Console setup turns all of this off if you'd rather stay put. Rename it to something you don't mind typing and put it somewhere on your `PATH`, and it becomes just `meshterm` from anywhere. > **Your system will complain the first time, and it's right to.** These builds aren't > code-signed — signing costs real money on both platforms and this is a free side > project. > > - **macOS**: *"cannot be opened because the developer cannot be verified"*. Clear it with > `xattr -d com.apple.quarantine meshterm-0.2.7-*` and run it again. > - **Windows**: *"Windows protected your PC"*. Click **More info → Run anyway**. > > Every release ships a `SHA256SUMS` file if you'd rather check the download first. > **Trying a build without touching your real data.** MeshTerm keeps everything in > `~/.meshterm` — your history database, contacts, channel keys — and *every* copy of > MeshTerm uses that same folder. Set `MESHTERM_HOME` to try one in isolation: > > ```bash > MESHTERM_HOME=~/meshterm-test ./meshterm-0.2.7-* # macOS, Linux > ``` > ```powershell > $env:MESHTERM_HOME = "$HOME\meshterm-test"; .\meshterm-0.2.7-windows-x64.exe > ``` > > Same variable if you run two radios and want them kept apart. ### Or install with pip You need **Python 3.10 or newer**. That's the only requirement — MeshTerm pulls in everything else itself. ```bash pip install git+https://github.com/jpmartineau/MeshTerm meshterm ``` `meshterm` opens the full-screen menu. That's the whole install. **Rather not touch your system Python?** [pipx](https://pipx.pypa.io/) puts the app in its own private environment and still gives you a plain `meshterm` command: ```bash pipx install git+https://github.com/jpmartineau/MeshTerm ``` **No radio yet?** `meshterm --mock` runs the entire app against a simulated mesh, so you can have a look around before you buy anything. ### Coming **`pip install mesh-term`**, once the package is published. It isn't yet — the name question is still being sorted out ([why](https://github.com/pypi/support/issues/12162)). Setting up for development instead? That's in [CONTRIBUTING.md](CONTRIBUTING.md). ## Quick start ```bash # Interactive full-screen menu (auto-discovers serial + Bluetooth companions) meshterm # Connect over Bluetooth (address from `meshterm devices`) meshterm --ble AA:BB:CC:DD:EE:FF info # Connect over the network to a TCP companion (host[:port], port defaults to 5000) meshterm --tcp 192.168.1.50 info # No radio attached? Use the built-in simulator for development. meshterm --mock # On a ClockworkPi PicoCalc (Luckfox Lyra / Calculinux) the handheld flavour is # auto-detected: a 53-column layout, a 16-slot palette, a compact glyph language on a # custom console font, and an F-key hint lane. Preview it anywhere, or inspect it: meshterm --mock --platform picocalc meshterm specimen ``` Handheld bring-up (fonts, palette, boot services) lives in `scripts/calculinux-setup.sh` + `scripts/calculinux-console-font.sh`. ## Features MeshTerm's menu answers one question — *what would you like to do?* — so its five sections are named for the doing, not the subject. Every interactive screen is listed here with its scripted equivalent, where one exists. ### 💬 Message — the people on the other end | Feature | What it does | Scripted | | --- | --- | --- | | **💬 Chat** | Live full-screen channel and direct messaging — a scrolling transcript with a pinned input line where sent and received messages stream together. Every message is logged; unread counts show in the menu header. | `meshterm chat send / history / list` | | **📻 Channels** | Create, join, reorder, mute, and share mesh channels — with QR codes and `meshcore://` share links. | `meshterm channels list / add / join / import / share / clear` | | **📨 Courier** | Store-and-forward outbox for contacts that aren't reachable yet. Queue a message; it goes out (with ack tracking and polite exponential backoff) the moment the contact is next heard, or at a scheduled time. | `meshterm courier queue / list / send / cancel / clear` | | **👥 Contacts** | This node and its known contacts — a recency heat-map, overheard packet counts, and full public keys with the path-hash prefix highlighted. | `meshterm contacts` | ### 📊 Watch — what the mesh is doing, and what it did | Feature | What it does | Scripted | | --- | --- | --- | | **📊 Dashboard** | The live mesh overview: a two-hour all-packet activity chart with a pulse line, session traffic tallies by packet class, and window RF health beside the radio's own live numbers. Repaints every second. | — | | **📰 Live feed** | Every packet as it arrives, newest first: time, what the frame *is* (its parsed payload class), and what it is *about* — the node for an advert, the channel for a channel text, sender → recipient for a direct message or a request, the tag for a trace — with reception quality beside it. Enter opens any row in the packet viewer, route graph and all. | — | | **🚨 Watchtower** | A passive sentinel over the nodes you star: silence alarms when a watched node goes quiet, SNR-sag warnings when reception degrades, recovery notes when it returns, and a heads-up when a never-before-seen node appears. Runs in the background all session; unacked alerts show as a header badge. | — | | **⏳ Time machine** | Everything the recorder ever heard, as braille charts: reception volume, the median-SNR band, hour-of-day rhythm, packets and nodes per day, and first-ever arrivals — over a switchable 24h / 7d / 30d / all-time window, per node or mesh-wide. | — | | **🎧 Monitor** | Passive recording is always on in the app. On the CLI, capture a bounded foreground window, tailing each overheard packet and summarising when it ends. It transmits nothing — it only listens. | `meshterm monitor --seconds 60` | ### 🧭 Explore — where the nodes are and how they reach each other | Feature | What it does | Scripted | | --- | --- | --- | | **🌍 Map** | Located nodes plotted over a real OpenStreetMap street basemap rendered as Unicode braille (streets, rivers, place names). Pannable and zoomable; repeaters highlighted and drawn on top. Falls back to a blank grid offline. | `meshterm map` (one-shot render) | | **🌐 Mesh walk** | The mesh's *observed shape*, walked one node at a time: an evidence graph built from trace walks, firmware routes, overheard relay chains, and repeater neighbour tables. SNR-coloured braille edges, quality bars, Enter to walk, ⌫ to backtrack, type to find any node. The map answers *where*; the walk answers *how it hangs together*. | — | | **🎯 Trace target** | A live trace screen: pick a target and watch each trace stream in hop by hop, with running per-hop medians and reliability. Compose or force a route through specific repeaters. A trace transmits **exactly once** (repeaters can blacklist nodes that burst) — sample more by running it again. | `meshterm trace --target …` | | **👣 Trace path** | The other half of tracing: compose the whole circuit by hand — out and back whichever way you choose — and walk it. The path composer suggests each next hop from the links actually observed, strongest first, and can fetch a repeater's neighbour table over the mesh when you hold its admin password. | `meshterm trace --path …` | | **🏆 Trophy case** | Every trace that comes home is scored, on seven boards: longest distance, farthest node, longest single leg, most nodes (with and without revisits), weakest surviving link, and biggest enclosed loop. Records are kept per hash width, and a record walk must be a *trail* — no link crossed twice the same way. | `meshterm records` | ### 🔧 This node — the radio in your hand | Feature | What it does | Scripted | | --- | --- | --- | | **📋 Device info** | The connected companion's identity and full radio configuration at a glance. | `meshterm info` | | **🔧 Device config** | View and *stage* changes to every setting (name, radio, behaviour, experimental) for review before applying, with TOML backup/restore. | `meshterm config` / `config set