Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
4fe5d57
feat(sdk): scaffold @meshtastic/sdk + @meshtastic/sdk-react
danditomaso Apr 24, 2026
763df03
refactor(web): import @meshtastic/sdk instead of @meshtastic/core
danditomaso Apr 24, 2026
e2874ba
feat(sdk): add MeshRegistry + multi-client React providers
danditomaso Apr 24, 2026
b94c4e6
refactor(sdk-react): rename useDevice → useMeshDevice
danditomaso Apr 24, 2026
9b80472
feat(web): mount MeshRegistryProvider at app root
danditomaso Apr 24, 2026
981c042
feat(web,sdk): register per-connection MeshClient in registry
danditomaso Apr 24, 2026
30e8824
feat(sdk): add MessageRepository port + InMemoryMessageRepository
danditomaso Apr 24, 2026
5265a35
feat(sdk-storage-sqlocal): SQLite WASM persistence adapters
danditomaso Apr 25, 2026
736e905
feat(web): persist chat history via @meshtastic/sdk-storage-sqlocal
danditomaso Apr 25, 2026
61aad66
test: testing strategy + 19 new SDK / storage / hook tests
danditomaso Apr 25, 2026
51eebc3
feat(web): MessagesPage reads chat history from SDK / SQLite
danditomaso Apr 25, 2026
5541a68
feat(web): MessagesPage outbound send through SDK ChatClient
danditomaso Apr 25, 2026
4b146c4
feat(sdk,web): drafts persisted in SDK chat slice (SQLite-backed)
danditomaso Apr 25, 2026
a427f9d
chore(web): drop saveMessage path from subscriptions, delete dead DTO
danditomaso Apr 25, 2026
2e5f6e6
feat(sdk,web): ChatClient.clearConversation + clearAll; wire DeleteMe…
danditomaso Apr 25, 2026
9c6f8b9
feat(sdk,sdk-storage-sqlocal,web): NodesRepository port + SQLite pers…
danditomaso Apr 26, 2026
17ca853
feat(sdk,web): SDK Node carries hops/mqtt/key-verified; legacy adapte…
danditomaso Apr 26, 2026
c7db122
refactor(web): NodesPage reads node list from SDK signals
danditomaso Apr 26, 2026
dd8bda1
refactor(web): migrate 9 nodeDB consumers onto SDK adapter
danditomaso Apr 26, 2026
b9f035b
refactor(web): rename node legacy adapters to *AsProto
danditomaso Apr 26, 2026
cb789a8
refactor(web): useFavoriteNode + useIgnoreNode drive SDK NodesClient
danditomaso Apr 26, 2026
84e69bf
refactor(web): ResetNodeDb + RefreshKeys dialogs read SDK; safer adapter
danditomaso Apr 26, 2026
54a8103
feat(sdk,sdk-react,web): node PKI / routing-error tracking on the SDK
danditomaso Apr 26, 2026
9037ac7
chore(web): remove dead PKI / nodeError paths from legacy nodeDB
danditomaso Apr 26, 2026
005d000
feat(sdk,web): SDK NodesClient owns user/position/lastHeard/snr +
danditomaso Apr 26, 2026
15df8ec
chore(web): drop leftover useNodeDB from Position settings page
danditomaso Apr 26, 2026
f29b2ba
chore(web): delete legacy nodeDBStore — SDK NodesClient is the only s…
danditomaso Apr 26, 2026
0342eca
chore(web): retire Zustand messageStore — keep only Message types/enums
danditomaso Apr 26, 2026
7aa5207
chore(web): rename useChatLegacy → useChatAsLegacyMessages
danditomaso Apr 26, 2026
0f8525c
feat(sdk-react,web): migrate read-only channel consumers to SDK Chann…
danditomaso Apr 26, 2026
5a5755f
feat(sdk,sdk-react): scaffold ConfigEditor for the radio/module/chann…
danditomaso Apr 26, 2026
3429e48
feat(web): pilot Position / LoRa / MQTT pages on ConfigEditor; Androi…
danditomaso Apr 26, 2026
ba3948e
feat(web): migrate Bluetooth/Device/Display/Power/Network/Security pa…
danditomaso Apr 26, 2026
e4d0b11
feat(web): migrate 11 module-config pages to ConfigEditor; Android-al…
danditomaso Apr 26, 2026
3388d4c
feat(sdk,web): migrate User + Channel + ImportDialog off changeRegist…
danditomaso Apr 26, 2026
0677191
feat(sdk,web): delete changeRegistry; ConfigEditor owns all settings …
danditomaso Apr 26, 2026
273bcf6
feat(sdk,sdk-storage-sqlocal,web): TelemetryRepository port + Sqlocal…
danditomaso Apr 26, 2026
eef69db
feat(sdk,sdk-react,web): unread counts on the SDK ChatClient
danditomaso Apr 26, 2026
d4cd649
refactor(web): split useConnections into focused modules
danditomaso Apr 26, 2026
daf7c22
chore: move packages/web → apps/web
danditomaso Apr 26, 2026
f899c97
feat: PR #12 Phase C — delete packages/core, retarget transports to @…
danditomaso Apr 26, 2026
a7b1923
fix(web/connections): probe = "online" not "configured"; flip status …
danditomaso Apr 26, 2026
9219b8a
feat(web): Position "Use browser location" button + Telemetry firmwar…
danditomaso Apr 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/crowdin-download.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
uses: crowdin/github-action@v2
with:
base_url: "https://meshtastic.crowdin.com/api/v2"
config: "./packages/web/crowdin.yml"
config: "./apps/web/crowdin.yml"
upload_sources: false
upload_translations: false
download_translations: true
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/crowdin-upload-sources.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
# Monitor all .json files within the /i18n/locales/en/ directory.
# This ensures the workflow triggers if any the English namespace files are modified on the main branch.
paths:
- "/packages/web/public/i18n/locales/en/*.json"
- "/apps/web/public/i18n/locales/en/*.json"
branches: [main]
workflow_dispatch: # Allow manual triggering

