Skip to content

Real nodes 2: a node on Meshtastic firmware as a radio - #7

Merged
A13xB0 merged 2 commits into
epic/real-nodesfrom
real-nodes/attached
Sep 16, 2026
Merged

A13xB0 merged 2 commits into
epic/real-nodesfrom
real-nodes/attached

Conversation

@A13xB0

@A13xB0 A13xB0 commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

Part of #5 (phase 2). Merges into epic/real-nodes.

What

Any node running Meshtastic firmware can be a RepeaterTastic radio: a board over USB (Heltec, T-Beam, RAK, T-Echo… on stock firmware) or a meshtasticd on the network.

radio:
    driver: meshtastic
    device: /dev/ttyACM0        # or 192.168.1.20, or meshtastic.local:4403

The node does the radio work (routing, ACKs, encryption, its own key) and is that radio's one identity. RepeaterTastic's chat, node map, packet log, plugins and an app port sit on top.

How

  • internal/nodes.Attached is both the radio and the identity's mesh.Remote, built on internal/mtclient (Real nodes 0+1: Meshtastic client API client and meshtasticd measurements #6).
  • Remote identities in mesh:
    • A remote identity's sends go to the node; Host.RemoteReceived takes what the node delivers (messages, ACK/NAK results, node DB, traceroutes, packet log, app clients).
    • The host never answers NodeInfo, position or admin requests for the node, and ignores frames a link would inject.
    • Host.SwapRemote replaces a placeholder identity once a node that was away at start first answers; its last state is also cached in attached-node.json.
  • Settings both ways: Host.UpdateConfig writes region, preset, frequency slot, TX power, hop limit, primary channel name and role to the node in one edit transaction. After each handshake the node's settings are mirrored into the host and the config file.
  • App port: the node's identity can have one; admin messages from the app are forwarded to the node.
  • Web API:
    • POST /setup and POST /setup/probe take driver: "meshtastic". Before a password exists, the probe only reaches this machine or the LAN.
    • Name and channel edits are written to the node.
    • Adding or moving identities onto a node radio answers 409, and key export answers 404.
    • Switching a radio to or from a node requires a restart; it can't happen on the fly.
  • GUI:
    • The radio switch is now USB modem | HAT or stick | Meshtastic node.
    • The setup wizard has a node card (USB or network) with Test node. It pre-fills the node's own region and preset, words the relay step as the node's role, and restarts by itself.
    • A Meshtastic node badge marks the identity; the identity limits above are reflected in the GUI.
  • Tests: mtclient/mtclienttest, a fake node shared by the tests.

Setup wizard with a Meshtastic node

Editing a node radio

Features affected

  • Removed earlier in the epic: meshtasticd raw modem (tcp://) and its experimental switch.
  • Limited on a node radio:
    • It has one identity, and the private key stays on the node.
    • MQTT and UDP multicast links on that radio don't see its channel traffic; the node's own MQTT module can be used instead.
    • It reports no airtime, noise floor or relay counters.
    • The experimental identities-on-several-radios feature doesn't apply to it.
    • The app port shows RepeaterTastic's default module settings.

Testing

  • go test ./... passes; -race is clean for mesh, nodes, mtclient, phoneapi and web.
  • New tests:
    • Remote identities: send, ACK, failure, received text, PKI record, traceroute, link frames ignored, swap.
    • Attached backend on the fake node: mirroring, send and receive, the sim echo dropped, settings push with begin/commit, a reboot on commit, monitor mode, a node away at start (placeholder, then saved state), the late config hook.
    • Config validation of the driver, and web setup/probe with the LAN guard.
  • Live against meshtasticd 2.8.0 (sim radio) in Docker:
    • First boot through the GUI with a headless browser, then the automatic restart.
    • A chat message from the web GUI was received by the node.
    • The app handshake on the identity port works, with admin answered by the real node.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc

random-thought and others added 2 commits September 16, 2026 11:55
radio.driver meshtastic takes a node's serial port or a meshtasticd
address. internal/nodes.Attached is the radio and the one identity's
mesh.Remote: the node does its own routing, ACKs and encryption, and the
host keeps chat, the node DB, the packet log, traceroutes and app clients
from what the node delivers.

- mesh: remote identities (no private key). Sends go to the node,
  RemoteReceived takes its deliveries, periodic broadcasts and module
  replies are skipped, link frames are ignored, SwapRemote replaces a
  placeholder once the node answers.
- Settings both ways: UpdateConfig writes region, preset, slot, power,
  hop limit, primary channel and role to the node in one edit
  transaction; MirrorConfig and the config file follow the node.
- App ports: a node's identity can have one, and admin messages from the
  app are forwarded to the node.
- Web: setup and probe take the meshtastic driver (probe limited to this
  machine and the LAN before a password exists); names and channels are
  written to the node; extra identities, moves and key export are refused
  on a node radio; switching to or from a node needs a restart.
- GUI: radio switch USB modem | HAT or stick | Meshtastic node, a node
  card and probe in the setup wizard (which restarts by itself), node role
  wording, a Meshtastic node badge.
- mtclient/mtclienttest: a fake node shared by the tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants