Changelog
Version notes from the MeshChatX repository, newest first. Unreleased work stays at the top until it ships.
-
v4.8.6 Unreleased
Added
- WebSocket hardening: Rate limits per socket, message checks, optional topic subscribe (all topics by default),
config.changedevents, sequence ids on broadcasts, idle disconnect, clear error replies, Nomad size caps with chunked large downloads, optional binaryrns.linkafterws.caps, andGET /api/v1/debug/websocket. - Archives: Search shows short previews and pages of results. Cards can preview Micron, Markdown, and HTML. You can recrawl a page from the viewer. On smaller screens the layout stacks.
- Smart Crawler: Limits how far and how often it crawls (one request per node per day, shallow depth, page caps). Honors
# nocrawland Archives opt-out. - Nomad private tabs: Purple private tabs (Ctrl+Shift+P). They are not archived, favourited, restored, or shown in the URL or history.
- Micron editor publish: Publish can create a mesh server, start it, upload the current tab, and open it in NomadNet. A stopped server is started first.
Fixed
- Messages: Coming back to Messages with the same chat still open marks it read and clears badges and desktop notifications.
- NomadNet: Opening a node while the app is still connecting no longer hangs on Loading. Downloads wait for the connection, then fail with a clear error if it never comes up. A hung page no longer freezes the whole Nomad UI.
- Desktop app: Startup and crash screens use your saved theme right away, so dark mode does not flash white.
- Map: Discovered interfaces and telemetry markers update again.
- UI: Dropdowns follow the theme colors.
Changed
- WebSocket:
/wsmax frame size is 16 MiB. Large Nomad payloads use chunks. Missing Origin is rejected on non-loopback binds when password auth is off. - Reticulum: Updated to RNS 1.5.2.
- Smart Crawler: Finished crawls stay marked finished until you refresh. Retries wait longer between attempts. At most two crawls run at once.
- Docs: Short READMEs at the repo root. Longer install and contributor guides live under
docs/en/.
- WebSocket hardening: Rate limits per socket, message checks, optional topic subscribe (all topics by default),
-
v4.8.5 Released
Changed
- About: Open-file count and nofile limits sit next to memory usage.
- Docker Compose:
nofileis 65536. Process start raises the soft limit when the hard limit allows it.
Fixed
- Docker (too many open files): Announce-thread SQLite handles close when the thread exits. RNS ratchet writes share one worker. Websocket reconnects close the previous socket. Finished RNS websocket clients drop their sockets. Log rollover writes to stderr when the log file cannot reopen.
- Messages: Conversation list and thread load keep working while announces arrive. A closed database handle reopens instead of returning HTTP 500.
- UI WebSocket:
/wsreturns 503 after 64 clients. Ready-status broadcasts JSON-encode the payload so aiohttp send_str does not reject a dict. - CSP: Drop invalid
ws://[::1]:*connect-src entries. Permissions-Policy is set without a duplicate Feature-Policy header.
-
v4.8.4 Released
Changed
- Community interface presets: Build-time fetch uses https://meshchatx.com/api/mcx-interfaces instead of directory.rns.recipes.
- Android release APK: GitHub assets use ReticulumMeshChatX-vVERSION-android-universal.apk, matching desktop installer names.
- Confirm and prompt dialogs: Electron uses the in-app dialogs instead of native OS prompts.
- Collapsed sidebar: The active rail item uses an inset pressed fill instead of the expanded blue pill. Collapsed buttons are square.
- Map announce: Off until a pack is published.
Fixed
- Relay Chat room keys: Hosted /list marks keyed public rooms with [+k] and never sends the key. Clicking an unkeyed listed room joins without a prompt. A stored key or a later bad key (+k) error still supplies or asks for the key.
- Map catalog: Catalog on Discover lists packs instead of hanging. Empty catalogs show a message.
- In-process memory growth: Announce-rate, LXMF flood, RNCP, auto-resend, Mesh Server, and map overlay lists prune on append instead of waiting for About to poll. Identity teardown drops ringtone, notification-sound, translator, and community-interface managers.
- macOS universal: Intel NumPy OpenBLAS dylibs stay in the freeze tree so the x86_64 slice can load numpy.
- Relay Chat loopback: Typed /help and /nick stay above the hub notice when talking to a locally hosted hub.
- macOS desktop: Frozen builds include Codec2 so LXST calls start instead of crashing on a missing libcodec2 dylib.
- macOS universal: The Intel slice of a universal Mac build runs as x86_64 instead of starting as arm64 and failing on zlib.
- Disconnected banner: Stopping or restarting the backend keeps the red banner until the backend answers again.
- Messages (path wait): Direct and opportunistic send wait for a path to the peer lxmf.delivery destination. A pasted identity hash is not a mail address and is no longer waited on.
- Telephone dial overlay: Initiation status stays while LXST is calling or ringing, so the overlay does not close mid-ring.
- Relay Chat: A WELCOME timeout tears the link and reconnects. Outbound chat is recorded locally only after it goes on the wire. A kline or ban with no room name leaves every joined room.
- RNCP and FileSync: Send, save, and sync-root paths refuse reserved identity-storage tops and files named identity. RNCP also refuses the shared storage identities tree, session_secret, and app_security.json.
- Plugins: Install refuses a source tree or zip that contains symbolic links, so copytree cannot ingest host files.
- Docs zip import: Zip members with NUL bytes, absolute paths, parent directory segments, or a colon are skipped.
- Plugins: Hook dispatch returns immediately when the plugin runtime is disabled. Locale lookup stays inside the plugin install tree.
- Map overlays: Cache writes refuse a .tmp that is already a symlink or directory.
- Identity switch: Relay Chat mention badge and LXMF unread refetch after switch. Compose drafts stay in the previous identity's localStorage bucket. Auto-resend locks drop with the torn-down identity. Deferred managers that finish after teardown starts are shut down and discarded. Database close invalidates thread-local connections.
- Messages: An LXMF upsert with blank content, title, or fields keeps the stored values instead of wiping them.
- NomadNet: Keep-alive tabs skip background polls when the Nomad route is not on screen. Opening a destination hash in the URL activates that tab.
- Telephone: AudioContext close is skipped when close() is missing, so Android call teardown does not throw.
- Map local URL: Unparseable tile or nominatim URLs are not treated as local.
-
v4.8.3
Fixed
- Electron AppImage: Loading screen shows the real package version instead of v0.0.0, and desktop IPC works in packaged builds.
- Database backup: Identity zip backups no longer fail schema upgrades when files have timestamps before 1980.
- Navigation: Sidebar works after Docker or backend restart without a full page reload.
- NomadNet tabs: Tab labels stay visible on short screens such as 800x600.
-
v4.8.2
Added
- Local developer tooling: Vue DevTools overlay on
task dev(Vite serve at http://127.0.0.1:5173 only,MESHCHAT_VUE_DEVTOOLS=0to disable). Productionvite buildkeeps__VUE_PROD_DEVTOOLS__false.task debugwraps the backend with debugpy on127.0.0.1:5678. Debugger launch configs: MeshChatX: Vite + Python. Playwright e2e forces DevTools off. - Public demo mode: Read-only mesh showcase via
MESHCHAT_DEMO_MODEor--demo(blocked sends and API mutations, in-app demo banner). Optional ALTCHA v3 on login/setup,MESHCHAT_AUTH_PAGE_HINTfor login page text, and docker-compose.demo.yml for Coolify. - Database upgrades: Automatic backup-pre-migrate-*.zip before schema migrations (skip with
MESHCHAT_SKIP_PRE_MIGRATE_BACKUP=1). CLI--list-backupsand--export-backupto list or copy backups for rollback. Post-migrate quick_check, structured migration logs, pre-migrate zip retention (five by default), storage lock for single-writer volumes, and N-1/N-2 fixture upgrade tests. - App sidebar: Grouped layout is the default. Primary links sit under Communicate, Explore, Network, and App labels, with a More disclosure for Archives, Interfaces, Identities, About, and similar destinations. Appearance setting
app_sidebar_layoutcan switch back to Classic (one flat list and the expanded identity footer). The grouped footer is a compact identity chip with announce, QR, and auto-announce interval (15 minutes through 24 hours). Press and hold a nav item or section while the sidebar is expanded to reorder links and groups. A save icon appears in the collapse row. Order is stored in the browser. - Tools page: Tools are listed under Diagnostics, Transfer, Messaging, Network, and Other instead of one ungrouped grid.
- Mesh Server executable pages: A per-node opt-in runs pages marked executable (Mesh Server toggle, or
chmod +xon Linux and macOS) as shebang scripts. On Windows the executable flag is stored in the node config and the shebang interpreter is resolved from PATH. Requestfield_*andvar_*values pluslink_idandremote_identityare passed in the environment. Script stdout is the page body. Failures return a controlled error page. Editing a page always shows the file source, never the script output. - Electron hardware choosers: Serial, USB, and Bluetooth device requests in the desktop app open a native picker (RNode flasher, Web Serial). Electron does not show Chromium's chooser dialog for those APIs.
- Propagation nodes: Preferred node hash can be pasted from the clipboard on the Propagation Nodes page and in Settings, with path-request and clear actions on the preferred row.
- Database health toast: SQLite health issues from
/api/v1/app/infoshow a warning toast once per issue fingerprint per session, not on every 15-second poll. Identity switch resets the fingerprint. The same text remains on About > Database. - Landlock extra read roots: The Sideband command-plugin directory from Settings is added as a Landlock read root when that folder exists on disk. A missing path is not widened to its parent.
/sysis a read root so USB serial metadata lookups can stat sysfs. - Map discovery (
map-data-v1): Publish sanitized GeoJSON/KML/KMZ packs from the Map page. Peers hear a slim announce (nlabel,ccount), fetch/catalogand/map/<id>over an RNS Link, and add the pack as a local overlay. NomadNet/file/and RNGit stay as an advanced source. Schema 55 addsmap_published. - KML/KMZ sanitizer: Strips NetworkLink, remote hrefs (including javascript and vbscript), unsafe KMZ entries (svg/html/js), and DTD/ENTITY before overlay import or mesh publish. Remote icon URLs are removed, not left for OpenLayers to fetch.
- RNStatus: Shows the same fields as the
rnstatusCLI, including the I2P.b32.i2paddress (copyable), transport/network IDs, totals, path-request rates, tunnel state, and a show-all-interfaces toggle. RX/TX rates are labeled as speeds, not packet counts.
Changed
- Community interface presets: Runtime no longer fetches directory.rns.recipes. Presets come from bundled
community_interfaces.json(refreshed at build time viascripts/build_community_interfaces_json.py) or an optionalpublic/community_interfaces.jsonoverride. - Runtime clearnet fetches: Micron WASM no longer installs from GitHub at runtime (local file upload or the build-time bundle remains). Repository bundled-wheel refresh is build-time only (
POST /api/v1/repository-server/refresh-bundledis gone). - Slow-interface path and link waits: Cold path requests use
path_response_windowinmeshchatx/src/backend/path_utils.py. It takesReticulum.get_first_hop_timeout(notTransport.first_hop_timeout, which is the local socket timeout on a shared rnsd client) and an airtime floor from the slowest online interface, clamped toRNS.Reticulum.MINIMUM_BITRATE(5 bps). New links wait onlink.establishment_timeoutplus 5 seconds. Nomad pages, RNS Link API, RNCP, FileSync, LXMF outbound path prep, LXST dial, map fetches, remote management, bug-report send, path-probe defaults, and Relay Chat hub identity recall use those helpers. A 15 second window cannot finish a 234-byte path exchange at 125 bits per second. - Dependencies: LXMF 1.1.1. Unanswered path requests during propagation peer sync now apply sync backoff instead of retrying immediately. Requires RNS 1.4.2 (already pinned).
- App sidebar: Network Visualiser is under Explore with Nomad Network and Map, not in More.
- Messages empty state: The no-conversations view sits near the top of the list without a dashed box. Actions are See announces (Announces tab) and Add contact.
- Mesh Server UI: Remaining mesh-server strings (create, start, stop, toasts, empty copy) are translated in the bundled locales. Announce interval 0 is labeled as manual-only (no periodic timer).
- Docs: Getting started lists sidebar More destinations. Linux sandbox guide covers USB serial under Landlock, executable Mesh Server pages, and
location_cmd/ Sideband plugin folders that must already exist on an allowed root at process start. - Map page: Discover / Publish / Layers / Offline side tabs. Drawing toolbar keeps Select, Point, Line, Polygon, Circle, Measure, one Bearing tool, Save, Load, and Locate. MBTiles export, Share, and Ping left the always-visible row (Ping stays on right-click).
- Map tiles and markers: TileCache keeps a memory LRU and no longer writes IndexedDB metadata on every hit. Marker updates reuse Features by destination hash and cluster on a grid. Inactive MapBrowser tabs wait until first shown before constructing OpenLayers. Online tile fetch timeout is 8s.
- Settings search: Query splits on hyphens and punctuation (
dark-mode), matches compact forms (darkmode), and treats 1-2 character tokens as whole words somedoes not hit Theme. Tab names and camelCase section ids are searchable. The empty state is translated. Matching tab counts stay visible while searching./focuses the search field. - Network visualiser planet view: Each interface is its own globe around your node. Peers sit on the interface they use. Drag orbits the system, scroll zooms. WebGL only.
- Mobile header: Relay Chat (
forum) and Telephone (phone) icon buttons sit in the top bar with Compose and propagation sync. Relay Chat is omitted when RRC is disabled.
Fixed
- Sidebar last announced: Relative time on the identity footer now recomputes on the 1-second shell poll. Auto-announce WebSocket events include
last_announced_at, so the stamp updates without a page reload. - Docker frontend build: Copy
scripts/vite-dx.mjsinto the Node stage.vite.config.jsimports it for Vue DevTools gates, sovite buildfailed with UNRESOLVED_IMPORT when the file was missing from the image. - Map KMZ import: ArcGIS KMZ files (including GhostMaps ATAK exports) failed in two ways. An unused .xsl balloon stylesheet next to doc.kml was treated as an unsafe zip entry and showed "Could not read vector file." HTML balloon text inside CDATA left a stray CDATA closer after sanitizing, so OpenLayers parsed zero features. Sidecars that are not KML or raster icons are skipped. CDATA HTML is flattened to escaped plain text. Placemarks and zip-local PNG/JPEG/GIF/WebP icons still import.
- Collapsed sidebar: Icons in the 64px app rail (nav, More, collapse chevron, identity chip, announce) and the Messages/Nomad collapse chevrons sit on the vertical center line. Collapsed nav links no longer keep the expanded right-margin offset.
- Messages (personal notes): Sending to your own LXMF address or identity is stored locally as delivered (method local) without LXMF router outbound, so self-chat no longer hangs in a waiting state.
- Messages sidebar: Conversation list updates optimistically when you press Send, before the server acknowledges the message.
- E2E: Playwright API helpers attach CSRF tokens for direct backend POSTs;
pretest:e2einstalls Chromium before the suite runs. - Android LXST / Codec2: When the Chaquopy pycodec2.so extension is an empty stub, fall back to a ctypes Codec2 binding over the bundled libcodec2.so so LXST Codec2 voice profiles work on device. Still preload jniLibs Codec2 and reload soft-imported LXST bindings after probe.
- Android RNode flasher: Open native flasher returns a real status, keeps USB-serial classes through R8, uses an ActionBar theme, and surfaces startup failures instead of silently doing nothing. Bluetooth Open settings tries GrapheneOS-friendly fallbacks (app details, Bluetooth settings, general Settings) instead of toasting unavailable. In-page Flash/DFU/detect actions reopen the native flasher window and stop instead of failing with a confusing web_serial_unavailable error when Web Serial is not present. Firmware is chosen with a file picker instead of HTTP download. The picker compiles on API 24 (byte stream read instead of
readAllBytes) and uses an effectively final filename in the background loader lambda. - Connection banners: Do not flash disconnected on startup before the first successful WebSocket open. Debounce disconnect UI for 2.5s and only show reconnected when the disconnect banner was actually shown. Foreground recovery prefers a ping for longer before forcing a reconnect.
- Android calls: Clarify that the web audio bridge on Android uses native mic and speaker through the telephone audio bridge, not browser getUserMedia. Toggle label reads "Native Audio Bridge" on Android, and the browser Microphone/Speaker device picker (which does nothing on native audio) is hidden there.
- Browser calls (Docker / HTTPS): Refresh Devices prompts with bare getUserMedia({ audio: true }) first so Brave and Chromium show the microphone dialog. Constrained requests (echoCancellation and friends) often return NotFoundError before permission is granted and never ask. Clearer toasts for insecure HTTP and pending/denied mic permission.
- HTTP security headers: Send Permissions-Policy allowing microphone, camera, bluetooth, serial, and usb for this origin so reverse proxies that omit the header do not block capture or RNode flasher hardware APIs by default.
- RNode flasher Bluetooth: Detect Brave's disabled-by-default Web Bluetooth API, show how to enable
brave://flags/#brave-web-bluetooth-api, and offer Try Bluetooth / Recheck actions. Web Bluetooth has no mic-style prompt. The device chooser from requestDevice() is the permission UI. - UI language: Persist language changes over the config HTTP API (not WebSocket-only), normalize legacy locale codes, and stop the Reticulum manual language picker from overwriting app UI language.
- Network visualizer: WebGL background follows light theme and clears while the WASM scene is still loading. Boot theme removes stale dark class when light is selected. WebGL live layout springs are 200 (me to interface) and 240 (peers) instead of 440/500, repulsion is 1800 instead of 5600, and ticks sleep once a step moves less than 0.15 world units. Planet view keeps nodes on the front-to-back hemisphere (no wrap past the far pole), draws far nodes under near ones, and reloads after an identity switch instead of keeping the previous identity's path table and positions.
- Electron / Android shell origin allowlists: Parse URLs before treating them as the local backend.
http://127.0.0.1:9337@example.comis example.com with userinfo, not loopback. Electron attacheswill-navigate,will-redirect, andwill-frame-navigateon everyweb-contents-created, deniesdata:and untrustedblob:in-window, and no-ops preloadwindow.electronIPC unless the page isfile:loading/crash,127.0.0.1/localhost:9337, or a trusted blob. Android WebView navigation is the configured backend origin only (not any loopback port), rejects userinfo, and deniesdata:becauseMeshChatXAndroidis injected into every loaded page. - WebSocket Origin and read gating:
/wsand/ws/telephone/audioreject a browser Origin that does not match the request authority (missing Origin still allowed for non-browser clients;X-Forwarded-Hostonly behind a trusted proxy). When password auth is enabled, every WS type exceptpingrequires a session, including former read types such askeyboard_shortcuts.get. - FileSync sync-root picker: Identity-storage
ssl(TLS key material) is a reserved top and cannot be chosen as a sync directory. - Plugin integrity at invoke:
invokeanddispatch_hookre-hash the install tree and refuse tampered backends. Python plugin loads delete__pycache__next to the entry file so bytecode excluded from the hash cannot replace source. - Peer HTML sanitizers: One XSS payload list (
javascript:,data:,<base>, SVG,onerror, CSSurl(), nested markdown) runs against MarkdownRenderer, Nomad HTML, MicronParser, and the KML sanitizer. Formatted pages still render. Scriptable nodes fail the oracle. - v-html: ESLint errors on
v-htmlunless the nearby disable comment names a sanitizer (renderMarkdown,renderMessageHtml,sanitizeNomadHtml*,convertMicronToHtml, and the other existing sites). - LAN bind: Browser UI shows a persistent banner when bound off loopback with authentication off. Dismissal is stored in localStorage. Electron and Android hide it. The process does not exit.
- Mesh payload caps: Announce
app_dataabove 2048 bytes is omitted from storage (destination and aspect still upsert). Map zipfile_too_largeand RRC 350-byte line caps keep matching oracles. - Outbound HTTP: New backend
httpx/urllib/aiohttpclients must go throughensure_outbound_http_allowedorhttp_url_guard. Remaining app fetches are translator and map tiles. - Translator (Landlock): On Linux, allow read/execute for user-local pipx CLIs (
~/.local/bin,~/.local/share/pipx) and read-write for Argos Translate data under~/.local/share/argos-translate, so argospm language lists and local Argos translation work with the filesystem sandbox enabled. - Tests: Landlock integration probes for subprocess spawn, translator Argos language listing, user-local CLI execution, and home write denial outside RW roots (tests/backend/test_landlock_integration_surfaces.py).
- Windows desktop: AppContainer sandboxing is opt-in (set
MESHCHAT_APPCONTAINER=1) instead of on by default, to avoid extra launcher processes and heavy startup until the path is stable. - Portable mode (Electron): The desktop app now honors
--data-dir/MESHCHAT_DATA_DIRand--storage-dir/--reticulum-config-dir(flag orMESHCHAT_STORAGE_DIR/MESHCHAT_RETICULUM_CONFIG_DIRenv var) on Linux and macOS, not just the Windows portable exe directory. Storage, Reticulum config, crash reports, and logs all resolve from the same root, so a removable drive or Tails persistence volume no longer touches~/.reticulum-meshchatxor~/.reticulum. Bots spawned for an identity now default their own RNS instance to the app'sreticulum_config_dirinstead of always falling back to~/.reticulum(MESHCHAT_BOT_RETICULUM_CONFIG_DIRstill overrides this for a separate bot RNS instance).resolve_log_dir()also derives a logs directory fromMESHCHAT_DATA_DIRwhenMESHCHAT_STORAGE_DIRis unset. - Mesh Server (page nodes): A page node now re-announces itself on the mesh on a timer instead of only once when you press Announce, so peers do not lose the route after the initial announce expires. Each server has its own announce interval (default 15 minutes, clamped between 1 minute and 24 hours) and an automatic-announce toggle to disable mesh announcing for that server entirely. The mesh servers page shows when a server last announced and lets you edit the interval and toggle from the server's detail view.
- Auth middleware: Public API paths are exact matches.
/api/v1/status.jsonis no longer public because it prefixed/api/v1/status. Plugin assets under/api/v1/plugins/that end in.js,.json, or.wasmrequire auth instead of matching the static-file suffix rule. - Interfaces / serial ports:
GET /api/v1/comportsno longer returns 500 when pyserial hits Landlock on USBidVendorunder/sys. It catches that TypeError and globs/dev/ttyUSB*,/dev/ttyACM*,/dev/ttyAMA*, and/dev/rfcomm*(not leftoverttyS*8250 nodes). - Announce interval 0: Mesh Server and Relay Chat hosted-hub settings keep 0 as manual announce (no timer) instead of snapping the slider back to 15 minutes.
- Propagation sync API:
/api/v1/lxmf/propagation-node/syncandstop-syncare POST (CSRF and demo-mode blocked), not GET. - Android startup: If
127.0.0.1:<port>already answers MeshChatX/api/v1/statusfor this listen port, the wrapper reuses that backend instead of treating the port as busy. A different process on the port still fails. RNS startup recovery only disables I2P on the first attempt when the error text mentions I2P, and attaches recent RNS error log lines to contained panics. - Relay Chat hosting:
/unregisterremoves the room from the registry./listomits a dangling-on rooms with no topic, and clients parse that line. Ban, invite, and op checks read existing room state and do not create a room just to answer./ban listrequires op before mutating state. - I2P interface: New I2P interfaces default
connectableto off, matching RNS. On plus Transport Mode opens a server SAM destination. If that session dies, RNS tears the I2P interface down until restart. The Add Interface form requires at least one b32 peer and states SAM must listen on 127.0.0.1:7656. - Identity switch logs: Persistent log handler flushes the in-memory buffer to the old database before swapping the handle, so lines queued during switch are not dropped.
- Local developer tooling: Vue DevTools overlay on
-
v4.8.1
Fixed
- Windows desktop: Frozen backends start again instead of failing when aiohttp imports the standard library email package.
- Desktop packaging: Only the correct LXST filterlib binary ships per platform so integrity checks match the installer. CI runs a frozen import smoke test before release.
-
v4.8.0
Added
- Browser PWA shell caching for UI assets, with a network-first fallback during short backend restarts (not used in Electron)
- Plugin system: ZIP and WASM install, signed packages (RSG), Python and Sideband backends, install consent, and Settings management
- Bundled Bug Reports plugin, plus plugin translations, UI slots, and contribution registries
- Map overlays from NomadNet and RNGit (KMZ, KML, GeoJSON) with cache and refresh
- RNS Link WebSocket API for external apps and plugins
- RNS File Sync for shared folders on the mesh, with an in-app file manager to browse, upload, download, and delete files
- Settings: Reticulum instance and share controls, tabbed Settings navigation, and desktop close or tray behavior
- Nomad favourites: per-identity section layout
- Optional pip-rns and rngit install path for RNS packages and docs
- Message export and import with contacts and read state
- Message maintenance in Settings: purge old local messages and clear duplicates
- Host battery status on About and in the header (Electron, Android, Chromium)
- System resource monitoring for CPU and memory in the UI
- RSM signing and verification for meshchatx.rsm
- Notification sound settings
- LXMFy 2.0.1 with RRC hub client support for bots
- Network visualiser WebGL and WASM renderer, with a vis-network fallback and a Settings preference
- Interfaces: internal mode, recursive path requests, announces-from-internal, discovery location command, and Backbone fast-flapping options
- Reticulum interface module management from the UI
- Reticulum 1.4.1 and LXMF 1.1.0, including propagation node options and cancel for incoming large message downloads
- Interfaces: path gravity, announces-to-internal, and discovery autoconnect gravity and mode options
- Relay Chat room keys so hosts can require a key to join a room
- Desktop privacy: Windows screen security to omit MeshChatX from screenshots, recording, and Recall
- Android privacy options to block screenshots and clear the clipboard when backgrounded
- About shows the git commit. Nightly, preview, and local builds also show a short channel label in the sidebar
- Tutorial connect: Internet plus local (recommended) adds AutoInterface and pre-selects three random community TCP bootstraps
- Remote management allow-list for identities that may query this instance with rnstatus and rnpath
- Post-install prompts for existing users after upgrades
- Coolify-oriented Docker Compose with resource limits for deployments
- LXST telephony half-duplex mode, live duplex switching, push-to-talk, and richer in-call stats
- Optional Linux seccomp-BPF syscall denylist alongside Landlock
- Bundled RNS-over-HTTP HTTPInterface with Interfaces page client and server setup, auto-install into the Reticulum interface path, and httpx support on Android
- Docker extra image variant with i2pd and yggdrasil, published beside the standard and hardened images
Changed
- Tutorial: language and theme controls no longer overlay bootstrap titles. Connection and bootstrap actions stay locked while discovery or random pick is running
- Tutorial connect and bootstrap: recommended mode stays on the bootstrap step for next and back, Finish is only on the last step, and random pick no longer sticks busy without a selection
- Tutorial privacy and security step: shorter mobile copy and toggle layout so labels and switches fit
- Auto propagation finder: requires usable paths and a scarce LXMF sync probe, remembers verified peers per identity, prefers live announces, cools down failures, and avoids re-probing a working preferred peer
- Community interface presets refreshed from directory.rns.recipes (69 online listings)
- Bundled Reticulum manual updated
- Discovery map markers use dual-halo badge icons with banded cluster badges, zoom-gated labels, and cached styles for denser maps
- Map default basemap is OpenStreetMap. Style presets list OSM first, and identity config wins over stale cached tile URLs
- UI opens sooner: HTTP binds first, Reticulum starts in the background
- Conversations load faster with slim list and thread queries
- Conversation list uses a per-peer summary so refreshes no longer scan full message history
- Conversation list omits contact image blobs and limits heavy callers such as Map and Network Visualiser
- Messages page conversation poll is slower and skips while the tab is hidden
- Fresh database init stays fast by skipping empty message-flag backfill
- Outbound message status icons and titles reflect delivery method and state more clearly
- Relay Chat: denser hub UI, announce interval, collapsed system lines, and reconnect notices
- Relay Chat: clickable Nomad and LXMF links plus basic markdown for code, bold, italic, and strikethrough
- Low-memory cleanup and SQLite tuning under memory pressure
- Auto-resend keeps attachments and cleans duplicate outbound rows more reliably
- Calls and audio work in Docker and headless setups via hostless LXST backends and the web audio bridge
- Hardened identity path handling, stamp enforcement, plugin integrity, Nomad downloads, and local file path jails
- Plugin strings live in plugin bundles, not main locale files
- Docker frontend build includes the visualiser WASM and fails if those artifacts are missing
Fixed
- LXMF outbound progress polling stops on REJECTED as well as delivered, failed, and cancelled states
- Android Codec2: preload native libcodec2 before Python codec imports
- Android RNode flasher is a native Activity with USB serial and ESP32 ROM flash. Tools opens it instead of a WebView serial polyfill
- Android RNode flasher: Bluetooth permission request returns real status, handles the result callback, and opens app settings when permanently denied
- Web Sync Messages after a backgrounded browser tab recovers a stale WebSocket, refreshes CSRF, and does not abort sync when path priming fails
- Conversations: re-opening an already-read thread no longer decrements the Messages unread badge
- Notifications: Do Not Disturb still updates the Messages unread badge. It only suppresses OS notifications and sound
- Identity switch clears Relay Chat, NomadNet browser tabs, Map tabs, and RNSh session UI so keep-alive pages cannot show the previous identity
- Map local URL checks use the private 172.16.0.0/12 range instead of treating all 172.x hosts as local
- Messages unread badge is no longer overwritten by a paginated or filtered conversation page count
- Deep-link or compose open marks the conversation read so the sidebar unread state clears
- Map my-location prefers LXMF address hash telemetry, not only identity hash
- RNode flasher integrity lookup matches flat and nested keys so required scripts load
- Relay Chat kick, ban, and failed auto-rejoin clear unread counts so room pills do not stick
- Nomad archive load owns its download id so mismatched replies cannot leave the spinner stuck
- Relay Chat invite-only rooms grant a reconnect invite on unexpected link drop so auto-rejoin still works after the one-shot invite was consumed
- Map remote overlay loads no longer undo layers from a stale generation, and tile cache view keys are identity-scoped
- Nomad micron LXMF links route to Messages instead of being ignored
- RNSh session config and identity paths stay under storage or the shared Reticulum config dir. Free-form extra args are rejected
- Failed identity switch no longer wipes keep-alive UI and clears the switching overlay correctly
- Settings, About, and Interfaces refresh identity-scoped state after an identity switch
- About snapshot and backup restore guards concurrent restores and reloads the web UI after a successful restore
- Interfaces enable and disable write the enabled flag when neither legacy key exists, and disable returns the correct success message
- Interfaces enable and disable roll back in-memory config when the Reticulum config write fails
- Disabling block-all-from-strangers restores a prior inbound stamp cost of 0 instead of forcing 8
- Discovery settings save returns an error when RNS reload fails after a successful disk write
- Interfaces stats map is replaced each poll so deleted interfaces cannot stay Connected
- Desktop AppImage: logs always append to the storage logs folder. Stdout is only used when a terminal is attached, so background launches no longer raise broken-pipe dialogs
- Android: LXMFy packaging, flock soft-lock, splash and logo clipping, Landlock skipped on Android
- Android RNode BLE and USB via Chaquopy
- Startup checks and disables unsupported interfaces
- Nomad favourites: no more Unknown Node or lost custom sections
- Relay Chat message dedupe. Network visualiser faster on large meshes
- Bots and RNSh work in frozen macOS and Windows builds
- Sensitive config is no longer mutable over WebSocket. Reticulum config repair on startup
- Paper message URI encoding for non-ASCII title and content
- LXMA contact import works with current RNS public-key loading and remembers the peer key before announce
- Android calls: overlay accept opens the phone tab so native audio attaches. Web audio is no longer permanently disabled after a bridge error
- Android Codec2: reliable native preload, builds fail without Codec2 wheels or jniLibs, and unavailable Codec2 profiles are hidden
- Unknown meshchatx links return a clear error instead of falling through to LXMF
- NomadNet Micron copy no longer inserts a newline between every character
- Unread message count is a red pill on the Messages nav icon
- Missed call count is a red pill on the Calls nav icon
- Notification bell removed from the header
- Unread badge stays circular and remains visible when the sidebar is collapsed
- Open conversations mark as read when a new message arrives without needing to reselect the thread
- Unread badges dismiss when navigating back to an already-open Messages or Relay Chat room
- Startup stage logs no longer print the same stage twice
- Ctrl+C shutdown no longer floods reentrant logging errors
- RNStatus interface mode labels match Reticulum modes again, including Internal
-
v4.7.2
Fixed
- Micron editor: Preview link clicks no longer navigate the main app window. http(s) links open in the system browser; Nomad mesh links route to the Nomad Network page; in-page #anchor links scroll within the preview. Fixes #27 and #34.
- Electron: A
will-navigateguard sends external http(s) URLs to the OS browser instead of replacing the MeshChatX window when a link is followed in-place (complements the 4.7.1window.openhandling). - Micron editor / mesh servers: Publishing preserves
.html,.md, and.txttab extensions instead of always saving as.mu. - Page node API: Publish failures return clear error messages for invalid JSON bodies, disk write errors, and mesh handler registration failures instead of unhandled 500 responses. File uploads use the same error handling. Fixes #25.
- Archives / RNCP / messages: Rich HTML link clicks use a shared navigation policy so http(s) links open externally and mesh links stay in-app (same behavior as Nomad Network and the micron editor preview).
- Android: External http(s) links open in the system browser instead of being silently blocked in the WebView.
- RNSh: Session startup prefers
python -m RNS.Utilities.rnsh.rnshover the piprnshconsole-script wrapper so sessions work when the wrapper is not executable (common withpip install --user) or when Landlock denies executing paths outside allowed read roots (e.g.~/.local/bin/rnsh). Fixes #36. - Interfaces: The Interfaces page no longer shows all links as Down when Reticulum is running normally.
/api/v1/interface-statsnow serializes all byte fields (network_id, interface hashes, IFAC signatures, and related values) instead of failing on unconverted payloads. Stats are merged on the frontend byinterface_nameas well asshort_name. Fixes false Down status when MeshChatX owns its own Reticulum instance (embedded mode). Attaching to an already-runningrnsdshared instance was unaffected. - Messages / stranger banner: The Add to Contacts button on the stranger-protection banner now uses the CSRF-aware
window.apiclient instead of rawfetch, so adding a contact from the banner works again. It also checks for an existing contact before creating a duplicate, and shows a success or error toast for the result. Fixes #38. - Android / QR scanning: Contact, message, and paper-message scanners now use a shared camera helper with higher-resolution constraints, optional continuous autofocus, and a jsQR canvas fallback when the WebView
BarcodeDetectorAPI is missing or unreliable. Camera permission is requested at Android startup. Fixes #41. - macOS: Universal DMG startup no longer fails with
ImportError: cannot import name '_add_newdoc_ufunc' from 'numpy._core._multiarray_umath'orincompatible architecture (have 'arm64', need 'x86_64')on NumPy. The x64 cx_Freeze slice now installs locked dependencies viauv sync --python-platform x86_64-apple-darwin(matching the arm64 slice) instead of unpinnedpip install -e ., and the universal merge step refuses to mix mismatched pure-Python files with native extensions under the same package. - UI / sidebars: Collapsed state for the main navigation sidebar and feature sidebars (Messages, Nomad Network, Relay Chat) now persists in localStorage when switching pages or returning later.
- Desktop / crash recovery: The Electron crash screen now diagnoses database corruption, lists local automatic backups, can restore a backup offline, relaunch with auto-repair or emergency mode, and includes copyable instructions for completely removing MeshChatX and Reticulum data folders.
- Database backups: Creating a manual backup no longer bundles the just-created backup
.zipinto its own archive when the file is written inside the identity storage directory. - Notifications: The notification bell now refreshes immediately when a conversation is marked as read from the conversation view or via bulk mark-as-read in Messages, instead of waiting on the next 5-second poll or the next dropdown open.
- Bots: Error and process log text in the Bots tool no longer picks up a stray line break from the closing
<pre>tag formatting.
Added
-
Messages / images: Conversation images show a hover download control and support right-click Save image to device in the full-screen preview, in addition to the existing message context menu action.
-
Docs: README troubleshooting section for database corruption, backup restore, and full data removal on Linux, macOS, and Windows.
-
Rich HTML links: Shared
NomadRichHtmlLinksclick handler for v-html surfaces (Nomad browser, micron editor preview, archives, RNCP, conversation messages). Routes Nomad mesh and LXMF links in-app, opens http(s) externally, scrolls in-page anchors, and blocks unsafejavascript:navigation. Behavior contract tests guard wiring across frontend, Electron, and Android. -
Location / telemetry: Settings include a Disabled location source so outbound telemetry replies are not sent until the operator opts in. New installs default to Disabled instead of browser geolocation.
-
Docs:
scripts/sync-meshchatx-docs.jscopiesdocs/*.mdinto the in-appmeshchatx-docsbundle onpredevandprebuild-frontend. Adds the Meta Quest (SideQuest) installation guide. -
Android: Vendored a patched bleak pure-python wheel (unsupported platform-specific backend requirements stripped from its metadata) so RNode interfaces can use Bluetooth LE on Android builds.
Changed
- Docs: README clarifies that Linux AppImage and
.debpackages require glibc (not Alpine/PostmarketOS/musl) and documentsAPPIMAGE_EXTRACT_AND_RUNfor FUSE-less launches. Fixes #40. - Tests: Removed obsolete no-op backend tests. Strengthened frontend security and UI coverage (BlockedPage, SettingsPage copy/banished visibility, TGS decode fuzz, RelayChat XSS fuzz, interfaceDiscoveryUtils, ArchivesPage, NomadRichHtmlLinks) with behavior assertions instead of always-true checks. Added
test_interface_stats_endpointfor/api/v1/interface-statsJSON serialization, plus an API client usage guard test that fails if a mutating request bypasses the CSRF-awarewindow.apiclient. Newtask test:quick/test:quick:be/test:quick:fecommands run a fast backend and frontend regression subset, and CI runs a thin Playwright smoke suite (tests/e2e/smoke.spec.js) against the backend and Vite dev server. - Packaging: Arch
PKGBUILDinstalls Python dependencies withuv syncinstead of a bareuv pip installstep. - CI: GitHub Actions workflows were reorganized around shared composite actions (
fetch-frontend-artifact,setup-dev-environment,setup-python-uv), the CodeQL workflow was renamed tosecurity.yml, and Dependabot now also tracks weekly GitHub Actions updates grouped by publisher. - Dependencies: Bumped Vue to 3.5.39, Vuetify to 3.12.8, electron-builder to 26.15.3, Playwright to 1.61.1, prettier to 3.9.3, and other minor frontend/backend package versions. Relaxed the dompurify version constraint to
>=3.4.11and added anapp-builder-lib>ajvoverride to keep transitive ajv versions compatible.
-
v4.7.1
Fixed
- Android: APK startup no longer crashes with
library "libpython3.so" not foundwhen loading cryptography 49 wheels. The Android wheel build rewrites PyO3 abi3 extension DT_NEEDED entries from the unversionedlibpython3.soSONAME to Chaquopy'slibpython3.11.soat pack time. - Electron:
window.openfor local backend popout routes (#/popout/), call pages, and blob: URLs now opens a child Electron window with the app session instead of failing or delegating to the OS browser.
- Android: APK startup no longer crashes with
-
v4.7.0
Fixed
- Messages: Conversation history preserves original message timestamps and sorts correctly when the API returns rows out of order.
- Messages / conversations: Outbound pending rows are deduplicated and reconciled when the conversation syncs so duplicate optimistic entries do not linger.
- Propagation: The local propagation node is started on boot when
lxmf_local_propagation_node_enabledis set in config (previously the setting could be on while the node never came up until toggled in the UI). - Interfaces / discovery: Turning discovery off also disables autoconnect for that interface. Saving discovery settings shows a restarting RNS toast with a proper spinner. The interfaces list shows all discovered peers with allowlist status, and the allowlist is applied when configuration is saved. Backbone interface cards label IFAC tunnels vs public relays and show remote host/port or listen address where applicable.
- RNode: TX power values are validated and normalized before interfaces start so invalid power settings cannot crash Reticulum on startup.
- Network visualiser: Direct and multi-hop edges render again when the path table references interfaces missing from interface-stats (synthesized interface nodes). vis-network edge smooth options use the object form required by v9; physics pauses during node drag for smoother interaction.
- Path finder: Conversation and Nomad pathfinding UI shows clearer loading states and handles archive snapshots more reliably.
- Downloads: Shared
DownloadUtilsparsesContent-Dispositionfilenames, routes API blob saves consistently, and uses the AndroidsaveDownloadbridge when present. WebView downloads use a dedicated listener with cookie forwarding and safer filename handling. - Page node (Nomad): File payloads accept bytearray bodies; existing files are registered during announces and listings.
- Chat UI: Outbound bubbles always show timestamps; the three-dot menu stays visible on orange and red outbound themes; timestamp and status icons remain readable on solid-colored outbound bubbles. Reply-quote previews wrap instead of truncating with
line-clamp. - Relative time: Sidebar and list time ago strings use finer combined units (e.g. hours and minutes) instead of coarse single-unit rounding.
- macOS: DMG builds include the microphone audio-input entitlement so calls can use the mic without extra manual signing steps.
- Voicemail: Auto-answer no longer runs for callers whose identity or destination is blocked.
- Banishment:
is_destination_blockedtreats an identity hash like a destination hash so voicemail, delivery, and other checks stay consistent with identity-level blocks. - Identity / keys: Public key loading accepts both 64-byte and 128-byte keys; identity recall uses consistent hashing.
- Database: Snapshot download names include the
.zipextension; restore and backup paths handle identity storage correctly. - Storage: Identity file path references and storage directory setup are more reliable across migration and restore flows.
- Relay chat: RelayChatPage layout and overflow handling on smaller screens.
- CI: setup-node runs before corepack on Ubuntu 24.04 so pnpm installs reliably; Micron WASM fetch and Electron coverage work with offline build flags.
- Docker:
pnpm-workspace.yamlis included in image build contexts. - Android wheels: cffi metadata pinned for consistent pycodec2 wheel builds; RECORD regeneration for installable Codec2 wheels.
Added
- Security: App-wide web UI IP allowlist (settings + middleware), CSRF tokens on mutating HTTP routes (with
/api/v1/auth/csrfbootstrap), and an LXMF message blocklist tool/API with import/export and inbound delivery filtering. - Messages: Cancel send for in-flight outbound LXMF messages (API, conversation UI, and localized strings).
- Attachments: Save image to device on Android (and shared download helpers elsewhere).
- RRC (Reticulum Relay Chat): Wire-compatible RRC client and local hub hosting (rrcd-style). Connect to remote hubs, join rooms, send messages, and host hubs locally. Member moderation for hub operators, keep-alive routing, message search, mention counts, and persisted hub history. Sidebar unread badge for relay chat; backup/restore includes RRC hubs and history files.
- RNSh: Remote shell tool with terminal session management, session resizing API, and config-directory support for saved sessions.
- LXMF reactions and replies (standard fields): Outbound reactions use
FIELD_REACTION(0x40) withREACTION_TOandREACTION_CONTENT. Replies useFIELD_REPLY_TO(0x30) andFIELD_REPLY_QUOTE(0x31). Parsing, delivery filtering, notification bell logic, and UI merge paths use the current LXMF field layout (legacy field-16 reaction payloads are no longer emitted or interpreted as reactions). - LXMF delivery: When
auto_resend_failed_messages_when_announce_receivedis enabled, failed outbound messages for a peer are resent after a successful ping or when an announce arrives and a path is already available. - LXMF stamps: UI for solving stamps with clearer tooltips and user feedback during proof-of-work.
- Messages / attachments: Improved file attachment handling; optional outbound transfer progress bar (speed, hops, elapsed time). Message import supports file upload with better error handling.
- Path finder: Shared
reticulumPathfinding.jshelper for quick request, force find, and drop path + request. Available from the conversation peer header and the Nomad browser toolbar when a page load fails; Nomad also offers load latest archive snapshot when archives exist. - Maintenance:
DELETE /api/v1/maintenance/path-tableclears the Reticulum path table; settings UI exposes Clear Path Table with localized description. - Crash recovery / diagnostics: Backend crash recovery with adaptive memory checks and improved logging. Electron persists backend crash reports, surfaces recovery hints in the loading UI, and can open the saved report from the desktop shell.
- Memory diagnostics: Optional
--memory-diagmode with/api/v1/diagnostics/memoryendpoints (snapshots, heap breakdown by type/category, GC, referrers). SQLite prepared-statement cache is capped to reduce long-lived backend memory growth. - Frontend heap monitor:
HeapMonitorlogs JS heap usage in development builds (window.heapSnapshotwhen enabled). - Network visualiser:
POST /api/v1/announces/querybulk endpoint for fetching many destination hashes in one request; faster graph hydration for large networks. - Database: SQLite WAL mode and busy timeout for better concurrency. Multipart file upload for database restore. Backups include identity storage with manifest creation; restore flow supports relaunch after import with optional storage lock bypass.
- Offline / air-gapped builds:
scripts/create-offline-bundle.shandscripts/install-offline.sh, pluspnpm run bundle:offline,build:offline, and:offlineLinux dist targets that setMESHCHATX_OFFLINE_BUILD=1. - Map: MBTiles import and tile-provider logic improvements; export panel and metadata handling for exchange exports; localized file-drop hints for map layers.
- Micron editor / Mesh Server: Publish to Mesh Server defaults new pages to
index.mu; ifindex.mualready exists and the tab still has a default name (New Tab), the editor prompts for another filename. Publish all tabs uses the same rules per tab. - Nomad browser: Opening a node focuses an existing tab for the same destination (or opens a new tab when
forceNewTabis set). Restored tabs validate destination hashes and filter external URLs. Dark shell styling and readable inputs on dark full-bleed pages; externalhttp/httpslinks open in a new browser tab. - Interfaces: Reworked interfaces page layout and discovery UX; API lists each interface’s allowlist membership. Minimum MTU validation for TCP client interfaces.
- Identities: Redesigned Identities page layout and translations.
- Telephony: Minimize control on active calls; call-related settings persist reliably across sessions (tests added).
- RNStatus:
speed_strhelper for human-readable bitrate formatting. - Codec2: Native Codec2 library integration in Android builds and CI workflows.
- Android:
AndroidStorageManagerfor internal vs external storage, migration, and setup options; refactored RNode interface handling; vendor wheel verification for aiohttp, cbor2, and cryptography. - Electron: Backend process management and automatic recovery; patches for electron-installer-common glob handling and electron-builder filesystem constants.
- Flatpak: Wayland socket, zypak Chromium sandbox module, and updated appId in packaging metadata.
- Sticker utils: Bounded gzip decompression to prevent unbounded memory allocation on malformed payloads.
- Panes / tabs: Browser-style pane and tab improvements across Nomad and tool pages.
- Tools UI:
ToolsPageHeadercomponent replaces ad-hoc headers on tool pages for consistent navigation and back links. - Docs: Meta Quest headset installation guide; LXMF address updates across documentation.
- i18n: Finnish (
fi) locale. New strings for map, interfaces, RRC, transfer progress, maintenance, MTU hints, Micron editor publish prompts, call minimize, cancel send, save-to-device, security settings, and failed-message status across supported locales.
Changed
- Dependencies: LXMF updated to 1.0.1, RNS to 1.3.5, aiohttp to 3.14.1, cryptography to 49.0.0, lxst to 0.4.7, Electron to 42.4.0, Vite to 8.0.16, dompurify to 3.4.11, UV to 0.11.15; cbor2 added for RRC; pnpm workspace overrides bump transitive form-data, socks, tar, tmp, undici, js-yaml, minimatch, and brace-expansion for known advisories.
- Android: Chaquopy recipes and
build.gradlepin aiohttp 3.14.1 and cryptography 49.0.0; CI wheel verification updated for the new versions. - Project URLs: Default homepage and documentation links moved from git.quad4.io to github.com/Quad4-Software/MeshChatX and official mirrors.
- Frontend: General styling refresh; Identities sidebar icon updated; MaterialDesignIcon uses centralized icon resolution.
- Interface discovery: Allowlist and blacklist pattern matching sanitizes patterns before matching.
- Android: Build metadata, wheel-fetch scripts (retries, local wheel paths), and PKGBUILD / Arch packaging now use a Python virtual environment.
- Electron / packaging: Legacy Electron Forge configs and scripts removed; desktop builds use electron-builder only. macOS target config uses an array form; CI can build additional Mac architectures.
- CI: Custom setup-node-pnpm action; GitHub release script excludes specific asset paths (
library.zip,*.so.yml) and improves notes generation; Rustx86_64-apple-darwintarget for macOS builds; Node.js version verification in workflows. - Tests: HTTP API route contract, interface discovery, call page, Micron editor publish, LXMF reaction field 0x40, RRC, RNSh, relay moderation, network visualiser bulk fetch and edge rendering, database restore, path finder / path-table maintenance, Nomad tab management, backbone interface labels, outbound cancel send, DownloadUtils, message blocklist, CSRF/IP allowlist, and notification user-facing filters updated for the above behavior.
Removed
- RNGit explorer: The in-app RNGit tool and its tests were removed.
- Electron Forge: Forge makers, config, and related packaging scripts (replaced by electron-builder workflows).
Loading more versions…