Expand All @@ -21,7 +21,7 @@ jobs:
uses: crowdin/github-action@v2
with:
base_url: "https://meshtastic.crowdin.com/api/v2"
config: "./packages/web/crowdin.yml"
config: "./apps/web/crowdin.yml"
upload_sources: true
upload_translations: false
download_translations: false
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/crowdin-upload-translations.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
uses: crowdin/github-action@v2
with:
base_url: "https://meshtastic.crowdin.com/api/v2"
config: "./packages/web/crowdin.yml"
config: "./apps/web/crowdin.yml"
upload_sources: false
upload_translations: true
download_translations: false
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,18 @@ jobs:
run: pnpm run test

- name: Build web package
working-directory: packages/web
working-directory: apps/web
run: pnpm run build

- name: Package output
working-directory: packages/web
working-directory: apps/web
run: pnpm run package

- name: Upload compressed build (artifact)
uses: actions/upload-artifact@v4
with:
name: web-build-nightly
path: packages/web/dist/build.tar
path: apps/web/dist/build.tar
if-no-files-found: error

- name: Compute tags and labels
Expand All @@ -66,8 +66,8 @@ jobs:

# Try to use latest release tag if it exists; fallback to package version; else date
LATEST_TAG="$(gh release view --json tagName --jq .tagName 2>/dev/null || true)"
if [ -z "$LATEST_TAG" ] && [ -f packages/web/package.json ]; then
LATEST_TAG="v$(jq -r .version packages/web/package.json)"
if [ -z "$LATEST_TAG" ] && [ -f apps/web/package.json ]; then
LATEST_TAG="v$(jq -r .version apps/web/package.json)"
fi

