archy/.claude/plans/polished-napping-squid.md
2026-03-17 00:03:08 +00:00

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:

  1. Nodes broadcast identity on mesh channel: ARCHY:1:{did}:{ed25519_pubkey}:{x25519_pubkey}
  2. Receiving node derives shared secret: X25519(our_secret, their_x25519_pub)
  3. All DMs encrypted: ChaCha20-Poly1305 with random 12-byte nonce
  4. 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

  1. Create mesh/ module, migrate existing code — types.rs + mod.rs from mesh.rs
  2. protocol.rs — Binary frame encode/decode, command builders, response parsers + unit tests
  3. crypto.rs — X25519 ECDH + ChaCha20-Poly1305 encrypt/decrypt + unit tests
  4. serial.rs — MeshcoreDevice with open/init/send/recv + device auto-detection
  5. listener.rs — Background task: serial reader, peer cache, message store, reconnect
  6. mod.rs MeshService — Wraps listener + config, start/stop lifecycle
  7. Update RPC handlers — New endpoints, wire MeshService into RpcHandler
  8. Update RPC dispatch — Add routes in mod.rs ~line 622
  9. Frontend store + view — mesh.ts Pinia store, Mesh.vue with glass-card UI, router + nav
  10. 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 with mesh/ directory
  • core/archipelago/src/api/rpc/mesh.rs — update handlers
  • core/archipelago/src/api/rpc/mod.rs — add routes (~line 622)
  • core/archipelago/Cargo.toml — add serial2-tokio
  • neode-ui/src/router/index.ts — add /dashboard/mesh route
  • neode-ui/src/views/Dashboard.vue — add Mesh nav item

Reusable Existing Code

  • identity.rs lines 140-152: Ed25519 -> X25519 conversion (CompressedEdwardsY -> Montgomery)
  • identity.rs pubkey_bytes_from_did_key(): extract raw pubkey from DID string
  • node_message.rs pattern: IncomingMessage store with max 100 circular buffer
  • mesh.rs MeshConfig + load_config/save_config: migrate directly into mod.rs
  • mesh.rs detect_meshtastic_devices(): keep as fallback, add Meshcore probe-based detection

Prerequisites

  • Flash both Heltec V3 with Meshcore Companion USB role
  • Add archipelago user to dialout group: usermod -aG dialout archipelago
  • Connect Heltec V3 to USB on .228 and .198

Verification

  1. cargo clippy --all-targets passes with zero warnings
  2. Unit tests pass: protocol encode/decode, crypto encrypt/decrypt roundtrip
  3. Device detected on /dev/ttyUSB0 or /dev/ttyACM0
  4. Init handshake completes (visible in tracing logs)
  5. Identity broadcast from .228, received on .198
  6. Encrypted DM sent .228 -> .198, decrypted and visible in UI
  7. Mesh.vue shows device status, peer list, message history