# Meshtastic Web: the SDK-first monorepo behind the browser client

> The meshtastic/web repository is not just a web UI. It is the domain-driven TypeScript SDK, a set of transport packages for HTTP, Bluetooth, serial and TCP, and the reference React client that talks to a LoRa mesh device. This review covers what each package does, how to run it locally, and where the design stops short.

**meshtastic/web** — Meshtastic Web Client/JS Monorepo

- Repository: https://github.com/meshtastic/web
- Website: https://client.meshtastic.org
- Stars: 855 · Forks: 314
- Language: TypeScript
- License: GPL-3.0
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/meshtastic-web

## What meshtastic/web actually ships, and who it is for

The repository is a pnpm workspace. Everything lives under packages/, plus one reference application under apps/web. The README describes the monorepo as consolidating "the official Meshtastic web interface, the domain-driven JavaScript SDK that drives it, and a set of runtime-specific transport packages."

That sentence is the whole product boundary. If you are building a browser tool that reads state from or sends commands to a Meshtastic device, the packages you need are already here. If you are building something that speaks to a different radio stack, nothing in this repo applies.

The audience splits three ways. Application developers want apps/web, the React client hosted at client.meshtastic.org. Library authors want packages/sdk, which is framework-agnostic, and packages/sdk-react, which wraps it for React. Integrators who only need to move bytes want one of the transport packages: transport-http, transport-web-bluetooth, transport-web-serial, transport-node, transport-node-serial, transport-deno, or the in-memory transport-mock used in tests.

One detail matters more than the package list. The protobuf boundary is described as 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. That is a deliberate constraint, and it means the generated stubs in packages/protobufs are not meant to be consumed directly by your UI code. They are the source of truth for wire-level types, regenerated with buf generate, and the SDK exists to keep them out of your components.

## The MeshClient pipeline: transport, codec, event bus, signals

The architecture section of the README lays out a single direction of travel for incoming data. A Transport produces bytes. The packet codec parses frames (the README names the 0x94 0xC3 frame marker), decodes FromRadio messages, and routes by portnum. The event bus fans decoded packets out over typed pub/sub channels. Slice infrastructure maps those into domain entities. Signals hold the state, and sdk-react exposes that state to React through useSyncExternalStore for concurrent-safe renders.

Outgoing traffic runs the other way. A use-case in a slice's application/ directory calls MeshClient.sendPacket, which hands work to the queue, which writes to the Transport. The queue also owns the packet ack and timeout pipeline, so retry behaviour is not something an application layer has to implement.

The design choice worth flagging is the error model. Expected domain errors come back as Result<T, E> through the better-result package, and the README states that exceptions are reserved for programmer errors and truly exceptional conditions. In practice that means every call site has to handle a result union rather than a try/catch. It is more verbose, and it is the kind of decision that either fits your codebase's conventions or fights them for the life of the project.

MeshClient itself is described as a thin orchestrator. It owns the transport, the queue, the event bus, and one instance of every slice client. The slice list is concrete: device, chat, nodes, channels, config, telemetry, position, traceroute, and files. Each slice follows the same layout of domain/, application/, infrastructure/, state/, a <Slice>Client.ts facade, and an index.ts barrel.

## Installing meshtastic/web and running the client locally

The README requires pnpm, and the root package.json enforces that with a preinstall script running npx only-allow pnpm. There is no npm or yarn path here. If you plan to regenerate protobufs you also need the Buf CLI.

Clone and install first:

```bash
git clone https://github.com/meshtastic/web.git
cd web
pnpm install
```

Then start the reference web client. The README gives this as a filtered workspace command rather than a root script:

```bash
pnpm --filter @meshtastic/web dev
```

Expect a local dev server for apps/web. The README does not state the port, so read the output of the command rather than assuming one.

For everything at once, including the packages:

```bash
pnpm -r build
```

Tests run under vitest from the repo root, and the root package.json also defines a Playwright end-to-end target:

```bash
pnpm -r test
pnpm test:e2e
```

The SDK ships a testing entry point, @meshtastic/sdk/testing, with createFakeTransport(). That is the intended way to wire tests without real hardware, and it is the reason transport-mock exists as a package rather than a fixture buried in a test folder.

If you are adding a transport instead of an application, the contract is small. The README prints it in full:

```ts
interface Transport {
  toDevice: WritableStream<Uint8Array>;
  fromDevice: ReadableStream<DeviceOutput>;
  disconnect(): Promise<void>;
}
```

Framing and decoding stay in the SDK. A transport supplies raw bytes and nothing else.

## Where the repository gets thin: docs, rollback and device assumptions

The README is an architecture document, not a user manual. It tells you how to install, build, test, lint and format. It does not document rollback, it does not document a versioning or upgrade policy for the published packages, and it does not tell you which Meshtastic firmware versions the SDK's packet handling is validated against. Each publishable package is said to carry build:npm, publish:npm, prepare:jsr and publish:jsr scripts, and the README points you at each package.json for details. That is the entire publishing story.

The transport split is the sharpest constraint. transport-web-bluetooth and transport-web-serial only work in browsers that implement those APIs, and transport-http requires a device exposing a network interface. There is no fallback that turns a USB-only device into something a browser can reach without Web Serial. If your target browser or device combination does not line up, the web client is the wrong tool, and no amount of SDK work fixes it.