if [ -n "${LATEST_TAG:-}" ] && [ "$LATEST_TAG" != "vnull" ]; then
Expand All @@ -91,7 +91,7 @@ jobs:
uses: redhat-actions/buildah-build@v2
with:
containerfiles: |
./packages/web/infra/Containerfile
./apps/web/infra/Containerfile
image: ${{ env.REGISTRY_IMAGE }}
tags: |
${{ steps.meta.outputs.moving_tag }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,4 @@ jobs:
run: pnpm run test

- name: Build web package
run: pnpm --filter "./packages/web" run build
run: pnpm --filter "./apps/web" run build
2 changes: 1 addition & 1 deletion .github/workflows/release-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
run: |
set -euo pipefail
if [ "${{ github.event.inputs.packages }}" = "all" ] || [ -z "${{ github.event.inputs.packages }}" ]; then
mapfile -t TARGETS < <(ls -d packages/* | grep -v 'packages/web')
mapfile -t TARGETS < <(ls -d packages/*)
else
IFS=',' read -ra TARGETS <<< "${{ github.event.inputs.packages }}"
TARGETS=("${TARGETS[@]/#/packages/}")
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/release-web.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,25 +51,25 @@ jobs:
run: pnpm install --frozen-lockfile

- name: Build web package
working-directory: packages/web
working-directory: apps/web
run: pnpm run build

- name: Create release archive
working-directory: packages/web
working-directory: apps/web
run: pnpm run package

- name: Upload archive (artifact)
uses: actions/upload-artifact@v4
with:
name: web-build
path: packages/web/dist/build.tar
path: apps/web/dist/build.tar
if-no-files-found: error

- name: Attach archive to GitHub Release
if: ${{ github.event_name == 'release' || inputs.attach_to_release == true }}
uses: softprops/action-gh-release@v2
with:
files: packages/web/dist/build.tar
files: apps/web/dist/build.tar
tag_name: ${{ github.event_name == 'release' && github.event.release.tag_name || inputs.tag_name }}
fail_on_unmatched_files: true
env:
Expand Down Expand Up @@ -134,7 +134,7 @@ jobs:
uses: docker/build-push-action@v6
with:
context: .
file: ./packages/web/infra/Containerfile
file: ./apps/web/infra/Containerfile
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.tag_name != '') }}
tags: ${{ steps.meta.outputs.tags }}
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ packages/protobufs/packages/ts/dist
*.pem
*.crt
*.key

# Claude Code session locks
.claude/
2 changes: 1 addition & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"recommendations": ["bradlc.vscode-tailwindcss", "biomejs.biome"]
"recommendations": ["bradlc.vscode-tailwindcss", "oxc.oxc-vscode"]
}
190 changes: 134 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,89 +9,163 @@
## Overview

This monorepo consolidates the official [Meshtastic](https://meshtastic.org) web
interface and its supporting JavaScript libraries. It aims to provide a unified
development experience for interacting with Meshtastic devices.
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/.

### Projects within this Monorepo (`packages/`)
## 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. |
| `apps/web` | Reference React web client. Hosted at [client.meshtastic.org](https://client.meshtastic.org). |
| `packages/ui` | Shared Radix + Tailwind component library. |
| `packages/protobufs` | Generated TypeScript stubs from [`meshtastic/protobufs`](https://github.com/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](https://jsr.io/@meshtastic) and [NPM](https://www.npmjs.com/org/meshtastic).

## 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/`** — the `Transport` interface every `transport-*` package implements.
- **`event-bus/`** — typed pub/sub channels populated by the packet codec.
- **`packet-codec/`** — frame parser (0x94 0xC3), `FromRadio` decoder, portnum router.
- **`queue/`**, **`xmodem/`** — packet ack/timeout pipeline and file-transfer protocol.
- **`signals/`** — signal and keyed-collection helpers consumed by every slice.
- **`logging/`** — `tslog` factory 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`](https://www.npmjs.com/package/better-result); exceptions are reserved for programmer errors and truly exceptional conditions.

All projects are located within the `packages/` directory:
## Getting Started

- **`packages/web` (Meshtastic Web Client):** The official web interface,
designed to be hosted or served directly from a Meshtastic node.
- **[Hosted version](https://client.meshtastic.org)**
- **`packages/core`:** Core functionality for Meshtastic JS.
- **`packages/transport-node`:** TCP Transport for the NodeJS runtime.
- **`packages/transport-node-serial`:** NodeJS Serial Transport for the NodeJS runtime.
- **`packages/transport-deno`:** TCP Transport for the Deno runtime.
- **`packages/transport-http`:** HTTP Transport.
- **`packages/transport-web-bluetooth`:** Web Bluetooth Transport.
- **`packages/transport-web-serial`:** Web Serial Transport.
- **`packages/protobufs`:** Git submodule containing Meshtastic’s shared protobuf definitions, used to generate and publish the JSR protobuf package.
### Prerequisites

All `Meshtastic JS` packages (core and transports) are published both to
[JSR](https://jsr.io/@meshtastic). [NPM](https://www.npmjs.com/org/meshtastic)
You need [pnpm](https://pnpm.io/) installed. If you plan to regenerate
protobufs, also install the [Buf CLI](https://buf.build/docs/cli/installation/).

---
### Setup

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

| Project | Repobeats |
| :------------- | :-------------------------------------------------------------------------------------------------------------------- |
| Meshtastic Web | ![Alt](https://repobeats.axiom.co/api/embed/e5b062db986cb005d83e81724c00cb2b9cce8e4c.svg "Repobeats analytics image") |
### Run the web client

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

## Tech Stack
### Build everything

This monorepo leverages the following technologies:
```bash
pnpm -r build
```

- **Runtime:** pnpm / Deno
- **Web Client:** React.js
- **Styling:** Tailwind CSS
- **Bundling:** Vite
- **Language:** TypeScript
- **Testing:** Vitest, React Testing Library
### Run tests

---
```bash
pnpm -r test
```

## Getting Started
### Lint + format

### Prerequisites
```bash
pnpm check
pnpm check:fix
```

## Developing

### Adding a new feature slice to `@meshtastic/sdk`

1. Create `packages/sdk/src/features/<slice>/` with `domain/`, `application/`, `infrastructure/`, `state/` subdirectories plus an `index.ts` barrel.
2. Implement a signals-backed store in `state/`.
3. Subscribe to the relevant `EventBus` channel(s) inside a `<Slice>Client.ts` class and write mapped domain entities to the store.
4. Wire the new client into `MeshClient` and re-export types from `packages/sdk/mod.ts`.
5. Add vitest coverage: domain invariants, use-cases against `createFakeTransport()`, and round-trip mapper fixtures.
6. If the slice has React callers, add a matching hook under `packages/sdk-react/src/hooks/`.

You'll need to have [pnpm](https://pnpm.io/) installed to work with this monorepo.
Follow the installation instructions on their home page.
### Adding a new transport

### Development Setup
Implement the `Transport` interface exported from `@meshtastic/sdk/transport`:

1. **Clone the repository:**
```bash
git clone https://github.com/meshtastic/meshtastic-web.git
cd meshtastic-web
```
2. **Install dependencies for all packages:**
```bash
pnpm install
```
This command installs all necessary dependencies for all packages within the
monorepo.
3. **Install the Buf CLI**
Required for building `packages/protobufs`
https://buf.build/docs/cli/installation/
```ts
interface Transport {
toDevice: WritableStream<Uint8Array>;
fromDevice: ReadableStream<DeviceOutput>;
disconnect(): Promise<void>;
}
```

### Running Projects
The SDK does the framing and decoding — transports only supply raw bytes.

#### Meshtastic Web Client
### Testing

Please refer to the [Meshtastic Web README](packages/web/README.md) for setup and usage.
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.

### Feedback
## 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 | ![Alt](https://repobeats.axiom.co/api/embed/e5b062db986cb005d83e81724c00cb2b9cce8e4c.svg "Repobeats analytics image") |

## Feedback

If you encounter any issues, please report them in our
[issues tracker](https://github.com/meshtastic/web/issues). Your feedback helps
improve the stability of future releases
improve the stability of future releases.

## Star history

Expand All @@ -108,3 +182,7 @@ improve the stability of future releases
<a href="https://github.com/meshtastic/web/graphs/contributors">
<img src="https://contrib.rocks/image?repo=meshtastic/web" width="100%"/>
</a>

## License

GPL-3.0-only. See [LICENSE](LICENSE).
Loading