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
+105
View File
@@ -0,0 +1,105 @@
+++
title = "SSH"
description = "A terminal on another machine: log in to a server with a password or with the device's own key, and run a shell, vim or top on the Cardputer's screen."
weight = 13
[extra]
tag = "SSH"
screens = ["ssh.png", "ssh-trust.png", "ssh-terminal.png", "ssh-key.png", "ssh-changed.png"]
+++
The SSH App opens a **terminal on another machine**: a server, a Raspberry Pi, a router. What you type goes there, and what its programs print is drawn here: a shell, `less`, `top`, `nano`, `vim`.
It is a client and nothing more: one session at a time, to a shell. No file transfer, no port forwarding, no jump hosts.
## Connecting
The App opens on the hosts it has connected to before, the last one first, then **New connection** and **This device's key**.
1. Choose **New connection** (or press <kbd>n</kbd>) and type **`user@host`**, or `user@host:port` when the port isn't 22. The host is a name or an address.
2. **The first time, it shows the server's fingerprint** and asks whether that is the right server. Compare it with what the server's owner gives you (`ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub` on the server prints it), and choose **Trust it**. It is remembered, and not asked again.
3. **Type the password** when it asks. It is used for this login and kept nowhere: not in the device, not on the card.
A host you logged in to is kept in the list: <kbd>Enter</kbd> connects to it again, <kbd>d</kbd> forgets it, and its fingerprint with it. Up to eight are kept.
From the [Shell](/guide/shell/), `ssh user@host` does the same and opens this App.
## If the server has changed
A server that shows **a different key than the one remembered** gets a warning instead of a question: **THE SERVER'S KEY CHANGED**. Either the server was reinstalled, or something between you and it is answering in its place. The safe answer, **Cancel**, is the one selected. Choose **Replace** only when you know why the key changed.
## Without a password: this device's key
**This device's key** makes a key pair for the device (Ed25519). The private half stays inside the device and is never shown. The public half is one line of text:
- it is shown on that page,
- written to the card as **`/ssh/id_ed25519.pub`**,
- and printed by `ssh status` in the Shell.
Add that line to `~/.ssh/authorized_keys` on a server, and the device logs in there with no password. See [Log in to a server without a password](/howto/ssh-key/).
Making a **new key** replaces the old one: servers that knew the old one ask for a password again.
The key has no passphrase, so **whoever holds the device can log in wherever its key is accepted**. Keys made elsewhere can't be imported.
## In the terminal
Every key goes to the other machine, with these differences:
| Key | Does |
|---|---|
| <kbd>`</kbd> | **Esc** (the key is printed "esc") |
| <kbd>Alt</kbd> + <kbd>`</kbd> | types a backtick |
| <kbd>Fn</kbd> + <kbd>;</kbd> <kbd>.</kbd> <kbd>,</kbd> <kbd>/</kbd> | the arrows |
| <kbd>Fn</kbd> + <kbd>Shift</kbd> + <kbd>;</kbd> <kbd>.</kbd> | Page Up, Page Down |
| <kbd>Ctrl</kbd> + a letter | Ctrl+C, Ctrl+D, Ctrl+Z and the rest |
| <kbd>Alt</kbd> + a key | that key with Alt (Esc first) |
| <kbd>Alt</kbd> + <kbd>;</kbd> <kbd>.</kbd> | scroll back and forward through the last 100 lines |
| <kbd>Ctrl</kbd> + <kbd>+</kbd> <kbd>-</kbd> | larger and smaller text |
| <kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>q</kbd> | disconnect |
| <kbd>Fn</kbd> + <kbd>`</kbd> | back to the Launcher, **leaving the session running** |
**Leaving doesn't disconnect.** Go to the Launcher or to another App and the session goes on; `SSH` shows in the [Status Bar](/guide/basics/#the-status-bar) for as long as it does. Open the SSH App again and you are back in it. `exit` on the other machine, or <kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>q</kbd> here, ends it.
### The size of the text
Five sizes, changed with <kbd>Ctrl</kbd> + <kbd>+</kbd> and <kbd>Ctrl</kbd> + <kbd>-</kbd>; the other machine is told the new size at once, and the choice is kept.
| Text | Columns × rows |
|---|---|
| tiny | 60 × 20 |
| small (to start with) | 48 × 15 |
| medium | 40 × 12 |
| normal | 40 × 9 |
| large | 26 × 8 |
A terminal is usually 80 columns wide, and this screen isn't: long lines wrap, and programs that want 80 columns look cramped. `top`, `vim` and `less` adapt.
### What it shows, and what it doesn't
- Sixteen colours, bold as a brighter colour, reverse video.
- Western European letters (é, ñ, ü). Other characters show as `?`, and box-drawing lines as `+`, `-` and `|`.
- No mouse.
## Memory
A session takes about **50 KB** of the device's 100 KB while it is open, and gives it back when it ends. That is too much to share with a secure connection:
- **It doesn't start with less than 75 KB free.** With IRC connected, or a Gemini page open, it says "Not enough memory: close IRC or a Gemini page".
- **While a session is open, IRC doesn't connect:** it says so in its buffer and tries again later.
See [When a connection says "not enough memory"](/howto/not-enough-memory/).
## From the Shell
```
ssh user@host connect, in the SSH App
ssh user@host:2222 on another port
ssh status the session, and this device's public key
ssh stop end the session
```
## Keys
What <kbd>Fn</kbd> + <kbd>h</kbd> shows on these screens. These tables are generated from the firmware's own lists, so they are always the current ones.
{{ keys(scopes=["ssh", "ssh-terminal", "ssh-key"]) }}