iOS
MeshSat iOS changelog: the iPhone gateway app, a port of MeshSat Android.
Every tagged release of the iPhone app, generated from the commit history of meshsat-ios: features and fixes, newest first. Untagged work on the main branch is listed under Unreleased. The app is a port of MeshSat Android and is being built up screen by screen and transport by transport; there is no release yet. Builds, once there are any, are on the releases page.
See v0.2.0 for the latest tagged changes. Later commits are listed under Unreleased.
Unreleased (main, last change 2026-10-28)
Features
- lxmf: the LXMF message packs and unpacks to the Bridge’s and Python’s bytes: hash, signature, stamp, the HeMB-NG mid, the audio and image fields, the announce app data and the stamper, proven against vectors the Bridge’s Go code and Python LXMF 1.2.1 agreed on (a006b035)
- lxmf: the Reticulum wire LXMF stands on, as Python RNS writes it: msgpack like umsgpack, the identity in Python’s key order with its hashes and single-packet token, the header with its context byte, the announce with its 32-byte ratchet, read against the vectors Python RNS wrote for Android (fc6e4ae3)
v0.2.0 (2026-10-28)
Features
- tak: a dispatcher’s TAK chat to this phone arrives as a message with a notification, and the answer goes back to the dispatcher (15ec3176)
- node: update the node’s firmware over Bluetooth from the published release or a file, through the OTA loader found by its service (5266efd5)
- node: switch the node’s status LED and screen on and off from Settings, through the node’s CONTROL characteristic (25f404d4)
- sms: SMS go out in app SMS wire v2, whose frame names its codec inside the ciphertext, and the app reads v2 by it and v1 by the old rule, as Android does (44721c83)
- crypto: a kit’s key bundle QR imports on iOS as on Android: signed, the kit’s key pinned on first use, a changed key only on the user’s yes (5988bece)
- iridium: the Hub’s send_mt reaches the node’s 9704 when it names it, and the background sync drains a queue the 9704 carries (059a0fb9)
- directory: the app reads the organisation’s directory on every Hub connect and every 15 minutes, pins the bundle’s key, follows no redirect, and shows it on People (f21ae124)
- directory: the Kit reads the organisation’s contacts and groups from the Hub, signed by the key pinned from the provisioning bundle, and follows the Hub’s key rotations, as Android does (80776c04)
- satellite: Setup shows the kit key’s check value to compare with the kit’s, and the satellite send and receive are the tested chain steps themselves (f171027f)
- satellite: the satellite chain can roll back to the kits’ old chain (smaz2, encrypt, base64) both ways, and its key is read as iridium:shared only (7913d9bd)
- satellite: MeshSat iOS drives the node’s 9704, sends and reads the kits’ encrypted satellite chain, and declares it to the Hub (e5a6b9ca)
- satellite: MeshSatKit reads and writes the kits’ encrypted satellite chain, drives a RockBLOCK 9704 over the node’s pipe, and declares the chain to the Hub with ordered birth stamps (0ba951ae)
- sos: an SOS carries its own id, so the Hub pages once whichever routes carry it, and its cancel goes until the Hub has it (40bbc673)
- messages: texts to another MeshSat app or kit through the Hub, as on Android, marked delivered only on the recipient app’s own answer (97d8dbb8)
- hub: the MQTT transport and Reticulum over it use the tenant’s topic root, and a root that is not meshsat or meshsat/<tenant> is refused (ead59f55)
Fixes
- node: what a node in its updater is left with belongs to the recovery session, so a later attempt never says the old firmware is still there after an earlier one erased it, and a recovery without history never claims it (5affad11)
- node: a firmware update that stops after the update command keeps the image and offers a retry, as the node may be left erased in its updater (f329abee)
- 9704: the modem’s segment request is answered the moment it is read, ahead of signal reads, MT handling and other Bluetooth writes (f133b42b)
- iridium: a 9704 send that saw no satellite comes back within a minute, an unanswered signal read keeps the last bars, and the bars say how old they are (a5e531d6)
- satellite: a plain text behind the version byte opens as a plain message on a phone that seals nothing, instead of cannot open (515486a6)
- sms: what a key opens outranks a clear SMS wire v2 reading of the same bytes, so an old sealed SMS whose nonce starts like a v2 header reads as its words (27848c39)
- directory: a reprovision to another Hub, bridge, topic root or tenant starts the directory over even under the same key, takes only the bundle’s tenant, and drops a reply from before (168701c8)
- sms: the plain-text check reads UTF-8 without the iOS 18 API, so the app builds for iOS 17 again (161afcc5)
- sms: a single Greek word or emoji SMS that was never compressed reads back as sent, not as SMAZ2 (a1767e6e)
- codec: text with bytes 0x01-0x08 survives SMAZ2 with the default table, as on the Bridge, and every other text compresses exactly as before (10711645)
- iridium: the 9704’s firmware version comes from the modem’s firmware query, as the official library reads it, not from the hardware version (abebefbb)
- sms: a compressed SMS with Greek, accented or emoji text reads back as sent, and an encrypted SMS that was never compressed is no longer read as SMAZ2 gibberish (d074e8bb)
- crypto: the key check takes only the 64 ASCII hex characters a key is made of, so a fullwidth digit is refused before encrypt refuses it (2958c4ea)
- satellite: a Reticulum packet that comes in over the 9704 is no longer stored, shown or forwarded as a text (63e80cdc)
- hub: births go out one at a time from one worker, so an older birth taken late can no longer clear a newer one that failed, and a stamp always labels the snapshot built with it (2d3350a6)
- sos: an owed SOS cancel is never dropped to make room, and a stopped SOS controller no longer runs one more round (dde0645e)
- sos: an SOS cancel is no longer lost to a failed enqueue, a new SOS, or a cancel during the first Hub publish (6e7cc18b)
- messages: a reconnect in the middle of a Hub pass no longer hides a lost status, and nothing goes out before the Hub subscriptions hold, as on Android (96a94e61)
- mqtt: a broker drop right after the connect brings the MQTT transport back instead of leaving it connected on a dead session (7a95d811)
- iridium: a satellite send that ends uncertain no longer takes back “The Hub has it” from an earlier session’s receipt (c891e136)
- delivery: a confirmation that lands while a send is out is never undone by the worker, and a confirmed delivery is never sent again (e6068935)
- delivery: each channel’s pending acks time out on its own timeout, a mesh broadcast no longer waits for an ack, a late ack is kept, and the dedup window applies and survives a restart’s hash (6e5f0ef3)
v0.1.0 (2026-10-06)
Features
- store: store_resubmit lane resolves the rejected item and resubmits the same submission (10530e44)
- satellite: Iridium BLE contract v2, node health card from STATS, pass windows to the node (241755fd)
- node: Setup > Advanced > Node log, the node’s live log over Bluetooth (087f163d)
- fastlane: store_review_state prints where the version stands with App Review (741aa8b1)
- fastlane: App Review notes and attachment as a lane; the tour wakes and unlocks the phone (d67b0c8e)
- fastlane: the App Store listing, price, countries and submission as lanes (d6d285a9)
- tests: a proof that opens the four Advanced sub-screens for the parity captures (8470a4cc)
- tests: the proving checklist runs on the phone over USB; five more rows verified (aee87edb)
- fastlane: uitest_build lane and a screen tour that runs on the phone over USB (de9500d8)
- fastlane: device_build lane, a development-signed ipa for the test phone (8346003d)
- diagnostics: link health scores, the batch queue, the telemetry table and configuration export and import on the Diagnostics screen (c127c4e0)
- config: configuration export and import in the Bridge’s JSON and YAML, with the evaluator reload (08bae26b)
- sms: the composer carries Android’s SMS wire format, compressed and encrypted per the settings and the recipient’s key (b3b9703d)
- mqtt: the device’s own MQTT transport (mqtt_0) with the Reticulum interface over it (8491faca)
- background: background windows, the Reticulum BLE peripheral, the bundle CA as broker trust root, and a build that passes App Store validation (7538bce2)
- hemb: HeMB bonding, RLNC and Reed-Solomon in the Bridge’s wire format, reassembly on the Reticulum node (2a70c372)
- msvqsc: MSVQ-SC codec with the bundled model, smaz2, canned messages and the transform pipeline (b59ea335)
- engine: dead man’s switch, burst queue and release telemetry with crash capture (daa566ee)
- tak: Cursor on Target events to and from the Hub, in the Bridge’s wire format (4d06bcba)
- aprs: APRS transport over KISS or APRS-IS with smart beaconing and acked messages (afb5c867)
- ui: the Ham radio, TAK and Reticulum setup section (e89ec959)
- ui: Home’s checklist and cards in the order set with Arrange (c9908c67)
- ui: mesh radio settings: name, radio, channels, position, Bluetooth, WiFi, restart and reset (a19fd620)
- zones: the geofence monitor and the Zones screen on the map (21cf4b1b)
- ui: mesh topology with the force layout, who hears whom, and the node list (be273536)
- audit: the signed audit log with its hash chain, and the audit screen (0ec28a8a)
- ui: encrypt or decrypt text, and certificates and keys; the Hub row proven (6935669b)
- diagnostics: the app log in the system log and in Setup, and the Hub command path logged (dbc3f432)
- crypto: read subject, expiry and fingerprint off a PEM certificate (9aa4945f)
- ui: links screen with capabilities, groups, backup links and health (6c8e58e0)
- ui: routing rules with the rule editor, deliveries and queue tabs (d4132da3)
- ui: message queue, Advanced and About screens (1b4bce56)
- ui: people tab with node details, signal, battery and contact cards (9ed8703e)
- ui: map tab with OpenStreetMap, offline MBTiles and the world overview (1c932820)
- ui: satellite passes screen with the sky chart and the Home sky card (89c38fe3)
- ui: the Setup sections for the satellite modem, the Hub with its QR scanner, messaging, safety, SMS and diagnostics (12051f0b)
- ui: the SOS card with hold to send, the banner, the result screen, the safety card with the contact picker (7e98a8b8)
- ui: the Messages tab with chats, the conversation view, the new-message picker and the SMS composer lane; signed archives with match (c945d17c)
- sos: the SOS on every route with the Hub told online, the parked SMS for the Messages composer, and the run that survives a restart (f6f32734)
- gateway: the Reticulum transport node wired in over the node link, the modem, a TCP peer and the Hub relay (864c53ab)
- reticulum: the Reticulum core, wire compatible with RNS: packets, announces, links, paths, resources, the transport node and four interfaces (5ed0c4ab)
- hub: the relay tunnel to a kit through the Hub, as a Reticulum interface that reconnects by itself (ce077b7d)
- hub: provisioning from a QR code or a deep link, with the Hub’s not-ready wait and the confirm dialogs (011e244c)
- gateway: the Hub reporter wired in, with the Hub’s commands, receipts, positions and the hub_0 delivery (9810d820)
- mqtt: the MQTT session on mqtt-nio with mTLS, SNI and the automatic reconnect (c15c43f8)
- hub: the Hub reporter, the uplink protocol types, the signed birth and the MQTT session contract (4085ed2c)
- gateway: passes from the phone’s position, the TLE cache in the store, the pass scheduler and location wired in (18ba73f2)
- satellite: the pass scheduler and the Iridium fragment reassembly, and the gateway’s Mac build fix (a09dce29)
- app: the app starts the gateway; Setup scans for and connects a node, Home and the strip show its state (db347781)
- gateway: the GatewayController wiring the node link, the modem pipe, the driver and the dispatcher, with the SOS frames (1d22889c)
- engine: dispatcher, access rules, interface manager and the engine parts on store protocols (e45b21cb)
- store: the database, DAOs, settings and Keychain store on Android’s schema, with the engine’s records (14c1c281)
- ble: Meshtastic node link and Iridium pipe over CoreBluetooth, with the protocol facade (5aab3a86)
- iridium: the 9603 AT driver over the node’s pipe, with its scripted-modem tests (aa869103)
- passes: SGP4, pass prediction and the TLE sets, pinned to Vallado’s reference (da8bd885)
Fixes
- ble: a link restored by iOS is picked up on poweredOn instead of staying on Connecting (c704cce4)
- background: no location background mode, significant-change updates in the background (5ef26ab5)
- setup: the node page asks for Bluetooth with Continue and offers Settings when denied (3b76f265)
- tak: the phone’s TAK messages reach the Hub, and it receives its own tenant’s TAK traffic (312ea9d6)
- satellite: the Node health card reads STATS again every 10 s, an idle node never notifies it (a498e2df)
- ble: say when a pipe has no STATS, a cached GATT table or older firmware (162201af)
- node: the Node log hint says the node restarts once to apply the setting (3182ace5)
- satellite: a BLE drop ends the AT command at once, no session on reconnect, the claim waits for the node (02f670c4)
- fastlane: store_submit reuses a draft submission and shows why Apple refuses an item (244e6870)
- fastlane: the App Review phone comes from the environment only (ef726d15)
- ui: disabled buttons look disabled, and a certificate’s expiry reads its date (7d1b6b48)
- hub: a receipt that outruns the satellite session still gives the second tick (fd4f84de)
- engine: a cancelled pending-query is not an error (1431873a)
- ci: the simulator test lane starts from a quit, erased simulator (4e90feef)
- tests: the Reticulum node tests wait up to 10 s on a loaded runner (8772ab78)
- ci: the simulator test lane runs the unit tests only (3cb71f7a)
- tests: the tour no longer expects a Diagnostics row under Setup (80d571fc)
- ui: the screens scroll, the map is dark, and every screen matches MeshSat Android (6c1bd4ad)
- hub: the broker is verified against the system roots again; an empty crash file is not a crash (52500d4d)
- tests: the gateway SMS test pins a wrong key as not the text, as the Kit test does (8513e756)
- sms: a wrong key is pinned as not the text, since a misread of printable ciphertext is Android’s outcome too (649c3d38)
- crypto: AES-CBC through CommonCrypto on Apple platforms, gateway helper tests, and no APRS callsign built from nothing (5c8f141a)
- aprs: the corner-pegging test parks the beacon loop instead of letting it spin (d2b4d795)
- sms: the receiver reports plain ASCII as smaz2, as Android does; the test expected otherwise (9c5c01d5)
- mqtt: received messages reach the app; the listener was freed and unregistered itself (baa608ff)
- diagnostics: the app log at notice level and in Documents, every received message logged (a4de22ec)
- iridium: a probe from a detached link no longer marks the modem silent (79e8ef42)
- hub: the whole bridge id as MQTT client id, and a refused password says to scan a new code (3f609839)
- hub: present the client certificate, use the tenant’s topics, sign births as Go marshals them (c4f6fc37)
- ui: night mode applied below the UIKit-backed containers, where a colour effect can reach (47e65f84)
- app: declare the OS-provided encryption exemption so TestFlight uploads and stops asking (e5811cde)
- ci: UTF-8 locale for fastlane on the macos runner (487a2cdd)
- ci: put Homebrew on the macos runner’s path (48b915cb)
- ci: lint entrypoint, formatting and the findings of the first pipeline (ea323d04)