aethersdr / aethersdr/AetherSDR

GHE: SWITCHLOCKS basis goes stale if an Everyware server restarts with a different announcement order

Open
#5,250 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug external devices maintainer-review
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_announced as the trusted basis, used for indexing exactly as today
  • accumulate the current connection's SWITCHADD order 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-3 are 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.