A native SSH + Mosh client for iOS, iPadOS and Android.
First-class rendering for claude --output-format stream-json, tmux-aware, Tailscale-friendly.
- Why NexaTerm?
- Features
- Demo
- Architecture
- Quick Start
- iOS Setup
- Android Setup
- Backend / Self-Hosting
- Comparison
- Tech Stack
- Roadmap
- FAQ
- Contributing
- License
Note
Blink Shell is excellent. Termius has cloud sync. Prompt is polished. But none of them are AI-CLI-first.
NexaTerm is built for the era where your real dev environment is a VPS running tmux + claude + gemini + aider — and your phone is the pane of glass. It is:
- Native SwiftUI + Jetpack Compose — not a WebView wrapper, not Electron, not a React-Native shim. Liquid Glass on iOS 26, Material 3 Expressive on Android 16.
- Stream-JSON aware — it parses the structured output from modern agent CLIs and renders tool calls, todos, diffs, thinking traces and usage as native cards over the terminal surface.
- Mosh-resilient — your session survives subway tunnels, airplane mode and IP changes. tmux on the VPS, Mosh on the wire, smooth on the screen.
- Tailscale-friendly — point it at a tailnet host, authenticate with your tailnet identity, never expose SSH to the public internet.
- Self-hostable backend — an optional FastAPI bridge gives you per-user isolated Docker containers with
tmux,claude,gemini,codexpre-installed. BYO API keys, zero vendor lock-in.
|
|
|
|
Tip
Short looping demos (GIF / MP4) live in docs/assets/. Swap these placeholders once you record the first sessions.
What you are looking at
- SSH into a tailnet VPS, attach to a persistent
tmuxsession. - Kick off
claude --output-format stream-jsoninside tmux. - Watch the terminal pane scroll raw text while an overlay of native cards renders every tool call, todo update and diff.
- Detach, lock the phone, come back 20 minutes later — Mosh reconnects, tmux state is right where you left it.
┌──────────────────────────────┐ ┌──────────────────────────────┐
│ iOS / iPadOS / Android app │ SSH / │ Your VPS (or your Mac) │
│ │ Mosh │ │
│ SwiftUI / Compose shell │◄────────►│ sshd │
│ SwiftTerm / termux-view │ │ └─ tmux │
│ StreamJSON overlay │ │ └─ claude, gemini, │
│ Tailscale, Secure Enclave │ │ codex, aider, zsh │
└───────────────┬──────────────┘ └──────────────────────────────┘
│
│ (optional) WebSocket
▼
┌──────────────────────────────┐
│ Self-hosted bridge │
│ FastAPI + Docker per user │
│ xterm.js-compatible stream │
└──────────────────────────────┘
See docs/concept-2026-04-18.md for the full design doc, docs/backend-architecture.md for the bridge, and docs/decisions.md for ADRs.
git clone https://github.com/schenkei-code/nexa-term.git
cd nexa-term
# Shared TypeScript types (parser schemas, host-config shape)
npm install
npm run shared:buildPick the target you care about and jump to its setup section below.
Requirements:
- macOS 15+ with Xcode 16.3+
- Tuist 4.x (the project uses Tuist manifests)
- An Apple Developer account if you want to run on-device
cd apps/ios
tuist install # resolve SwiftTerm + Citadel via SwiftPM
tuist generate # writes MobileTerminal.xcodeproj
open MobileTerminal.xcworkspaceHit ⌘R. Simulator defaults to iPhone 17 Pro on iOS 26.
Manual Xcode path (no Tuist)
Create a fresh "App" project in Xcode, drop the contents of apps/ios/MobileTerminal/ in, and add these SwiftPM dependencies:
https://github.com/migueldeicaza/SwiftTermhttps://github.com/orlandos-nl/Citadel
Run tests: tuist test or ⌘U in Xcode.
Requirements:
- JDK 21
- Android Studio Narwhal+ with Android SDK 36 (Android 16)
- An Android device or emulator on API 34+
cd apps/android
./gradlew :app:assembleDebug
./gradlew :app:installDebug # deploys to the first connected deviceSee apps/android/ARCHITECTURE.md for module boundaries and apps/android/KEYBINDINGS.md for the keymap.
The backend is optional. You only need it if you want the per-user Docker container model (for example, to hand accounts out to teammates or run a hosted service).
cd backend
cp .env.example .env
docker compose up --buildDefault port: 8080. WebSocket endpoint is xterm.js-compatible so any browser terminal can also attach.
Deploy recipes:
- Fly.io — see
backend/fly.toml - Hostinger VPS — see
docs/deployment-hostinger.md - Your own Docker host —
docker-compose.ymlis the source of truth
API keys (Anthropic, OpenAI, etc.) are never stored by the backend. Users paste them into their own container volume after first login.
| Feature | NexaTerm | Blink Shell | Termius | Prompt 3 | Shell Fish |
|---|---|---|---|---|---|
| Native SwiftUI / Compose (no WebView) | Yes | Partial | No | Yes | Yes |
| Open source | MIT | GPL-3 | No | No | No |
| Mosh | Yes | Yes | No | No | No |
| stream-json agent rendering | Yes | No | No | No | No |
| Agent-CLI switcher (claude/gemini/...) | Yes | No | No | No | No |
| Tailnet-native host picker | Yes | Manual | Manual | Manual | Manual |
| Self-hosted backend | Yes | No | Cloud | No | No |
| Liquid Glass (iOS 26) | Yes | No | No | Partial | No |
| Material 3 Expressive (Android 16) | Yes | — | Yes | — | — |
- iOS / iPadOS — SwiftUI, SwiftTerm, Citadel (pure-Swift SSH), Secure Enclave
- Android — Jetpack Compose, Material 3, termux-view fork, Android Keystore
- Backend — FastAPI + Docker-in-Docker, per-user volumes, WebSocket bridge
- Shared — TypeScript types for the stream-json schema, host-config shape, SSH-config shape
- v0.1 — MVP (Weeks 1–8) · iOS SwiftUI + SwiftTerm + SSH + slash-command autocomplete
- v1.0 — Multi-platform (Months 3–4) · Mosh, multi-tab, Android GA, stream-json overlay on both platforms
- v2.0 — Swarm (Month 6+) · Agent dashboard across all your hosts, one-tap CLI switcher, Web PWA, watchOS companion
Track progress in CHANGELOG.md and the GitHub Projects board.
Why not just use an existing SSH client?
Existing mobile SSH clients treat agent CLI output as a wall of text. NexaTerm parses the structured stream and renders it as UI — so a 400-line diff from an agent becomes a collapsible diff card, a long tool-use trace becomes a tappable card stack, and token/cost data shows up in the status bar. It is still a full SSH client underneath.
Do I have to be on iOS 26 / Android 16?
Older versions are on the roadmap. The MVP targets the newest OS because Liquid Glass (iOS 26) and Material 3 Expressive (Android 16) are the design systems this project ships with. A compat layer for iOS 17+ is planned for v1.1.
Is my private key safe?
Keys never leave the device. iOS stores them in Secure Enclave, Android in the hardware-backed Keystore. The optional self-hosted backend never sees your keys or your API tokens.
Do you phone home?
No analytics, no telemetry, no crash reporting to a third party. Period. If you run the self-hosted bridge, you control the logs.
Can I use it without any AI CLI?
Yes. It is a great SSH + Mosh client even if you never install claude or gemini. The stream-json overlay only activates when one of those CLIs is detected.
Commercial use?
MIT licensed. Fork it, rebrand it, ship it. If you use it commercially a credit in your about screen is appreciated but not required.
PRs welcome. Please read CONTRIBUTING.md and CODE_OF_CONDUCT.md first.
- Bug? File a bug report.
- Idea? Open a feature request.
- Security issue? See
SECURITY.md.
MIT © Dominik Schenkel.
Built for the dev who lives in tmux and commutes on the U-Bahn.


