aethersdr / aethersdr/AetherSDR
GHE: SWITCHLOCKS basis goes stale if an Everyware server restarts with a different announcement order
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 221
- Forks
- 117
- Avg merge
- 2d 7h
- Merged PRs (30d)
- 299
Description
What
GreenHeronModel::announcedOrder() is the index basis for SWITCHLOCKS slots, and m_announced is append-only across reconnects (src/models/GreenHeronModel.cpp:294-298). If an Everyware server restarts and announces its switches in a different order, the list keeps the pre-restart basis for the rest of the app's lifetime and locksBySwitch() silently mislabels which switch holds which antenna.
No crash, nothing logged, and the UI looks entirely normal — it just tells the operator that a different switch is holding the antenna they were about to select. Raised as the strongest nit in the bot review of #5209 (review); this issue is the follow-up that PR deliberately did not fold in.
Why append-only is right today
It is not an oversight. Within a session it protects the basis against a partial replay: a reconnect that delivers two of four SWITCHADDs and then a SWITCHLOCKS must not be allowed to rewrite the ordering from an incomplete roster. Append-only is the correct answer to that. It is the wrong answer to a device that came back different.
What is measured, and what is not
Half of the question is now settled, read-only, against the reference installation (recorded in docs/green-heron-everyware.md, § How stable is announcement order?):
Ten consecutive connections — opened, read until the roster landed, closed. All ten announced
AS-84F-1, AS-84F-3, AS-84F-2, AS-84F-4, first byte 36–41 ms. Nothing transmitted, not even the keepalive.
So the order is not a per-connection race. The server replays a stored ordering rather than re-deriving one per client; a server enumerating its serial bus per connect would have varied at least once in ten tries.
Cold start is untested, not disproved. A server that enumerates once at service startup and caches would look identical on every reconnect and could still come back in a different order. The reference installation belongs to someone else and cannot be restarted, so this cannot be closed by measurement from here — which is the argument for closing it by design.
Proposed fix
Track announcement order per socket, and promote it only when it is complete:
- keep
m_announcedas the trusted basis, used for indexing exactly as today - accumulate the current connection's
SWITCHADDorder in a separate list - when that list contains every name the trusted basis holds, the roster has fully replayed — compare, and if the order differs, adopt the new one
- until then, keep indexing on the old basis
This preserves the partial-replay protection (a half-arrived roster is never promoted, because it cannot yet cover the old basis) and self-heals against a restarted server as soon as the full roster lands. Strictly better than a comment, because a note cannot help an operator who has no way to know the basis went stale.
Worth logging the swap when it happens — a silently changed lock basis is the failure mode this exists to prevent, so it should not be silent on the way out either.
Also worth surfacing
disconnectFromHost() already clears m_announced (GreenHeronModel.cpp:71-72), so Disconnect → Connect rebuilds the basis by hand; an automatic reconnect deliberately does not. That workaround is currently invisible to the operator. Now documented, but a UI affordance may be the better answer once the above lands.
Testing
Loopback covers this fully — no hardware needed. FakeDevice can announce in one order, drop, and re-announce in another:
- a reconnect replaying the same order leaves the basis untouched
- a reconnect replaying a different complete order adopts it, and
locksBySwitch()follows - a partial replay in a different order does not adopt, and the old basis still indexes — the regression this must not cause
- the discriminating case still holds afterwards:
AS-84F-2/AS-84F-3are the only switches whose slot differs between announced and sorted order (docs/green-heron-everyware.md, §SWITCHLOCKS— the trap)
Not blocking #5209
Everything above is a hardening path for a case not yet observed. #5209 is green on all four checks with the assumption documented.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/models/GreenHeronModel.cpp at announcedOrder(), m_announced, disconnectFromHost(), and locksBySwitch(), then review docs/green-heron-everyware.md for the SWITCHLOCKS basis. Use the loopback FakeDevice scenario to verify same-order reconnects, complete reordered rosters, and partial replays; done means only a complete changed roster is adopted and lock lookup remains correct.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- networking, testing-qa
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100