The node gates the shell on its security.admin_key list - the same list the
remote-admin passkey exchange goes through - so opening a shell on a node
that has not authorized this phone got no reply at all.
Remote Shell now has its own section rather than sitting under
Administration, and runs that check itself: the row stays enabled, tapping
it ensures the session and then opens the terminal. Previously the session
was only ever established as a side effect of visiting the remote-admin
screen, so the user had to know to go there first.
Also: openRemoteAdmin and openRemoteShell share one ensureSessionThenNavigate
helper, and the ViewModel now gives up on OPEN after 20 s instead of sitting
in OPENING forever, naming the admin-key requirement when it does.
Verified against meshtasticd: from a node with no session, one tap
establishes the session and lands on a live prompt; with the node stopped,
the watchdog reports 'Session failed' with a Reconnect action.
Three things stood between the April branch and a working session:
- The CRT theatrics are gone - scanlines, phosphor presets, flicker, and
the AGSL curvature shader, which rendered the block cursor as a skewed
amber quad in the corner. The terminal is now monospace text on the app
theme.
- Turn-taking ('talking stick', RemoteShell.flags) landed on the firmware
side after this branch was written. DMShellModule marks every session
turn-managed and withholds output until the peer grants a turn, so a
client that never grants sees an open session and no prompt, and the
device re-sends RTS every 250 ms until its TX queue fills. We now grant
on every frame we originate and answer any request immediately.
- The invisible input sink passed a constant "" as its value, so Compose
re-delivered characters we had already consumed: 'whoami' reached the
PTY as 'wwhwhowhoa'. It now tracks what it consumed and forwards the
delta, mapping a shrinking field to backspaces.
Verified against meshtasticd: session opens, 'whoami' echoes exactly and
returns 'root'.
- license header year: the tree standardised on 2026, not 2025-2026
- spotless: ktlint's blank-line-between-when-conditions is new since
- MeshDataHandlerImpl: drop shouldBroadcast, removed with the AIDL
service rework (#5586); nothing broadcasts on this path any more
- RemoteShellViewModel: DataPacket.nodeNumToDefaultId and
DataPacket.PKC_CHANNEL_INDEX both moved to NodeAddress
Conflicts, all re-integration of the April branch into the current tree:
- MeshDataHandlerImpl/Test: keep main's constructor (ServiceScope, session
context, geofence/beacon collaborators) and re-add remoteShellHandler.
- Capabilities.kt: re-add supportsRemoteShell, still gated to UNRELEASED.
- strings.xml: main re-sorted the file; re-insert remote_shell and
phosphor_colour in their sorted positions.
- NodesNavigation/AdministrationSection: union both sides.