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/.
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. |
apps/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. |
All publishable packages ship to both JSR and NPM.
@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.
You need pnpm installed. If you plan to regenerate protobufs, also install the Buf CLI.
git clone https://github.com/meshtastic/web.git
cd web
pnpm installpnpm --filter @meshtastic/web devpnpm -r buildpnpm -r testpnpm check
pnpm check:fix- 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/.
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.
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.
Each publishable package has build:npm / publish:npm / prepare:jsr /
publish:jsr scripts. See each package's package.json for details.
| Project | Repobeats |
|---|---|
| Meshtastic Web |
If you encounter any issues, please report them in our issues tracker. Your feedback helps improve the stability of future releases.
GPL-3.0-only. See LICENSE.