Repository navigation
v4.9.2
·
3627 commits
to master
since this release
Immutable
release. Only release title and notes can be modified.
Release Notes
Added
- Network visualiser gains a radial view mode that pins nodes on deterministic hop rings around the local node, plus screen-space label decluttering so dense zoomed views stay readable.
- Settings self-test gains a CBOR roundtrip check that exercises the RRC codec's encode, decode, and stream replay paths.
- CI performance suite measures cold-load FCP, LCP, SPA route transition latency, API round-trip, and post-mount heap per page with per-page budgets, plus a heap-growth spec that fails when a page leaks listeners, timers, or nodes across mount/unmount cycles.
- NomadNet: local page nodes rescan their pages and files directories on a timer and on each incoming link, so content dropped in externally is served without a restart and removed content stops answering. Matches upstream NomadNet's page_refresh_interval behavior.
- Backend fault-injection suite covers malformed RRC wire traffic, reconnect timer storms, hub store corruption, page-node rescan failures, crawler garbage inputs, link-cache teardown errors, and callback exceptions.
Security
- HTTP-set
command_plugins_pathis jailed under storage so remote callers cannot point the plugin loader at arbitrary Python files. - Reticulum
rpc_keyand interface secrets are served only to loopback or authenticated callers, and a redacted GET round-trips through PUT via a sentinel so non-privileged edits cannot erase real values. auth_session_epochis stamped into session cookies and checked on HTTP middleware and the WebSocket upgrade, so password, auth-toggle, and setup changes revoke outstanding sessions. Session secret files are restricted to 0600.- Translation-pack extraction is bounded by per-member, total, and file-count caps with streamed copies.
/apiresponses sendCache-Control: no-store, the static auth bypass is scoped to actual asset suffixes, and remote markup rendered into the main document losesid/nameattributes so archived pages cannot clobber window globals.
Fixed
- Messages: opening a raw outbound message no longer 404s once it leaves router memory (delivered or post-restart). The paper URI endpoint rebuilds the signed
lxm://URI from the stored row, preserves the original timestamp so the message hash and ingest deduping hold, verifies the repacked hash against the stored row, and skips the request entirely for inbound messages, which can never be signed by the local sender. - Messages: resend preserves title, reply quotes, reactions, app extensions, and correctly decomposed telemetry, and other sessions are told when the old failed row is deleted.
- Messages: cancel validates the hash, reaches forwarding-alias routers, and reconciles rows stuck in generating/outbound/sending to cancelled after a restart instead of returning ok while nothing changed.
- Messages: startup recovery fails
sent+directrows that could never advance after restart, and a same-hash inbound delivery can no longer demote an outbound row to incoming or repoint its peer. - Messages: hash lookups normalize case across message, attachment, spam, and announce paths.
- Messages: queued sends snapshot peer and composer state so a cleared composer or peer switch cannot send into the wrong conversation, pending placeholders dedupe correctly, inbound duplicates are dropped, and audio decode is serialized with contexts closed on unmount.
- Messages: paper URI generation and URI ingest over WebSocket were fire-and-forget. A failed send or dropped reply left buttons disabled or a spinner forever. Sends now check the result, toast on failure, and bound the wait with a timeout.
- Android: bridge-backed buttons (WiFi Aware grant, nearby permission, native RNode flasher) were silently dead because the injected bridge sat inside a Vue reactive proxy. The bridge now marks itself non-reactive and binds methods to the raw instance.
- Android: release APKs kept stripping
org.meshchatx.locallink.*because the classes are only reached viajclass()from Python, so WiFi Aware stayed disabled in minified builds. R8 keeps them and a dex check gates the build. - Android: the native RNode flasher no longer shows a duplicate title hidden behind the status bar.
- Reticulum: a second AutoInterface bind failure left a zombie singleton that killed every in-process restart. Recovery now releases interface sockets and resets singleton state before rebinding, a colliding interface is rejected at add time, and a guidance notice reports auto-disabled interfaces.
- NomadNet: image loading now parses the NomadNet 1.4 whole-line image syntax (paren links with
w/h/a/s/k/profilefields and percent widths) and sends thekeyfield the/mediaprotocol requires. Verified live againstrns.recipes. - NomadNet: page and file download events are correlated by request id so stale transfers cannot feed replacement entries, and archive navigation performs full teardown.
- NomadNet: requesting an unknown page or file from a local node opened a doomed link to the node itself. Local serving now resolves only canonical /page, /file, and /media paths, and the download handlers fail fast instead, reporting archive availability for pages.
- Relay chat: prefs and drafts are scoped per identity, so hide-join/part and ignore state apply once the identity hash resolves, hub switches reset room state, the composer clears before send to end double-send and lost-text races, and debounced config writes survive identity switches.
- Relay chat: hub auto-reconnect retried on a 60 s backoff cap forever, so a large set of unreachable hubs produced a constant stream of link and path requests. Backoff now doubles per failure up to a 15 minute ceiling with jitter, startup connects are staggered, path requests dedupe per hub, and a fresh hub announce resets the backoff so a recovering hub reconnects quickly. Manual connects reset the counter for an immediate retry.
- Service worker: subframe requests are excluded from the shell navigation strategy and only shell documents or extensionless SPA routes use the fallback slot, fixing the stale-cache stuck Loading page. Updates reload exactly once, WebTransport falls back to WebSocket on session death, and a version-mismatched backend triggers a reload after reconnect.
- Backend:
websocket_broadcastfrom foreign loops forwards onto the owning client's loop, the self-test endpoint runs off the event loop so its own probes cannot deadlock, and the Windows AppContainer probe wait is bounded. - Backend: deleting an identity tears down its live context first, telemetry per destination is capped, and in-flight propagation-node tasks are cancelled on shutdown.
- Electron: protocol links are stripped from backend argv and delivered to the renderer after load, certificate-error bypass is scoped to the local backend, DevTools shortcuts and hardware permissions are gated, and the Electron runtime version invalidates stale caches on upgrade.
- UI: context menus dismiss on click-off and re-right-click again, popup carets stay off rounded corners, and the map drawing toolbar stays pinned at the top on xl screens.
- UI: stuck-state follow-through in MiniChat (send failures now toast and fresh timestamps render), ContactsPage lxma import, NomadNet archives spinner, and the archived-pages flush result toast.
- CI: alpine APKs are built without fpm and verified by actually installing them in an apk-tools container.
- Locale files gained the missing aware/nearby permission strings in all 15 locales.
- Backend: a wedged SQLite pool could leave every API call answering 503 until a manual restart. When WAL or SHM files get unlinked under open connections, every statement fails with a disk I/O error and per-connection retries never recover. The provider now spots failures that persist on fresh connections, resets the whole connection pool at once, and if that is not enough restarts the process. Restart attempts are bounded, so a permanently broken store keeps serving retryable 503s instead of crash-looping.
- Relay chat: a malformed hub or client envelope could raise inside a packet handler and break session processing. Client and server dispatch now contain handler errors per packet, and the announce-reset and path-request rate limits use never-fired sentinels so the first retry is not skipped on hosts with low uptime.
- HTTP: the safe file response raises a real error instead of asserting when aiohttp hands back no writer.
- CI: Docker builds install pnpm 12 through npm since the pinned node image's corepack cannot shim its native binary, and the dev container does the same.
- CI: the shared Node setup keys its corepack cache by runner architecture, so an arm64 pnpm binary can no longer be restored onto the x64 macOS build runner.
Changed
- The Landlock sandbox now uses
landlockpyinstead of the custom ctypes plumbing. The enforced filesystem policy is unchanged and Android is unaffected. - Relay chat's CBOR codec moved from cbor2 to cborx, a zero-dependency RFC 8949 implementation with an optional compiled fast path. Canonical wire encoding is unchanged, decode rejects trailing bytes instead of ignoring them, and Android packages cborx through a Chaquopy recipe instead of the cbor2 wheel.
- Visualiser layout spacing widened to match node size, WASM and JS paths gained LOD color, NaN guard, edge-filtering, and dead-scene fallback parity, and
visualiser.wasmwas rebuilt. - UI lighthouse, performance, and heap suites run against the production bundle instead of the Vite dev server, and service workers are unregistered before audits so scores cannot be nulled by a controlled navigation.
- Backend: the eight per-identity periodic loops (auto-announce, propagation sync, crawler, auto-backup, telemetry, retention, flood cooldown, auto propagation selection) now share one background event loop instead of one thread each. Backups, retention sweeps, and announce table reads run in worker threads so they cannot stall the shared loop. This cuts several threads and thread-local database connections per identity.
- Backend: debug log writes to SQLite are batched into one transaction per flush, the retention sweep runs at most every ten minutes instead of every five seconds, and MESHCHAT_LOG_DB=0 disables database logging entirely. On SD-card installs this removes a steady stream of small writes.
- Backend: after identity setup finishes, startup objects are frozen out of the cyclic garbage collector's scan set, and the periodic cleanup calls malloc_trim so freed memory returns to the OS instead of sitting in allocator arenas.
- Docker images and the Raspberry Pi installer set MALLOC_ARENA_MAX=2 and OPENBLAS_NUM_THREADS=1, and the Pi guide documents both plus MESHCHAT_LOG_DB=0 for low-memory and SD-card deployments.
- Backend: the LXST telephony stack, numpy, and its audio backends no longer load at startup. The web audio bridge sits behind a lazy proxy that constructs it on first call, and telephone and voicemail resolve their LXST symbols on demand. Text-only installs keep tens of MB of memory and the OpenBLAS worker threads out of the process. If the audio stack fails to initialize or a bridge call keeps raising, the proxy disables audio for the session instead of letting errors reach the messaging paths.
Changelog
No previous tag found; changelog omitted.
SHA256 Checksums
| Asset | SHA256 |
|---|---|
| meshchatx-frontend.zip | d8dfa44c89c0b423f64aad95ba78ff92e0c1771ea6b812de4398cf749379c066 |
| ReticulumMeshChatX-v4.9.2-win-installer.exe.blockmap.cosign.bundle | 920b89d3c54d1ad50c0eda1dc4291fd9ad588cc00f0a686247a9035bcc9e41cf |
| ReticulumMeshChatX-v4.9.2-linux-x86_64.flatpak | 4b8475ae04f9d5038495f426ee5456c7c94c41e59332d3ea9ea4b9a5d2a5944e |
| ReticulumMeshChatX-v4.9.2-mac-arm64.dmg | 24a5f38c8438a252ef3983423a0e51ae5ce7681599927d5342b92b32be5c78ce |
| ReticulumMeshChatX-v4.9.2-linux-arm64.AppImage | 4ac09143bbba91fb038dcdfc2c53290c19052eb4908ae8aa110f83a7f60ff575 |
| meshchatx-desktop-v4.9.2.intoto.jsonl | 9eeeb1c295019ce5708bd05034beae44bcc8ce5b6aff5576ddcaeabc4d85fe84 |
| ReticulumMeshChatX-v4.9.2-win-installer.exe | ac6c6b35c05668692ea6965e0757fae6b275f86c38b79d1186eedb8906bf58fc |
| reticulum_meshchatx-4.9.2-py3-none-any.whl | 0072dff4dff1b7285d62f927fff9a2062c0c43182fbb06e85879f95f7a6e5f42 |
| ReticulumMeshChatX-v4.9.2-android-universal.apk | f79f36947aa4b7a3787c54bf5843d0d755844b93b83401d77054b3a47ea9ed55 |
| ReticulumMeshChatX-v4.9.2-win-installer.exe.blockmap | a476af38eeed10379e85d073ec92247167cadbffacb5f34f496e0df91e5e138e |
| latest.yml.cosign.bundle | ac0d7e795e37d311ee6e7c872a1397c655edecec9c91a789976c79f9eded5db9 |
| ReticulumMeshChatX-v4.9.2-mac-arm64.dmg.cosign.bundle | f02a9797f6b54544e30ee2b0f831b56967bbd2289b5310ae7eaab866f95dfbeb |
| ReticulumMeshChatX-v4.9.2-linux-arm64.deb | 8323a4a126abc5a95d534e4b01d5cfc6cda7e8b099ad8865e971a3e9f51f8bb0 |
| ReticulumMeshChatX-v4.9.2-mac-x64.dmg | 0e9f6ba686a96ff232b98af21825e4696d218c341127a00cfea6709020157911 |
| meshchatx-linux-v4.9.2.intoto.jsonl | bba24bda1075c36cfde264721369bd7b446fb949af6dba3383bd3ae5319eeb37 |
| ReticulumMeshChatX-v4.9.2-win-portable.exe | 859bda2ce408220db7b83fea051265852666d85641abe55da1a1e7e6138cc268 |
| ReticulumMeshChatX-v4.9.2-win-portable.exe.cosign.bundle | 25802092c6fa1d3a3a313346aa8cabd7d46d2c1177e8abf6ebf62041f8828deb |
| ReticulumMeshChatX-v4.9.2-linux-alpine-x64.apk | 2592d4858dcf3e414c8c72151fb7f9cdb41f053354d320c70c3b29c01588777a |
| ReticulumMeshChatX-v4.9.2-linux-x86_64.rpm | ef5d0fed4c5c8c1b5efe334c83f5981573a26b59ffbb68553439ec69baaf27e1 |
| latest.yml | 7ad057d5541778805bc43ef44f1bf3d856deff4bbf171e22ba56a895d2a8d2c0 |
| ReticulumMeshChatX-v4.9.2-linux-amd64.deb | b81ef3c37204c83458925370327cb688d0925970fc7269f5ac7bb677ee3da6e1 |
| meshchatx-py314-linux-x64.pyz | 1382c878fdd25395a614a85f9107c32a3567b8fd93d8f9bb0b56ea5a07e9f5d0 |
| meshchatx-py311-linux-arm64.pyz | bd1c358f8e23e6d2c7a64312dd03a7771f51c8397ff5117fadcd380f558fbffb |
| ReticulumMeshChatX-v4.9.2-linux-x86_64.AppImage | 1eef11101e4e73a8dfa7cc7c3ce859dafd16d8dd8a93cec69162896b5d9d5cf2 |
| openvex.json | d6aa027853ecbc974f8c52f64c4fa5c874724ad412bd33c77483cf167853077d |
| meshchatx-py314-linux-arm64.pyz | 52bc98a87d1b236bedc7bbd41c3b1ee9f2da7dddc4512e527726177b1b94a9d1 |
| ReticulumMeshChatX-v4.9.2-mac-x64.dmg.cosign.bundle | ede105f8f985deb78cec5f6285881ca3011e58679a899fc8775444757e176b59 |
| sbom.cyclonedx.json | 29c5b064897caeb52ba63060c45e3a3787352f5eb1bf1e45bce3aa7a819c21d5 |
| meshchatx-android-flatpak-v4.9.2.intoto.jsonl | bceab907560307a079eca9e33f5cbe5aadbd1a05f1866b3ba74f077a2ceea733 |
| meshchatx-py311-linux-x64.pyz | 71675b8e5a5e44db1c652505f7bb35cb9412b8a3663792882458d3fb4002cf62 |
| ReticulumMeshChatX-v4.9.2-win-installer.exe.cosign.bundle | 1e5ce8b72979b8a06220902aa47f7b84b44ce2c176224cd0e29aee726a75245e |
Verification
- Cosign bundles (
.cosign.bundle) are attached for keyless sigstore verification. - SLSA provenance (
.intoto.jsonl) is available for supply-chain attestation. - Or verify manually using the SHA256 table above.