First three settings pages migrated off the legacy changeRegistry to the
SDK ConfigEditor. Section structure, field order, and visible strings
are taken from the Meshtastic-Android source of truth so the web and
mobile UIs converge.
Position (radio config):
- Four cards in Android order: Position Packet → Device GPS → Position
Flags → Advanced Device GPS.
- Card 1 fields: positionBroadcastSecs, positionBroadcastSmartEnabled,
broadcastSmartMinimumIntervalSecs (renamed "Smart Interval"),
broadcastSmartMinimumDistance ("Smart Distance").
- Card 2 fields: fixedPosition, lat/lng/altitude (when fixed), gpsMode
("GPS Mode (Physical Hardware)"), gpsUpdateInterval ("GPS Polling
Interval"). gpsMode + gpsUpdateInterval disable when fixedPosition is
on; lat/lng/altitude disable when it's off.
- Card 3 fields: positionFlags multiselect (description from Android).
- Card 4 fields: rxGpio ("GPS Receive GPIO"), txGpio ("GPS Transmit
GPIO"), gpsEnGpio ("GPS EN GPIO").
- onSubmit now calls editor.setRadioSection("position", payload). The
setFixedPosition admin message path is unchanged.
LoRa (radio config):
- Two cards: Options + Advanced (was three: Mesh / Waveform / Radio).
- Options: region, usePreset, modemPreset (when usePreset),
bandwidth/spreadFactor/codingRate (when !usePreset).
- Advanced: ignoreMqtt, configOkToMqtt, txEnabled, overrideDutyCycle,
hopLimit (now "Number of Hops", select 0..7), channelNum,
sx126xRxBoostedGain ("RX Boosted Gain"), overrideFrequency
("Frequency Override"), txPower.
- Drop frequencyOffset (Android does not surface it).
- onSubmit calls editor.setRadioSection("lora", payload).
MQTT (module config):
- Two cards: MQTT Config + Map reporting (new card).
- Card 1: enabled ("MQTT enabled"), address, username, password,
encryptionEnabled, jsonEnabled, tlsEnabled, root ("Root topic"),
proxyToClientEnabled.
- Card 2: mapReportingEnabled, mapReportSettings.shouldReportLocation
(consent gate, "I agree."), positionPrecision (12-15 buckets),
publishIntervalSecs ("Map reporting interval (seconds)").
- positionPrecision + publishIntervalSecs disable until both
mapReportingEnabled and shouldReportLocation are true.
- onSubmit calls editor.setModuleSection("mqtt", payload).
- Validation schema gains shouldReportLocation; address/username/
password limits widened to 63 to match proto, root capped at 31.
Settings/index.tsx handleSave:
- After the legacy changeRegistry flow, runs editor.commit() when the
editor is dirty. Two beginEdit/commitEdit windows are fine — the
device handles them sequentially. Once the rest of the settings pages
migrate, the legacy flow goes away and only editor.commit() remains.
- Save button gating includes editor.isDirty so users can save
ConfigEditor-only changes (e.g. just a LoRa region tweak).
i18n:
- New section labels (config.json position.{positionPacket,deviceGps,
advancedDeviceGps}; lora.{optionsCard,advancedCard}; moduleConfig.json
mqtt.{mqttConfigCard,mapReportingCard}).
- Field labels reworded to match Android strings.xml.
- mqtt.mapReportSettings.shouldReportLocation gains label, description,
consentHeader, consentText (consent text quoted verbatim from
Android).
Meshtastic Web Monorepo
Overview
This monorepo consolidates the official Meshtastic web interface, the domain-driven JavaScript SDK that drives it, and a set of runtime-specific transport packages. Everything you need to read state from or send commands to a Meshtastic device lives here.
Note
You can find the main Meshtastic documentation at https://meshtastic.org/docs/introduction/.
Packages
All projects live under packages/.
| Package | Purpose |
|---|---|
packages/sdk |
Framework-agnostic TypeScript SDK. Domain-driven feature slices (device, chat, nodes, channels, config, telemetry, position, traceroute, files) built around a MeshClient orchestrator with @preact/signals-core reactive state. |
packages/sdk-react |
React hooks + MeshProvider on top of @meshtastic/sdk. Wraps signals in useSyncExternalStore for concurrent-safe renders. |
packages/web |
Reference React web client. Hosted at client.meshtastic.org. |
packages/ui |
Shared Radix + Tailwind component library. |
packages/protobufs |
Generated TypeScript stubs from meshtastic/protobufs, produced via buf generate. Source of truth for every wire-level type. |
packages/transport-http |
HTTP transport for devices exposing a network interface. |
packages/transport-web-bluetooth |
Web Bluetooth transport for BLE-capable devices (browsers). |
packages/transport-web-serial |
Web Serial transport for USB-serial devices (browsers). |
packages/transport-node |
TCP transport for Node.js. |
packages/transport-node-serial |
Serial transport for Node.js. |
packages/transport-deno |
TCP transport for Deno. |
packages/transport-mock |
In-memory transport for tests. |
packages/core |
Legacy SDK — superseded by @meshtastic/sdk and slated for removal after the slice-by-slice migration completes. |
All publishable packages ship to both JSR and NPM.
Architecture
@meshtastic/sdk organises its source by feature slice. Each slice follows the
same DDD layout:
features/<slice>/
domain/ # entities & value objects — pure TypeScript types
application/ # use-cases (SendTextUseCase, FavoriteNodeUseCase, …)
infrastructure/ # protobuf ↔ domain mappers, admin-message adapters
state/ # signals-backed reactive stores
<Slice>Client.ts # public facade exposing readable signals + command methods
index.ts
The shared kernel under packages/sdk/src/core/ owns:
client/—MeshClient, the thin orchestrator that owns the transport, queue, event bus, and one instance of every slice client.transport/— theTransportinterface everytransport-*package implements.event-bus/— typed pub/sub channels populated by the packet codec.packet-codec/— frame parser (0x94 0xC3),FromRadiodecoder, portnum router.queue/,xmodem/— packet ack/timeout pipeline and file-transfer protocol.signals/— signal and keyed-collection helpers consumed by every slice.logging/—tslogfactory used consistently by every class.identifiers/,errors/— small primitives shared across slices.
The protobuf boundary is strict: wire messages enter through the packet codec,
get mapped into domain entities inside features/*/infrastructure/*Mapper.ts,
and signals only ever expose the domain shape.
Transport ─▶ Packet codec ─▶ EventBus ─▶ Slice infrastructure
│
▼
Signals (state) ─▶ sdk-react hooks ─▶ UI
▲
│
Slice application (use-cases) ─▶ MeshClient.sendPacket ─▶ Queue ─▶ Transport
Expected domain errors are returned as Result<T, E> via better-result; exceptions are reserved for programmer errors and truly exceptional conditions.
Getting Started
Prerequisites
You need pnpm installed. If you plan to regenerate protobufs, also install the Buf CLI.
Setup
git clone https://github.com/meshtastic/web.git
cd web
pnpm install
Run the web client
pnpm --filter @meshtastic/web dev
Build everything
pnpm -r build
Run tests
pnpm -r test
Lint + format
pnpm check
pnpm check:fix
Developing
Adding a new feature slice to @meshtastic/sdk
- Create
packages/sdk/src/features/<slice>/withdomain/,application/,infrastructure/,state/subdirectories plus anindex.tsbarrel. - Implement a signals-backed store in
state/. - Subscribe to the relevant
EventBuschannel(s) inside a<Slice>Client.tsclass and write mapped domain entities to the store. - Wire the new client into
MeshClientand re-export types frompackages/sdk/mod.ts. - Add vitest coverage: domain invariants, use-cases against
createFakeTransport(), and round-trip mapper fixtures. - If the slice has React callers, add a matching hook under
packages/sdk-react/src/hooks/.
Adding a new transport
Implement the Transport interface exported from @meshtastic/sdk/transport:
interface Transport {
toDevice: WritableStream<Uint8Array>;
fromDevice: ReadableStream<DeviceOutput>;
disconnect(): Promise<void>;
}
The SDK does the framing and decoding — transports only supply raw bytes.
Testing
Vitest is wired at the repo root and picks up packages/* projects. The SDK
ships @meshtastic/sdk/testing with createFakeTransport() for wiring tests
without real hardware.
Publishing
Each publishable package has build:npm / publish:npm / prepare:jsr /
publish:jsr scripts. See each package's package.json for details.
Repository activity
| Project | Repobeats |
|---|---|
| Meshtastic Web |
Feedback
If you encounter any issues, please report them in our issues tracker. Your feedback helps improve the stability of future releases.
Star history
Contributors
License
GPL-3.0-only. See LICENSE.