{"id":50714508,"url":"https://github.com/moukrea/sshx-mobile","last_synced_at":"2026-06-09T18:00:38.750Z","repository":{"id":362954910,"uuid":"1261380540","full_name":"moukrea/sshx-mobile","owner":"moukrea","description":"Reach your computer's shell from your phone, anywhere, with zero infrastructure — a touch-native sshx terminal app + persistent host tooling","archived":false,"fork":false,"pushed_at":"2026-06-06T17:08:51.000Z","size":315,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-06T19:07:18.918Z","etag":null,"topics":["android","mobile","remote-shell","self-hosted","sshx","terminal","tmux","zero-infrastructure"],"latest_commit_sha":null,"homepage":"https://github.com/moukrea/sshx-mobile","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/moukrea.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-06T15:56:23.000Z","updated_at":"2026-06-06T17:08:54.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/moukrea/sshx-mobile","commit_stats":null,"previous_names":["moukrea/sshx-mobile"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/moukrea/sshx-mobile","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moukrea%2Fsshx-mobile","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moukrea%2Fsshx-mobile/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moukrea%2Fsshx-mobile/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moukrea%2Fsshx-mobile/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/moukrea","download_url":"https://codeload.github.com/moukrea/sshx-mobile/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moukrea%2Fsshx-mobile/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34118757,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","mobile","remote-shell","self-hosted","sshx","terminal","tmux","zero-infrastructure"],"created_at":"2026-06-09T18:00:23.248Z","updated_at":"2026-06-09T18:00:38.742Z","avatar_url":"https://github.com/moukrea.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# sshx-mobile\n\n**Reach your computer's shell from your phone — anywhere, with zero infrastructure.**\n\n[![Release](https://img.shields.io/github/v/release/moukrea/sshx-mobile?sort=semver)](https://github.com/moukrea/sshx-mobile/releases/latest)\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE)\n[![CI](https://github.com/moukrea/sshx-mobile/actions/workflows/pr.yml/badge.svg)](https://github.com/moukrea/sshx-mobile/actions)\n\nsshx-mobile turns an [sshx.io](https://sshx.io) session into a real, phone-native\nterminal. A small Android app reshapes the session into a single-window **tabbed\nterminal** built for touch, and a host package keeps that session **alive and\nreachable** through reboots, network changes and process restarts — so your phone\ncan always find your shell again.\n\nNo port forwarding. No VPN. No account. No server to run. The host pushes its\ncurrent session address to a free, end-to-end-encrypted relay; the app reads it\nback and reconnects automatically.\n\n\u003e **Heads up:** an sshx session URL grants full read/write access to your shell.\n\u003e Everything after `#` is the end-to-end decryption key and never reaches the\n\u003e sshx server. Treat it like a password. See [Security](#security).\n\n---\n\n## Features\n\n- **Touch-native terminal** — one active terminal fills the screen; a top tab bar\n  switches/creates/closes sessions; a bottom bar sends real `Esc Tab Ctrl Alt - /`\n  and arrow keys. Keyboard-aware: the bars lift above the on-screen keyboard.\n- **Swipe to scroll** the scrollback; start typing and you snap straight back to\n  the live prompt.\n- **Lossless persistence** — your work lives in a dedicated tmux server, so a\n  dropped connection, a new session URL, or a reboot never loses a pane.\n- **Auto-reconnect** — the app follows the host to its new address with no manual\n  re-pairing.\n- **Self-healing sessions.** If the relay drops the session (long idle, a network\n  blip, waking from sleep), the host detects it and re-mints automatically, so you\n  never land on a stale blank screen.\n- **Host windows sync to tabs** — open or close a window on the host and the app's\n  tabs follow, live.\n- **Built-in chat \u0026 presence** — sshx's collaboration, reworked for mobile, with\n  an unread badge.\n- **QR pairing** — scan once; the app remembers the resolver across session changes.\n- **Tunable density** — pick your column count (default 80) for bigger or smaller text.\n\n## How it works\n\n```\n   your phone                     a free relay                 your machine\n┌───────────────┐   reads addr   ┌─────────────┐   pushes addr  ┌──────────────────────┐\n│ sshx-mobile   │ ◀───────────── │  resolver   │ ◀───────────── │ sshx  ──▶  tmux (-L   │\n│   (the app)   │                │ (MantleDB / │                │            sshx)      │\n│               │ ──────────────▶│  self-host) │                │ persistent, isolated  │\n└───────────────┘   sshx session └─────────────┘                └──────────────────────┘\n        │                                                                    ▲\n        └────────────── end-to-end encrypted sshx session ───────────────────┘\n```\n\nThe host runs `sshx` against a persistent tmux server and publishes the current\n(rotating) session URL to a **resolver** — by default a random, private\n[MantleDB](https://mantledb.sh) namespace (no account, no email), or your own\nloopback/LAN service. The app polls the resolver, connects to the session, and\nre-syncs whenever the host's address changes.\n\n---\n\n## Install\n\nYou need two things: the **app** on your phone, and the **host tooling** on the\nmachine whose shell you want to reach.\n\n\u003e **Using Claude Code (or another coding agent)?** It can do the whole host setup for you,\n\u003e interactively — package, `sshx` CLI, always-on services, resolver + claim, verification, and\n\u003e wiring `claude` into the app's tmux. See\n\u003e [**Assisted setup with Claude Code**](./docs/setup-with-claude-code.md) (or run the bundled\n\u003e `/sshx-mobile-install` skill from a clone). You still install the APK and scan one QR.\n\n### 1. The app (Android)\n\nDownload the latest `sshx-mobile-*.apk` from the\n[**Releases**](https://github.com/moukrea/sshx-mobile/releases/latest) page and\ninstall it (you may need to allow \"install unknown apps\" for your browser/file\nmanager).\n\nYou only sideload once: **the app updates itself.** On launch it checks the\nReleases feed and, when a newer version exists, offers to download and install it\nin-app (no Play Store, no re-sideloading) — see [Updating](#updating).\n\n### 2. The host\n\n`tmux`, `python3` and `qrencode` install automatically as package dependencies. The only extra\npiece is the [`sshx`](https://sshx.io) CLI — it isn't in distro repos, so **`sshx-host-setup` offers\nto install it for you** (or run `curl -sSf https://sshx.io/get | sh`, which puts it in `/usr/local/bin`).\n\n\u003cdetails open\u003e\n\u003csummary\u003e\u003cb\u003eDebian / Ubuntu (apt)\u003c/b\u003e\u003c/summary\u003e\n\n```bash\ncurl -fsSL https://moukrea.github.io/apt-repo/pubkey.gpg | sudo gpg --dearmor -o /usr/share/keyrings/moukrea.gpg\necho \"deb [signed-by=/usr/share/keyrings/moukrea.gpg] https://moukrea.github.io/apt-repo stable main\" | sudo tee /etc/apt/sources.list.d/moukrea.list\nsudo apt update \u0026\u0026 sudo apt install sshx-mobile-host\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eFedora / RHEL (dnf)\u003c/b\u003e\u003c/summary\u003e\n\n```bash\nsudo tee /etc/yum.repos.d/moukrea.repo \u003e /dev/null \u003c\u003c'EOF'\n[moukrea]\nname=moukrea\nbaseurl=https://moukrea.github.io/rpm-repo/\nenabled=1\ngpgcheck=1\ngpgkey=https://moukrea.github.io/rpm-repo/pubkey.gpg\nEOF\nsudo dnf install sshx-mobile-host\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003emacOS / Linux (Homebrew)\u003c/b\u003e\u003c/summary\u003e\n\n```bash\nbrew install moukrea/tap/sshx-mobile-host\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eArch Linux\u003c/b\u003e\u003c/summary\u003e\n\n```bash\n# Grab the PKGBUILD from the matching release and build it:\ncurl -fsSLO https://github.com/moukrea/sshx-mobile/releases/latest/download/PKGBUILD\nmakepkg -si\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eFrom source\u003c/b\u003e\u003c/summary\u003e\n\n```bash\ngit clone https://github.com/moukrea/sshx-mobile.git\ncd sshx-mobile\nhost/install.sh\n```\n\u003c/details\u003e\n\n### 3. Connect\n\nOn the host, run the one-shot setup (as your normal user, **not** root):\n\n```bash\nsshx-host-setup\n```\n\nIt generates a private resolver, enables the per-user services, and prints a **QR\ncode**. Open the app, tap **Scan QR**, point it at the code — done. Your shell is\nnow in your pocket, and it will stay reachable across reboots and network changes.\n\nTo check status later: `systemctl --user status sshx tmux-server sshx-tmux-bridge`.\n\n### Updating\n\n**The app updates itself.** On launch (from the home screen, so it never\ninterrupts a live session) it checks the GitHub Releases feed; if a newer version\nis out it shows an **\"Update available\"** dialog. Tap **Update** and it downloads\nthat release's APK and hands it to the system installer — you grant \"install\nunknown apps\" to the app once, the first time. **Later** re-prompts next launch;\n**Skip** silences that one version. (An update can only self-install if it is\nsigned with the same key as the installed app, which holds for these releases; a\nkey change would need a one-time manual reinstall.)\n\n**The host** you upgrade the way you installed it, then re-run setup so the new\nversion is the one actually running:\n\n```bash\nsudo apt update \u0026\u0026 sudo apt install --only-upgrade sshx-mobile-host   # or: sudo dnf upgrade sshx-mobile-host / brew upgrade\nsshx-host-setup\n```\n\n`sshx-host-setup` restarts `sshx` and the window bridge (lossless: your tmux\nsession is preserved, the phone re-resolves automatically) so the update takes\neffect right away.\n\n---\n\n| Gesture / control | Action |\n|---|---|\n| Tap a tab | Switch terminal |\n| Burger menu (top-right) | Columns (text size), new terminal, connected people, chat |\n| `×` on a tab | Close that terminal |\n| Bottom bar | `Esc Tab Ctrl Alt - /` and arrow keys (real terminal input) |\n| Set your name (main menu) | Shown to others in the session, persisted across reconnects |\n\n### Scrolling \u0026 selecting\n\nScrolling works on **both** surfaces, including inside full-screen TUIs such as\nClaude Code (whose output lives in the scrollback):\n\n- **Scroll** — on the **phone**, swipe up/down on the terminal; on a **laptop/desktop**\n  tmux client (`sshx-desk`, or a `claude` session wired through the app's tmux) use the\n  **mouse wheel**. To resume typing after scrolling up, scroll back to the bottom (or\n  press `q` / `Enter`).\n\n**Selecting text on the desktop.** The wheel and plain-drag native selection can't both\nbe live at once — that's a tmux limitation, not a bug (tmux either owns the mouse for the\nwheel, or releases it for selection). The desktop defaults to **wheel on**, so:\n\n- Hold **Shift while you drag** to select — the terminal's own native selection (works on\n  every terminal, copies via the terminal itself). This is the standard way to select over\n  any mouse-aware program.\n- Or run **`sshx-mouse off`** to switch that session to plain-drag native selection for a\n  while (the wheel then sends arrows until `sshx-mouse on`). `sshx-mouse` with no argument\n  toggles.\n\n\u003e The mouse setting is **per surface**: desktop sessions (`sshx-desk`, `claude` windows)\n\u003e keep `mouse on` for the wheel; phone tabs are `mouse off`, so a stray touch never drops\n\u003e the pane into tmux copy-mode. The phone keeps scrolling because its swipe is sent as\n\u003e keys, not mouse.\n\n**Selecting text on the phone** is limited today: sshx renders the terminal with WebGL and\nkeeps its terminal object private, and sshx itself has no clipboard support\n([sshx#77](https://github.com/ekzhang/sshx/issues/77)), so the app can't yet pull selected\ntext into the Android clipboard. Tracked for a follow-up (a tmux-capture bridge). For now,\ncopy from the desktop side.\n\nCo-access from your desktop at the same time with `sshx-desk` (a normal tmux\nclient sharing the windows). Route `claude` (or any command) into the shared\nsession with the installed `claude-tmux.sh` helper so it shows up as a tab — the\nhost installer wires this into `~/.bashrc` automatically; for zsh add the\ndelegating wrapper from\n[`docs/setup-with-claude-code.md`](./docs/setup-with-claude-code.md#wire-claude-into-the-apps-tmux).\n\n---\n\n## Troubleshooting\n\n**App shows a blank/black screen with no terminals after scanning the QR.** The sshx\nsession the resolver points at has gone stale (sshx sessions expire after a long idle or\na network drop, and the host process doesn't always notice). **The host now detects this\nand heals itself**: a liveness monitor re-mints the session automatically, usually within\na minute, and the app re-resolves and reconnects with **no re-scan needed**. Just wait a\nmoment. To force it immediately:\n\n```bash\nsystemctl --user restart sshx.service\n```\n\nThis self-heal is on by default; tune or disable it with `SSHX_HEALTH_*` (see\n[`host/README.md`](./host/README.md#self-heal-a-stale-session-is-detected-and-re-minted-automatically)).\nPeriodic rotation is now optional (it only limits how long any one URL is exposed):\n\n```bash\nsystemctl --user enable --now sshx-rotate.timer    # default 1h; `systemctl --user edit sshx-rotate.timer` to change\n```\n\n**Check the host services are up:**\n\n```bash\nsystemctl --user status tmux-server sshx sshx-tmux-bridge\n```\n\n**New host windows don't appear as tabs / a closed tab lingers.** The window list is\npublished by `sshx-tmux-bridge`; make sure it's `active`. More operational recipes\n(reboot, no URL served, sizing) are in [`docs/runbook.md`](./docs/runbook.md).\n\n---\n\n## Security\n\n- The session URL's `#fragment` is the **end-to-end encryption key**. sshx uses it\n  only in the browser; it never reaches the sshx server. Anyone with the full URL\n  can use your terminal.\n- `sshx-qr` encodes the URL **offline** — it never sends it anywhere. Never paste\n  an sshx URL into an online QR generator.\n- The resolver stores the full URL (incl. the key). The default MantleDB namespace\n  is random and private (the namespace itself is a bearer secret). **Claim it**\n  (`sshx-resolver-setup --claim`, no email) to attach a key: as of 2026-06 a claimed\n  namespace returns HTTP 401 without the key on **reads and writes** (verified — see\n  [`docs/setup-with-claude-code.md`](./docs/setup-with-claude-code.md)), so claiming is what makes a\n  *readable* namespace safe. For guaranteed read-privacy with zero third-party trust, run the bundled\n  self-hosted resolver over a network you control. Never expose a resolver to the public internet.\n  (Note: MantleDB lowercases namespaces on claim — use the lowercase form.)\n- `host/resolver.env` (your live resolver config) is git-ignored and never\n  published. Only `resolver.env.example` ships.\n\n---\n\n## Build from source\n\n**App:** `cd android \u0026\u0026 ./gradlew assembleRelease` → `app/build/outputs/apk/release/`.\nRequires JDK 17 and the Android SDK. See [`android/BUILD.md`](./android/BUILD.md).\n\n**Host packages:** `SSHX_VERSION=x.y.z nfpm pkg -p deb -f packaging/nfpm.yaml -t dist/`\n(and `-p rpm`). CI builds and publishes them on every `x.y.z` tag — see\n[`.github/workflows/`](./.github/workflows/).\n\n## Design \u0026 internals\n\nThe lossless remote-access design, decisions and runbook live in\n[`docs/`](./docs/): [setup-host](./docs/setup-host.md),\n[setup-app](./docs/setup-app.md), [runbook](./docs/runbook.md), and the\n[ADRs](./docs/adr/). Host tooling reference: [`host/README.md`](./host/README.md).\nApp internals: [`android/README.md`](./android/README.md).\n\n## License\n\n[MIT](./LICENSE) © moukrea\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoukrea%2Fsshx-mobile","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoukrea%2Fsshx-mobile","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoukrea%2Fsshx-mobile/lists"}