Skip to content

feat(app): add Share my node name, off by default, sending a zero-hop advert each cycle a companion is the target - #577

Open
khagele wants to merge 1 commit into
efiten:masterfrom
khagele:feat/576-share-name
Open

feat(app): add Share my node name, off by default, sending a zero-hop advert each cycle a companion is the target#577
khagele wants to merge 1 commit into
efiten:masterfrom
khagele:feat/576-share-name

Conversation

@khagele

@khagele khagele commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Closes #576

The problem

The hunter's companion never announces itself. The app sends Discover broadcasts and trace-pings, which carry no identity, and nothing else. MeshCore firmware answers a request only from a sender it can look up in its contact list (src/Mesh.cpp:150-156), and a node adds us to that list when it hears our advert (src/helpers/BaseChatMesh.cpp:151-176). So a companion that has not heard us cannot answer the telemetry request #553 wants to send. The firmware check behind that is in the #553 thread.

An advert is also the first time the app would transmit who the hunter is. That is the hunter's decision, not the app's.

My changes in this PR

  • Share my node name, a checkbox in a new Identity section of Settings, off by default. Under it: "Shares your companion's name and key with nodes in direct range, once per auto-discover cycle while a companion is your target. Off: the app never transmits who you are."
  • With it on, every auto-discover cycle that has a companion among the selected targets sends one CMD_SEND_SELF_ADVERT (7) with the route byte at 0, which the firmware sends zero-hop (examples/companion_radio/MyMesh.cpp:1258). Never a flood. Per cycle rather than once at switch-on: the target can be out of range at that moment, and it has to hear us before it can answer.
  • A repeater target keeps its trace-ping and gets no advert. selectedCompanionIds (feed.js) is the selection minus the repeater reading, so the two never disagree about one node; a room server or sensor lands on the companion side, which is right, since neither forwards a trace.
  • The FAB pulses and the tx cue sounds, as for every frame that goes out. The setting lights the settings dot like every other non-default, since it is the one setting that transmits.
  • What the advert carries is the companion's own: its public key and name, and a position only if the owner set the location policy in the MeshCore app (advert_loc_policy, default none). The app adds nothing.
  • app/src/announce.js holds the frame and the cycle rule; settings.js the loader. Changelog entry in both copies.

Airtime

One advert per cycle is one more frame next to the Discover, only while a companion is selected and the setting is on. Zero-hop, so nothing is repeated by the mesh. Against the budget in docs/2026-08-17-speed-adaptive-autoping.md it is the cost of one extra target.

Verification

Suite Result
app vitest 906 passed, build and eslint clean
web vitest 509 passed (the changelog copies)

Eight mutation checks over the two rounds, each red on exactly one test: the route byte set to flood, a truthy instead of exact on-check, the loader accepting 'true', the dot ignoring the setting, the companion reading including repeaters, the reading listing a node per reception, the cycle rule ignoring the target count, and the route byte again on the final code.

Browser at 360x780 with a stub transport: off on a fresh load; on stores '1', marks the row and lights the dot; nothing selected sends no advert; a companion selected sends one [7,0] per cycle; the same node read as a repeater gets a trace-ping and no advert; the setting off, or the selection cleared, sends none; the setting survives a reload.

Not verified here: the advert on air. The frame is the one the MeshCore app sends for its own "Send advert (zero hop)".

Left out on purpose

🤖 Generated with Claude Code

… advert each cycle a companion is the target

The hunter's companion never announced itself (efiten#576): the app sends
Discover broadcasts and trace-pings, which carry no identity, and
nothing else. MeshCore firmware answers a request only from a sender
it can look up in its contact list, and a node adds us to that list
when it hears our advert. So a companion that has not heard us cannot
answer the telemetry request efiten#553 wants to send.

An advert is also the first time the app would transmit who the hunter
is, so it is a setting, off by default: Share my node name, in a new
Identity section of Settings, with the plain words under it. With it
on, every auto-discover cycle that has a companion among the selected
targets sends one CMD_SEND_SELF_ADVERT (7) with the route byte at 0,
which the firmware sends zero-hop, never a flood. Per cycle rather
than once at switch-on, because the target can be out of range at
that moment and it has to hear us before it can answer. A repeater
target keeps its trace-ping and gets no advert: selectedCompanionIds
is the selection minus the repeater reading, so the two never disagree
about one node. The FAB pulses and the tx cue sounds, as for every
frame that goes out. The setting lights the settings dot like every
other non-default. What the advert carries is the companion's own: its
public key and name, and a position only if the owner set the location
policy in the MeshCore app.

Verified: app 906 unit tests, build and eslint clean; web 509 (the
changelog copies). Eight mutation checks over the two rounds, each red
on one test. Browser at 360 with a stub transport: nothing selected
sends no advert; a companion selected sends one [7,0] per cycle; the
same node read as a repeater gets a trace-ping and no advert; the
setting off, or the selection cleared, sends none; the setting
survives a reload.

Closes efiten#576

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.

app: the hunter's companion never announces itself, so a node that has not heard it cannot answer

1 participant