Agent reference: Meshtastic channel URLs & Store & Forward

August 21, 2026 · View on GitHub

Deep subsystem reference for AI assistants. Open this when a task touches Meshtastic config apply, admin, channel URLs, Store & Forward, remote admin, last-heard, or static GPS. Hard rules live in AGENTS.md.

  • Config apply (Radio / Modules / Security): Firmware setConfig / setModuleConfig replace full protobuf structs. UI must merge cached device slices with form edits via meshtasticConfigApply.ts (mergeMeshtasticConfigApplyValue, buildMeshtasticModuleApplyValue); slices live in deviceStore.meshtasticConfigSlices and moduleConfigs (PacketRouter + runtime lifecycle wire effects). Module-specific validation: meshtasticMqttModuleApply.ts, meshtasticSerialModuleApply.ts. Apply failures surface clientNotification text within 8s (meshtasticClientNotification.tsformatMeshtasticModuleApplyError); inline status via ConfigApplyNotice.tsx. Forms re-sync after reboot via useSyncFormFromConfig.
  • Administration tab: AdminPanel.tsx — device commands and Danger Zone (reboot, shutdown, factory reset, NodeDB reset, OTA/DFU); shared ConfirmModal.tsx with Radio/Modules destructive flows. Local-only OTA/DFU disabled when Configure node targets a remote node.
  • Remote admin module snapshot: meshtasticRemoteAdminModuleFetches.ts — canonical list/count of ModuleConfig reads during remote snapshot (REMOTE_ADMIN_MODULE_CONFIG_FETCHES).
  • Channel URLs: src/shared/meshtasticUrlEncoder.ts (parse/generate), src/shared/meshtasticChannelApply.ts (replace vs add-only apply); Radio panel UI; Meshtastic-only.
  • S&F chat history: src/renderer/lib/meshtasticBacklogUtils.tsCLIENT_HISTORY on primary router heartbeat after RF configure (auto: 50-msg cap, 120 min window cap, 15 min per-server cooldown, 5 min offline gate; storeForwardAutoFetchHistory opt-out; storeForwardHistoryProfile: 'conservative' | 'aggressive' in defaultAppSettings.ts tunes offline gate / cooldown / cap aggressiveness; manual catch-up in Chat). Protobuf decode for replayed text, via_store_forward on messages; do not await SDK queue for history (async replay).
  • MQTT broker clientId: src/main/mqtt-broker-client-id.ts — stable per-install IDs in app_settings (meshtasticMqttClientId, meshcoreMqttClientId); MeshCore LetsMesh v1_ username unchanged as clientId.
  • PKC remote admin (firmware 2.5+): meshtasticRemoteAdmin.ts — PKI-wrapped AdminMessage via MeshDevice.sendRaw() (pkiEncrypted: true, channel omitted on wire); session passkeys (~300s); tab-scoped snapshot routes in meshtasticRemoteAdminSnapshot.ts (Channels-first LoRa load). Per-node keys: meshtasticRemoteAdminKeyStorage.ts (meshtasticRemoteAdminKey:<nodeNum> in app_settings; base64 / base64: / 64-char hex paste). Dest public key: NodeDB hex first, stored admin-key base64 fallback. useMeshtasticRuntime: configureTargetNodeNum, remoteConfigSnapshot, runRemoteAdminOp (errors → UI + toast); serialize admin reads with S&F (remoteAdminReadsActiveCount in meshtasticBacklogUtils.ts). Requires connected local radio (MQTT-only cannot admin). UI: ConfigureNodeSelector.tsx; NodeDetailModal admin key + Configure node remotely; SecurityPanel Copy public key. Persist last target in meshtasticConfigureTargetNodeNum. Gate with hasRemoteAdmin. Legacy admin channel (PSK + "admin") out of scope.
  • Meshtastic last heard: meshtasticLastHeard.ts — bump last_heard on live RF packets (not only text); computeNodeInfoLastHeardMs merges radio NodeDB timestamps with client-side values (max wins). Configure replay guard must apply in nodeStore (upsertNode, updatePosition, meshtasticLastHeardPatch) via meshtasticConfigurePhase.ts, not only in meshtasticNodeSideEffects — PacketRouter updates the store before side effects run. During device.configure(), the Meshtastic SDK replays NodeDB as node_info frames and may emit synthetic onUserPacket / onPositionPacket with rxTime = now; guards skip those bumps. UserPacket path uses mergeMeshtasticUserPacketLastHeard (ms); NodeDB path uses computeNodeInfoLastHeardMs (sec). Protocol decode must use meshtasticPacketRxTimeMs@meshtastic/core PacketMetadata.rxTime is already a Date (ms); never rxTime * 1000 (Date×1000 → ~1e15; regression lock in MeshtasticProtocol.test.ts Date-shaped rxTime + source-policy meshtastic-protocol-rxtime-via-helper). SQLite stores last_heard as unix seconds; meshtasticDbCacheHydration.ts normalizes to ms on hydrate (normalizeLastHeardMs); stale/online checks use effectiveLastHeardMs. BLE configure uses a stall watchdog (MESHTASTIC_BLE_CONFIGURE_TIMEOUT_MS, reset on each replayed node_info / position / telemetry via touchMeshtasticConfigureProgress) so large NodeDBs are not cut off mid-stream. Node list JSON export emits last_heard as unix seconds with last_heard_unit: 'unix_sec'.
  • Static GPS: src/renderer/lib/gpsSource.ts — App tab static coordinates sync to self-node, map, and radio setPosition.