4.9 KiB
4.9 KiB
Meshcore Mesh Networking — Phase 1 Implementation Plan
Context
Adding mesh networking to Archipelago using Heltec V3 devices running Meshcore firmware (Companion USB). Two nodes (.228 and .198) will exchange encrypted identity and text messages over LoRa radio with no internet required. The existing mesh.rs wraps the Meshtastic CLI — this replaces it with a native Meshcore serial protocol driver.
Architecture
Convert mesh.rs into mesh/ module directory:
core/archipelago/src/mesh/
├── mod.rs — Public API, MeshService, config (migrated from mesh.rs)
├── types.rs — MeshPeer, MeshMessage, MeshStatus, DeviceType
├── protocol.rs — Meshcore binary frame protocol (encode/decode/commands)
├── serial.rs — MeshcoreDevice: async serial driver (serial2-tokio)
├── crypto.rs — X25519 ECDH + ChaCha20-Poly1305 per-message encryption
└── listener.rs — Background tokio task: serial reader + message dispatcher
Frontend:
neode-ui/src/stores/mesh.ts — Pinia store
neode-ui/src/views/Mesh.vue — Mesh status, peers, messaging UI
Dependency
Add to core/archipelago/Cargo.toml:
serial2-tokio = "0.1"
All crypto deps already present (chacha20poly1305, ed25519-dalek, curve25519-dalek).
Meshcore Protocol Summary
- Frame format:
>+ 2-byte LE length + data (outbound),<+ 2-byte LE length + data (inbound) - Baud: 115200, 8N1
- Max message: 160 bytes
- Init sequence: CMD_DEVICE_QUERY (0x16) -> CMD_APP_START (0x01) -> CMD_SET_DEVICE_TIME (0x06)
- Key commands: SEND_TXT_MSG (0x02), SEND_CHANNEL_TXT_MSG (0x03), GET_CONTACTS (0x04), SYNC_NEXT_MESSAGE (0x0A), SEND_SELF_ADVERT (0x07)
- Push events (async, >=0x80): NEW_CONTACT (0x8A), ACK (0x82), MESSAGES_WAITING (0x83)
Encryption Design
Reuses existing identity.rs X25519 key agreement:
- Nodes broadcast identity on mesh channel:
ARCHY:1:{did}:{ed25519_pubkey}:{x25519_pubkey} - Receiving node derives shared secret: X25519(our_secret, their_x25519_pub)
- All DMs encrypted: ChaCha20-Poly1305 with random 12-byte nonce
- Wire format: [nonce 12B] + [ciphertext] + [tag 16B] — fits in 160B limit for ~130B plaintext
RPC Endpoints
| Method | Action |
|---|---|
mesh.status |
Device + mesh status (updated) |
mesh.peers |
NEW — list discovered mesh peers |
mesh.messages |
NEW — get message history (last 100) |
mesh.send |
NEW — send encrypted message to peer |
mesh.broadcast |
Broadcast identity (updated for Meshcore) |
mesh.configure |
Update config (updated) |
Implementation Steps
- Create mesh/ module, migrate existing code — types.rs + mod.rs from mesh.rs
- protocol.rs — Binary frame encode/decode, command builders, response parsers + unit tests
- crypto.rs — X25519 ECDH + ChaCha20-Poly1305 encrypt/decrypt + unit tests
- serial.rs — MeshcoreDevice with open/init/send/recv + device auto-detection
- listener.rs — Background task: serial reader, peer cache, message store, reconnect
- mod.rs MeshService — Wraps listener + config, start/stop lifecycle
- Update RPC handlers — New endpoints, wire MeshService into RpcHandler
- Update RPC dispatch — Add routes in mod.rs ~line 622
- Frontend store + view — mesh.ts Pinia store, Mesh.vue with glass-card UI, router + nav
- Deploy + test — Deploy to .228 and .198, plug in Heltec V3s, test end-to-end
Key Files to Modify
core/archipelago/src/mesh.rs-> delete, replace withmesh/directorycore/archipelago/src/api/rpc/mesh.rs— update handlerscore/archipelago/src/api/rpc/mod.rs— add routes (~line 622)core/archipelago/Cargo.toml— add serial2-tokioneode-ui/src/router/index.ts— add /dashboard/mesh routeneode-ui/src/views/Dashboard.vue— add Mesh nav item
Reusable Existing Code
identity.rslines 140-152: Ed25519 -> X25519 conversion (CompressedEdwardsY -> Montgomery)identity.rspubkey_bytes_from_did_key(): extract raw pubkey from DID stringnode_message.rspattern: IncomingMessage store with max 100 circular buffermesh.rsMeshConfig+load_config/save_config: migrate directly into mod.rsmesh.rsdetect_meshtastic_devices(): keep as fallback, add Meshcore probe-based detection
Prerequisites
- Flash both Heltec V3 with Meshcore Companion USB role
- Add
archipelagouser todialoutgroup:usermod -aG dialout archipelago - Connect Heltec V3 to USB on .228 and .198
Verification
cargo clippy --all-targetspasses with zero warnings- Unit tests pass: protocol encode/decode, crypto encrypt/decrypt roundtrip
- Device detected on /dev/ttyUSB0 or /dev/ttyACM0
- Init handshake completes (visible in tracing logs)
- Identity broadcast from .228, received on .198
- Encrypted DM sent .228 -> .198, decrypted and visible in UI
- Mesh.vue shows device status, peer list, message history