Licensing is the other boundary. The root package.json declares GPL-3.0-only and the repository LICENSE carries the same identifier. For anyone embedding the SDK in a closed product, that is a decision to take to counsel before writing code, not after. Note also that the devDependencies reference @meshtastic/core from JSR alongside the workspace @meshtastic/sdk, which is a naming split worth understanding before you assume one package name covers everything.

Maintenance is real but should be stated precisely: the last push to the default branch was on 2026-09-09, and the most recent release listed is v2.7.2 from 2026-08-09.

## Choosing between the web client and the Node or Deno transports

The obvious alternative to running apps/web is to skip the browser entirely and use packages/transport-node or packages/transport-deno with the same SDK. The difference is not cosmetic. The browser transports depend on Web Bluetooth and Web Serial, both of which require a user gesture to grant device access and both of which are unavailable in a headless context. A Node TCP transport has no such gate: it opens a socket to a device on the network and runs unattended.

That makes the two paths suitable for different jobs. The web client is for a person sitting in front of a device, pairing once and watching nodes, messages and telemetry. A Node or Deno transport is for a long-running process that logs packets, bridges to another system, or runs in CI against transport-mock.

A second comparison is internal to the repo. You can consume @meshtastic/sdk directly, or you can consume @meshtastic/sdk-react and let MeshProvider plus the hooks handle subscription lifecycle. The React package wraps the SDK's signals in useSyncExternalStore, which is the piece that keeps concurrent renders consistent. If your application is not React, that package adds nothing, and the framework-agnostic SDK is the correct entry point.

The third option is to ignore the SDK and use @meshtastic/protobufs with your own transport. The README's strict boundary is a hint that this is possible but unsupported: you would reimplement the frame parser, the FromRadio decoding and the portnum routing yourself. For a narrow, read-only integration that may be acceptable. For anything that sends commands, the queue's ack and timeout handling is the part you would be rebuilding.

## Who should adopt meshtastic/web, and what to check first

Adopt it if you are building a browser or Node tool for Meshtastic hardware and you want the frame parsing, packet queueing and protobuf mapping handled for you. The slice layout is consistent enough that a new feature has an obvious home, and the fake transport means you can write tests before hardware arrives.

Do not adopt it if you need a stable public HTTP API to expose to third parties. Nothing in the README describes one, and the transport packages are about reaching a device, not about serving other clients. Do not adopt it either if your product cannot ship under GPL-3.0-only.

Verify three things before you commit. First, which transport your target device and runtime can actually use, because that determines whether the browser client is viable at all. Second, whether the published package names you intend to depend on match what you find on NPM and JSR, given the @meshtastic/core and @meshtastic/sdk split visible in the root package.json. Third, which firmware version your devices run, since the README does not state a validated range and the packet codec is where a mismatch would surface.

The repository was last pushed on 2026-09-09, and v2.7.2 is the newest release listed. If your integration depends on a specific slice, check that slice's directory under packages/sdk/src/features/ for the current shape rather than trusting the overview table.

## Conclusion

Adopt meshtastic/web if you want to drive a Meshtastic device from a browser or from Node, and you are willing to work inside the SDK's slice layout rather than against it. Do not adopt it if you need a documented HTTP API for a non-Meshtastic backend, or if you expect the README alone to teach you the radio protocol. Before writing code, confirm that your target device exposes a network interface for transport-http, or that your browser supports Web Bluetooth or Web Serial, because the transport you can use decides which packages are relevant to you at all.

## FAQ

### How do I use the Meshtastic web client?

Clone the repository, run pnpm install, then start the reference client with pnpm --filter @meshtastic/web dev. The README does not state which port the dev server binds to, so read the command output.

### Is there a Meshtastic web client alternative?

Within this repository the alternative to the browser client is using the same SDK with packages/transport-node or packages/transport-deno, which reach a device over TCP instead of through Web Bluetooth or Web Serial. The browser transports require a user gesture to grant device access; a Node TCP transport does not.

### Can I use Meshtastic on my PC?

Yes, in two ways. You can run the reference client locally with pnpm --filter @meshtastic/web dev, or you can consume @meshtastic/sdk from a Node process using packages/transport-node or packages/transport-node-serial.

### Is Meshtastic legal to use?

The repository material does not address radio regulation or licensing of spectrum use; it only states that the code is licensed GPL-3.0-only. Questions about operating a LoRa mesh in your jurisdiction fall outside what this repository documents.

### Can Meshtastic access the internet?

The README describes transport-http as an HTTP transport for devices exposing a network interface, and transport-node and transport-deno as TCP transports, so the SDK can reach a device over a network. It does not describe bridging mesh traffic to the internet.

## Sources

- [License: GPL-3.0](https://github.com/meshtastic/web/blob/main/LICENSE)
- [meshtastic/web on GitHub](https://github.com/meshtastic/web)
- [Project website](https://client.meshtastic.org)
- [README](https://github.com/meshtastic/web/blob/main/README.md)
- [Releases](https://github.com/meshtastic/web/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/meshtastic-web
