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
6.7 KiB
+++ title = "Shell" description = "The firmware's own commands, typed on the device: look at its state, the SD card, the radio and the network with no PC and no cable." weight = 9 [extra] tag = "Shell" screens = ["shell.png"] +++
The firmware has a set of commands, made for working on it from a PC. The Shell runs them on the device itself: no computer, no cable, no Wi-Fi. It is the tool for the day something is wrong and you are nowhere near a desk.
It can do real damage: rm deletes, reboot restarts, debug on opens the device to the network. It is the same trust as holding the device, and nothing more.
Using it
Type a command and press Enter. help lists them all; the command reference says what each does. A few to start with:
| Command | Shows |
|---|---|
info |
The firmware's version, uptime, memory, Wi-Fi, the SD card and both firmware slots |
wifi status |
The network, the address, and where the DNS and time servers came from |
ls /notes |
A folder of the SD card, with sizes and dates |
crash |
The last crash, if there was one |
update check |
Whether a newer release exists |
lora status |
What the radio is set to and what it has heard |
- Tab completes what you are typing: the command, word by word (
lora stgiveslora status, andgnss trackwith Tab listsstart stop), then a path on the SD card.ls /noand Tab givesls /notes/; Tab again goes on inside the folder. If several names fit, it completes as far as they agree and lists them. You can type the name in any case, and after a file command you can leave out the first slash:cat noand Tab givescat /notes/. A name with a space in it isn't completed. - Fn with up and down brings back lines you typed before.
- Alt with up and down scrolls back through what was printed.
clearempties the screen, andquit(or Back) leaves.
What you see
The replies to your own commands, and nothing else. The firmware prints a lot besides: IRC connecting, a packet received, whatever a PC on the USB port or the Debug Console is asking for. None of that reaches the Shell. The firmware knows who each line is printed for, so an answer that comes a moment later from another part of it (a folder listing, tasks) is still yours.
Ctrl + b shows everything the firmware prints instead, and all shows in the corner. Press it again to go back.
Opening an App
Type an App's name with a capital letter to open it, without going back to the Launcher:
Irc Wifi Gnss Gemini Lora Storage Notes Ssh System Settings
The capital is the difference: every command is in small letters, every App starts with a capital. Tab completes them too.
The network
For the day the network doesn't do what it should. Each of the first six takes a moment and prints as it goes; one runs at a time, and cancel stops it.
| Command | Tells you |
|---|---|
ping 10.9.0.1 |
Whether a host answers, and how fast. A count and a size may follow: ping 10.9.0.1 10 1392 |
nslookup roro9stack.net |
A name's addresses, which DNS server answered and in how long. Another server may follow: nslookup roro9stack.net 9.9.9.9 |
port git.twis.la 443 |
Whether a TCP port is open, refused (the host is there, nothing listens) or silent (down, or filtered) |
traceroute 9.9.9.9 |
The routers on the way, one a line |
tls git.twis.la |
A secure connection's certificate: who it is for, who signed it, until when, and whether this device trusts it. A port may follow (443 if not) |
ntp |
A time server's clock against this device's, and how far the server is. Another server may follow |
ssh user@host |
Opens the SSH App and connects; ssh status and ssh stop for the session |
ifconfig |
The interfaces (Wi-Fi, and the VPN when it is up), their addresses, which one is the default route, and the DNS servers |
arp |
The neighbours heard on the Wi-Fi: is the gateway there at all |
netstat |
What the device listens on (updates, the Debug Console, sharing) and what is connected to it now |
A host can be a name or an address. When the network doesn't work puts them in order.
Deleting
rm works as it does on Unix, with one addition: it asks.
| You type | What happens |
|---|---|
rm /notes/a.txt |
Asks, then deletes the file |
rm -f /notes/a.txt |
Deletes it without asking |
rm /captures/old |
Refused: it is a folder, and a folder needs -r |
rm -r /captures/old |
Removed at once if it is empty. If not, asks first |
rm -rf /captures/old |
Removed with everything in it, without asking |
Several files at once
* stands for any run of characters in a name and ? for exactly one, in ls, du, rm, cp and mv:
| You type | What happens |
|---|---|
ls /notes/*.txt |
Only the notes ending in .txt |
du /captures/lora/*.pcap |
The size of each capture |
cp /gnss/2026-10-0?.gpx /backup |
Copies the tracks of the 1st to the 9th into /backup, which must exist |
rm /screenshots/2026* |
Asks once, saying how many match, then deletes them |
rm -f /screenshots/* |
Deletes them all without asking |
The pattern goes in the last part of the path (/notes/*.txt, not /*/a.txt), and capitals don't matter. A folder that matches is left alone by rm unless you add -r. At most 64 names at a time: past that nothing is done, and you are asked for a narrower pattern. cancel stops what is left.
The folders the firmware keeps its own files in can't be removed, as in the Storage App.
Screenshots
screenshot the screen, now
screenshot 5 the screen in 5 seconds: time to go to another App
The picture is saved as a PNG in /screenshots on the SD card, named by date and time, and a Toast says so once it is written (so the Toast is never in the picture). From the Shell, "now" is always a picture of the Shell: use the pause to get to the screen you want, or, simpler, press Fn + p on that screen: it takes the same picture from anywhere. The Storage App lists the files and shows them.
What it costs
Nothing while it is closed. Open, about 7 KB of memory, given back when you leave: with IRC connected and a Gemini page open, that can be the difference (see the memory limit).
The keys, as the device lists them
What Fn + h shows on this screen. This table is generated from the firmware's own lists, so it is always the current one.
{{ keys(scopes=["shell"]) }}