## Installation
### Flathub
```
flatpak install flathub page.codeberg.sesivany.Meshy
```
### Meshy Flatpak repo
1. Add the repo:
```
flatpak remote-add --if-not-exists meshy https://meshy-app.org/meshy.flatpakrepo
```
2. Install Meshy:
```
flatpak install meshy page.codeberg.sesivany.Meshy
```
To install the development version (latest from main branch) instead:
```
flatpak install meshy page.codeberg.sesivany.Meshy//master
```
### AppImage
> The AppImage format is not officially supported. It is provided 'as is' and with no support.
Download [the latest AppImage from](http://meshy-app.org/appimage/Meshy.AppImage), make it executable and run:
```
chmod +x Meshy.AppImage
./Meshy.AppImage
```
### Building from source
#### Flatpak
Install `flatpak-builder` and the GNOME SDK:
```
flatpak install flathub org.gnome.Sdk//50 org.gnome.Platform//50
```
Build and install:
```
flatpak-builder --force-clean --user --install builddir page.codeberg.sesivany.Meshy.json
```
Or to create a `.flatpak` bundle:
```
flatpak-builder --force-clean --repo=repo builddir page.codeberg.sesivany.Meshy.json
flatpak build-bundle repo meshy.flatpak page.codeberg.sesivany.Meshy master
```
All dependencies (including third-party Python packages and native libraries) are handled by the Flatpak manifest automatically.
#### AppImage
Meshy also includes a local AppImage build path intended for packaging tests and release automation.
Build dependencies are the same as the native build, plus:
* `linuxdeploy` (required for AppImage bundling; the script can download it automatically)
* `curl` or `wget` if you want the script to download missing AppImage tools automatically
Build a local AppImage:
```
uv run ./build-aux/appimage/build-appimage.sh --download-tools
```
This is the normal full build path. It writes a staged AppDir and the final AppImage to `dist/`.
If `linuxdeploy` is already installed on your machine, you can skip the download step:
```
uv run ./build-aux/appimage/build-appimage.sh
```
When the script downloads `linuxdeploy` as an AppImage, it runs it with `APPIMAGE_EXTRACT_AND_RUN=1` so CI and other FUSE-less environments do not need `/dev/fuse`.
The final artifact is `dist/Meshy.AppImage`.
Useful variants:
```
# Stop after producing dist/AppDir for inspection
uv run ./build-aux/appimage/build-appimage.sh --appdir-only --download-tools
# Build an AppImage without QR scanner support
uv run ./build-aux/appimage/build-appimage.sh --download-tools --no-qr
```
The generated AppImage uses the same packaging flow that CI uses, so local runs exercise the release path directly.
#### Native build
##### Dependencies
Build dependencies:
* meson (>= 0.62.0)
* ninja
* python3
* glib2 (gio-2.0, glib-compile-resources, glib-compile-schemas)
* gtk4 (gtk4-update-icon-cache)
* desktop-file-utils (desktop-file-validate)
* appstream (appstreamcli)
* gettext (msgfmt)
Runtime dependencies:
* gtk4
* libadwaita
* gstreamer
* libshumate
* geoclue2
* zbar (optional, for QR code scanning)
* python3-gobject (PyGObject)
* python3-pycryptodome (pip: `pycryptodome`)
* python3-pyzbar (pip: `pyzbar`) (optional, for QR code scanning)
* python3-pyserial
* python3-segno (pip: `segno`)
* python3-msgpack (pip: `msgpack`)
On Fedora:
```
sudo dnf install meson ninja-build python3 glib2-devel gtk4-devel libadwaita-devel \
gstreamer1-devel libshumate-devel geoclue2-devel zbar-devel \
python3-gobject python3-pyserial python3-msgpack desktop-file-utils appstream gettext
pip install pycryptodome pyzbar segno msgpack
```
On Debian/Ubuntu:
```
sudo apt install meson ninja-build python3 libglib2.0-dev libgtk-4-dev libadwaita-1-dev \
libgstreamer1.0-dev libshumate-dev libzbar-dev \
python3-gi python3-pycryptodome python3-pyzbar python3-serial python3-segno \
python3-msgpack desktop-file-utils appstream gettext
```
##### Build options
| Option | Default | Description |
|--------|---------|-------------|
| `qr_scanner` | `true` | Enable QR code scanning support (requires zbar and pyzbar) |
| `shortcuts_dialog` | `true` | Enable keyboard shortcuts dialog (requires libadwaita >= 1.8) |
To build without QR code scanning (e.g. if zbar or pyzbar are not available on your distribution):
```
meson setup builddir --prefix=/usr -Dqr_scanner=false
```
The "Scan QR Code" option will be hidden from the UI when built without this feature.
To build without the keyboard shortcuts dialog (e.g. on systems with libadwaita < 1.8 such as Debian Stable):
```
meson setup builddir --prefix=/usr -Dshortcuts_dialog=false
```
The "Keyboard Shortcuts" menu item will be hidden from the UI when built without this feature.
##### Build and install
```
meson setup builddir --prefix=/usr
meson compile -C builddir
sudo meson install -C builddir
```
### macOS
Meshy supports macOS 13+ with full native integration including CoreBluetooth (BLE), CoreLocation (GPS), USB serial, and camera support.
#### Option 1: Homebrew Cask (Recommended)
The easiest installation method for developers:
```bash
brew tap jreznik/meshy https://codeberg.org/jreznik/homebrew-meshy
brew install meshy
```
This installs a 15 MB bundle that uses GTK4 and libadwaita from Homebrew. All dependencies are installed automatically.
To update:
```bash
brew upgrade meshy
```
To completely uninstall (including app data):
```bash
brew uninstall --zap meshy
```
#### Option 2: Standalone DMG
For users who don't want to install Homebrew, a fully self-contained DMG is available:
**Download:** [Meshy-Standalone-20260610.dmg](dist/Meshy-Standalone-20260610.dmg) (151 MB)
Installation:
1. Download the DMG
2. Open the DMG file
3. Drag Meshy.app to your Applications folder
4. Launch Meshy from Applications
The standalone bundle includes everything: Python 3.14, GTK4, libadwaita, and all dependencies. No additional software needs to be installed.
#### Building from Source on macOS
Prerequisites:
```bash
# Install Homebrew dependencies
brew install python@3.14 gtk4 libadwaita libshumate meson ninja
# Install Python packages (required for BLE, GPS, camera, notifications)
pip3.14 install pycryptodome pyserial segno msgpack pygobject \
pyobjc-framework-Cocoa \
pyobjc-framework-CoreBluetooth \
pyobjc-framework-CoreLocation \
pyobjc-framework-AVFoundation \
pyobjc-framework-Vision \
pyobjc-framework-UserNotifications \
pyobjc-framework-libdispatch
```
Build and run:
```bash
meson setup builddir --prefix=/usr/local
meson compile -C builddir
PANGOCAIRO_BACKEND=fc ./builddir/src/meshy
```
**Note:** When using the Homebrew Cask or creating app bundles with the build scripts, all Python dependencies are bundled automatically.
Or create a Homebrew-style app bundle:
```bash
./build-aux/macos/build-homebrew-bundle.sh
open dist-homebrew/Meshy.app
```
Or create a fully self-contained bundle:
```bash
./build-aux/macos/create-standalone-bundle.sh
open dist/Meshy.app
```
See [STANDALONE_BUNDLE_SUCCESS.md](STANDALONE_BUNDLE_SUCCESS.md) for technical details about the standalone bundle.
## Development
### Code Quality Tools
The project uses automated code quality checks via [pre-commit](https://pre-commit.com/) hooks:
**Setup** (one time):
```bash
# Install pre-commit (Fedora)
sudo dnf install pre-commit ruff
# Or with pip
pip install --user pre-commit ruff
# Install git hooks
pre-commit install
```
**What the hooks do**:
- **Ruff linter** - Fast Python linter checking code style, imports, and common issues
- **Ruff formatter** - Automatic code formatting for consistent style
- **i18n check** - Validates that UI strings are marked translatable
- **Standard checks** - Trailing whitespace, EOF fixes, YAML validation, merge conflict detection
**Usage**:
Hooks run automatically on every commit:
```bash
git commit -m "Fix: something"
# Hooks run automatically and may modify files
```
Manual run on all files:
```bash
pre-commit run --all-files
```
Run just ruff:
```bash
ruff check src/
ruff format src/
```
See [.pre-commit-hooks.md](.pre-commit-hooks.md) for detailed documentation.
### Running Tests
The project has an automated `pytest` test suite under `tests/` covering the
pure-Python core modules (protocol, crypto, storage, models, backup, message
handling, and more).
The tests do **not** require GTK/PyGObject to be installed: `tests/conftest.py`
installs a minimal `gi` stub (`tests/gi_stub.py`) so the pure-Python modules can
be imported and tested standalone. It also registers `src/` as the `meshy`
package and provides an in-memory `storage` fixture.
Run the full suite:
```bash
pytest
```
Or with `uv`:
```bash
uv run pytest
```
Beyond the automated tests, GUI behaviour is still validated by manual testing.
### Project Structure
- `src/` - Python source code
- `views/` - UI view components (chat, channels, contacts, map, device, settings)
- `protocol.py` - MeshCore protocol implementation
- `mesh_crypto.py` - Cryptographic operations
- `storage.py` - SQLite database management
- `models.py` - Data models
- `data/` - GTK resources, UI files, icons, schemas
- `po/` - Translation files
- `build-aux/` - Build scripts (AppImage)
## Translations
Meshy uses [Codeberg Translate](https://translate.codeberg.org/) (Weblate) for translations. If you'd like to help with translations, go check [existing ones](https://translate.codeberg.org/projects/meshy/application/). If your language is in the list, you can start translating as a registered user. If it isn't among existing translations, [file a ticket](https://codeberg.org/sesivany/meshy/issues/new) and request it.
meshy/build-aux/ 0000775 0000000 0000000 00000000000 15262126417 0014103 5 ustar 00root root 0000000 0000000 meshy/build-aux/appimage/ 0000775 0000000 0000000 00000000000 15262126417 0015666 5 ustar 00root root 0000000 0000000 meshy/build-aux/appimage/AppRun 0000775 0000000 0000000 00000002732 15262126417 0017025 0 ustar 00root root 0000000 0000000 #!/bin/sh
set -eu
APPDIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
export APPDIR
export PATH="$APPDIR/usr/bin:$PATH"
export PYTHONHOME="$APPDIR/usr"
PYTHONPATH_ENTRIES=""
for LIBDIR in "$APPDIR/usr/lib" "$APPDIR/usr/lib64"; do
for PYTHON_DIR in $(find "$LIBDIR" -maxdepth 1 -type d -name 'python3.*' 2>/dev/null | sort -V -r); do
for PYTHON_SUBDIR in site-packages dist-packages; do
PYTHON_PATH="$PYTHON_DIR/$PYTHON_SUBDIR"
if [ -d "$PYTHON_PATH" ]; then
PYTHONPATH_ENTRIES="${PYTHONPATH_ENTRIES:+$PYTHONPATH_ENTRIES:}$PYTHON_PATH"
fi
done
done
done
export PYTHONPATH="$PYTHONPATH_ENTRIES${PYTHONPATH:+:$PYTHONPATH}"
export LD_LIBRARY_PATH="$APPDIR/usr/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
export GI_TYPELIB_PATH="$APPDIR/usr/lib/girepository-1.0${GI_TYPELIB_PATH:+:$GI_TYPELIB_PATH}"
export GSETTINGS_SCHEMA_DIR="$APPDIR/usr/share/glib-2.0/schemas"
export XDG_DATA_DIRS="$APPDIR/usr/share:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
if [ -d "$APPDIR/usr/lib/gstreamer-1.0" ]; then
export GST_PLUGIN_SYSTEM_PATH="$APPDIR/usr/lib/gstreamer-1.0${GST_PLUGIN_SYSTEM_PATH:+:$GST_PLUGIN_SYSTEM_PATH}"
fi
if [ -f "$APPDIR/usr/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache" ]; then
export GDK_PIXBUF_MODULEDIR="$APPDIR/usr/lib/gdk-pixbuf-2.0/2.10.0/loaders"
export GDK_PIXBUF_MODULE_FILE="$APPDIR/usr/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"
fi
exec "$APPDIR/usr/bin/python3" "$APPDIR/usr/bin/meshy" "$@"
meshy/build-aux/appimage/build-appimage.sh 0000775 0000000 0000000 00000024632 15262126417 0021114 0 ustar 00root root 0000000 0000000 #!/usr/bin/env bash
# Copyright 2026, Jiri Eischmann Meshy is a client for MeshCore LoRa mesh networking devices. Connect to your companion device over Bluetooth, USB or TCP to send and receive encrypted messages, manage contacts, configure channels, and monitor the mesh network.
Features:
New features:
Improvements:
Fixes:
New features:
Improvements:
Fixes:
New features:
Improvements:
Fixes:
Improvements:
New features:
Improvements:
Fixes:
First stable release of Meshy.
The page you requested could not be found.
{% endblock content %} meshy/docs/themes/easydocs/templates/anchor-link.html 0000664 0000000 0000000 00000000066 15262126417 0023333 0 ustar 00root root 0000000 0000000 meshy/docs/themes/easydocs/templates/index.html 0000664 0000000 0000000 00000017440 15262126417 0022241 0 ustar 00root root 0000000 0000000 {% if config.extra.favicon -%} {% set _favicon = config.extra.favicon -%} {% if (_favicon is starting_with(pat="http")) == false -%} {% set _favicon = get_url(path=config.extra.favicon) -%} {% endif -%} {% endif -%}