Native terminal for iPhone and iPad
An iOS terminal that keeps your session when the network drops.
StoicTerm speaks mosh on the wire, with plain SSH as the fallback. Switch Wi-Fi to cellular, lock the phone, lose signal in a tunnel: your shell is still there.
Wi-Fi, 11 ms round trip. Client 10.0.0.7:51822 to devbox, udp 60001.
The session belongs to you, not to the connection.
Mosh keeps the terminal state on the server and syncs it to the phone over UDP. A new address, a sleeping radio, a dropped packet: none of them ends the session. StoicTerm adds the parts a phone needs.
- Roams between networksWi-Fi to cellular changes your IP address. The server accepts the first authenticated datagram from the new address and carries on. No reconnect, no new login.
- Survives sleep and backgroundingiOS suspends the app; the server keeps the session. On foreground StoicTerm sends a heartbeat, shows how long since the last contact, then resyncs the screen in one round trip.
- Stays current on a bad linkMosh syncs the newest screen, not a backlog of bytes. On a lossy link you skip straight to what the shell shows now, and ^C is not stuck behind old output.
The keys iOS forgot, and the bytes they send.
A row above the keyboard: esc, tab, ctrl, arrows and ^C. Press one here to see what goes down the wire. Arrows are sent as SS3 sequences in mosh mode and CSI sequences in SSH mode; ctrl modifies the next key.
Keys are made on the phone and never leave the Keychain.
Generate an Ed25519 key in the app and only the public half ever goes anywhere. Already have keys? Import OpenSSH Ed25519, RSA or ECDSA files. Password login works too, for hosts that insist.
Generate an Ed25519 key
- Private key stays in the KeychainGenerated on the device, used from there, never written to a file you could paste by mistake.
- Import what you haveOpenSSH Ed25519, RSA and ECDSA keys, with or without a passphrase.
- Password login when you mustFor the host that still asks. Nothing is stored unless you say so.
First connection to a new host
devbox is new to this device
The host's ED25519 key fingerprint is
SHA256:06OfHU8dDSdL8yPeWhcNb9IF3wC5k0orl5HwjLBMaWi
Compare it with the server before trusting. StoicTerm will remember it and warn you if it ever changes.
Tabs for every host. Split view and a real keyboard on iPad.
Each tab holds its own session, mosh or SSH. On iPad, put two hosts side by side, type on a hardware keyboard with ctrl and esc as real keys, and pinch to zoom the text.
devbox ~ $ git log --oneline -6
9f3e2a1 roaming: accept new source
c71b0d4 ocb3: rfc 7253 vectors
4d18e77 keychain: ed25519 import
b02f1c9 ssh: fallback without mosh-server
7ae30d5 render: pinch zoom keeps rows
e51c88a heartbeat: last-contact notice
devbox ~ $ git status -sb
## main...origin/main
M Sources/Transport/Roaming.swift
?? notes/spec-errata.md
devbox ~ $ swift test --filter OCB3
Test Suite 'OCB3Tests' passed
Executed 4 tests, 0 failures
devbox ~ $ build-02 ~ $ tail -f build.log 12:03:11 build 412 started 12:03:19 resolving packages 12:03:40 compiling 212 files 12:04:02 linking StoicTerm 12:04:19 tests passed (238) 12:04:51 signing with team key 12:05:07 image pushed 12:05:30 deploy staging ok 12:05:44 smoke: mosh roam ok 12:05:52 smoke: ssh fallback ok 12:06:01 build 412 done
- TabsOne session per tab, mosh or SSH, each with its own host and theme. Close a tab and only that session ends.
- Hardware keyboardModifier keys work as modifiers. The accessory bar steps aside when a keyboard is attached.
- Pinch to zoom, themes for day and nightThe native renderer rescales text without reflowing the session. Pick a theme per tab.
Mosh, re-implemented from the written spec.
StoicTerm's mosh client is a clean-room implementation: no code from the reference project, only a written specification of the protocol and its behaviour, and a test suite that checks the result against a stock mosh-server.
The cipher is AES-128-OCB3 running on Apple's CommonCrypto, checked against the test vectors in RFC 7253. The terminal itself is a native renderer, not a WebView, so scrolling, selection and pinch zoom behave like the rest of iOS.
The protocol spec
The specification covers the datagram format, the OCB3 construction, the state-sync diff rules and the roaming handshake. It ships with the beta; the datagram layout below is the summary.
| Transport | UDP, state-synchronization protocol |
|---|---|
| Cipher | AES-128-OCB3 on CommonCrypto |
| Verification | RFC 7253 test vectors, stock mosh-server interop |
| Server | Unmodified mosh-server, started over SSH |
| Fallback | Plain SSH with OpenSSH keys or a password |
| Renderer | Native, no WebView |
| Key storage | iOS Keychain |
mosh datagram, one UDP packet ┌─ nonce 8 B ─┬── AES-128-OCB3 ciphertext ──┬─ tag 16 B ─┐ nonce 8 B direction bit + 63-bit sequence, in clear timestamp 2 B sender clock in ms, for RTT estimates ts-reply 2 B echo of the peer's last timestamp payload n B state diff (protobuf TransportInstruction) tag 16 B OCB3 authentication tag
Questions before you install.
Does my server need anything?
For mosh, the host needs mosh-server installed; it ships in most package managers as mosh. StoicTerm starts it over SSH, the same way the desktop client does, then switches to UDP. For plain SSH, nothing extra.
Does it run on iPad?
Yes. Split view with two sessions, a hardware keyboard with ctrl, esc and arrows as real keys, and the same tabs as on the phone.
What about Android?
Later. iPhone and iPad come first. Nothing is scheduled yet.
What happens when I lock the phone?
iOS suspends the app and the server keeps the session. When you come back, StoicTerm sends a heartbeat, shows how long it has been since the last contact, and resyncs the screen in one round trip.
Is it compatible with the mosh I already run?
Yes. It speaks the same UDP protocol to a stock mosh-server. The client is a clean-room implementation from a written spec, and its AES-128-OCB3 is checked against the RFC 7253 test vectors.
Can I try it without a server?
Yes. The app includes an offline “Try the terminal” demo, so you can test the keyboard, the accessory bar and the renderer before connecting anywhere.
Where do my keys live?
Ed25519 keys generated in the app go into the iOS Keychain, and imported OpenSSH keys are stored the same way. Only public keys ever leave the device.
TestFlight opens soon. The App Store follows.
Leave an email and you get the invite when the beta opens. No newsletter, no drip, no sharing.