SSH client: a terminal on another machine (#2)
CI / build (pull_request) Successful in 2m22s
Site / build (pull_request) Successful in 10s

The SSH App opens one session to a shell, over libssh (LibSSH-ESP32 5.10.0)
on mbedTLS. A server is trusted the first time on its fingerprint, and a
changed key is a warning with Cancel selected. The password is typed each
time and kept nowhere; or the device makes itself an Ed25519 key, whose
public half is shown, written to /ssh/id_ed25519.pub and printed by
`ssh status`.

lib/term is the terminal: what a shell, less, top, nano and vim send, with
sixteen colours, scroll regions, the alternate screen and 100 lines of
scrollback. Five text sizes with Ctrl and + or -, from 60x20 to 26x8, told
to the far end. The session goes on when the App is left; SSH shows in the
Status Bar. `ssh user@host` in the Shell opens the App.

Also:
- Keys that aren't characters carry Shift, Ctrl and Alt. The terminal needs
  it, and it makes Ctrl+Fn+up/down in a note and Shift+Tab in Gemini work
  from the real keyboard.
- IRC doesn't try to connect under 60 KB free: started with a session open,
  its TLS handshake took the heap down to 236 bytes.
- libssh's own curve25519 is left out of the build (scripts/libssh_filter.py):
  libsodium's has the same names.

Costs 292 KB of flash and about 50 KB of heap while a session is open; not
started under 75 KB free.

Docs: guide page, how-to, FAQ, home page, Status Bar, SD card folders, the
memory how-to, README, glossary, N1 notes with Q254 to Q266 and the checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
2026-10-08 04:38:36 +02:00
co-authored by Claude Opus 5.5
parent 7223147f26
commit 6b71aace4f
47 changed files with 2801 additions and 20 deletions
+5
View File
@@ -48,6 +48,11 @@ const Definition kDefinitions[] = {
{"help_told", Kind::Bool, 0, nullptr, 0, 1},
{"vpn_config", Kind::String, 0, "", 0, 900}, // empty, or a .conf that parses
{"vpn_auto", Kind::Bool, 0, nullptr, 0, 1},
{"ssh_hosts", Kind::String, 0, "", 0, 800},
{"ssh_known", Kind::String, 0, "", 0, 2400},
{"ssh_key", Kind::String, 0, "", 0, 800},
{"ssh_public", Kind::String, 0, "", 0, 200},
{"ssh_font", Kind::Int, 1, nullptr, 0, 4},
};
static_assert(sizeof(kDefinitions) / sizeof(kDefinitions[0]) == static_cast<size_t>(Setting::Count),
"every Setting needs a definition");
+5
View File
@@ -36,6 +36,11 @@ enum class Setting : uint8_t {
HelpTold, // bool: this device has been told about the help key once (issue #69, Q201)
VpnConfig, // string: the WireGuard tunnel as a .conf (wg_config.h), private key included: never shown (issue #8)
VpnAuto, // bool: the tunnel starts whenever Wi-Fi is connected (Q247: off unless switched on)
SshHosts, // string: the SSH App's saved hosts, one user@host[:port] a line (issue #2, ssh_hosts.h)
SshKnown, // string: the fingerprint each server showed first, one "host:port fingerprint" a line
SshKey, // string: this device's own SSH private key, as OpenSSH writes one: never shown
SshPublic, // string: its public half, the line to put in a server's authorized_keys
SshFont, // int: the terminal's font, 0 (smallest) to 4
Count
};