{"id":51700446,"url":"https://github.com/0xshawn/remote-shell","last_synced_at":"2026-07-16T10:04:17.314Z","repository":{"id":365799468,"uuid":"1273736823","full_name":"0xshawn/remote-shell","owner":"0xshawn","description":"Browser-based shell with transparent tmux-backed session persistence — refresh, close the tab, or switch devices and resume the exact same shell (cwd, env, running processes).","archived":false,"fork":false,"pushed_at":"2026-07-01T17:15:59.000Z","size":844,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-01T17:26:06.464Z","etag":null,"topics":["docker","node-pty","nodejs","persistent-sessions","pty","remote-shell","shell","terminal","tmux","ttyd","web-shell","web-terminal","websocket","xterm-js"],"latest_commit_sha":null,"homepage":null,"language":"Java","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/0xshawn.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-18T20:28:50.000Z","updated_at":"2026-07-01T17:16:02.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/0xshawn/remote-shell","commit_stats":null,"previous_names":["0xshawn/remote-shell"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/0xshawn/remote-shell","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xshawn%2Fremote-shell","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xshawn%2Fremote-shell/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xshawn%2Fremote-shell/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xshawn%2Fremote-shell/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/0xshawn","download_url":"https://codeload.github.com/0xshawn/remote-shell/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xshawn%2Fremote-shell/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35539521,"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-07-16T02:00:06.687Z","response_time":83,"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":["docker","node-pty","nodejs","persistent-sessions","pty","remote-shell","shell","terminal","tmux","ttyd","web-shell","web-terminal","websocket","xterm-js"],"created_at":"2026-07-16T10:04:16.632Z","updated_at":"2026-07-16T10:04:17.308Z","avatar_url":"https://github.com/0xshawn.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# remote-shell\n\nA monorepo for **remote-shell**, a browser-based interactive shell whose defining\nfeature is **session persistence**: refresh, close the tab, or switch devices and\nyou resume the *exact* same shell — working directory, environment, running\nprocesses, and history all intact.\n\nThe server forks a PTY directly and keeps a per-session in-memory **ring buffer**;\nthe frontend (xterm.js + WebGL, 50k-line scrollback) owns all scrolling, so it\nstays buttery-smooth and a reconnect repaints the screen from the buffer. No tmux.\n\n```\nBrowser (xterm.js + WebGL) ──WebSocket──► Go gateway ──fork──► PTY ──► bash/zsh (or `ssh host`)\n                                              │\n                                     per-session ring buffer  → reconnect repaints\n```\n\n## Projects\n\n| Path | Project | Description |\n|------|---------|-------------|\n| [`server/`](server/) | **server** | Go gateway: PTY + ring-buffer session persistence, WebSocket wire protocol, auth. See [`server/README.md`](server/README.md). |\n| [`web/`](web/) | **web** | xterm.js + WebGL frontend (no build step; vendored addons). Served by the Go server. |\n| [`android/`](android/) | **android** | Native Android client (Kotlin + Jetpack Compose, Termux terminal emulator) that speaks the same login API and WebSocket protocol. See [`android/README.md`](android/README.md). |\n| [`deploy/`](deploy/) | **deploy** | Docker Compose, Dockerfile, and the nginx TLS proxy. Driven by [`install.sh`](install.sh). |\n\n## Quick start\n\n**Server + web** (Docker) — one command, no clone needed:\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/0xshawn/remote-shell/main/install.sh | bash\n# open the printed https://\u003chost\u003e:8443  (self-signed cert → accept the warning)\n```\n\n`install.sh` fetches the repo into `~/.remote-shell` (override with\n`REMOTE_SHELL_DIR=`) when run outside a checkout, then builds the image, creates\n`.env`, auto-detects your host user, generates + persists the secrets, generates\na self-signed TLS cert, and authorizes the container's SSH key on the host —\nthen prints the login password. It is safe to re-run. From an existing clone,\nrun `./install.sh` directly.\n\nBy default the web terminal logs into the **host** shell over SSH (not the\ncontainer). To pin credentials, use real TLS certs, or switch to a plain\ncontainer shell, see [`server/README.md`](server/README.md).\n\n**Binary** (no Docker) — download one self-contained binary and run it in the\nbackground; it serves its own HTTPS:\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/0xshawn/remote-shell/main/install-binary.sh | bash\n# open the printed https://\u003chost\u003e:8443  (self-signed cert → accept the warning)\n```\n\n`install-binary.sh` downloads the prebuilt binary for your architecture, runs it\nunder **systemd** when available (else a `nohup` process that survives the shell\nclosing), and prints the generated password. The web terminal is the host user's\nown shell — no SSH hop. Serve on a **different port** with `PORT=` (e.g.\n`PORT=9443 bash remote-shell-installer-linux-amd64.sh`); pin a version with\n`REMOTE_SHELL_VERSION=`. See [`server/README.md`](server/README.md) for changing\nthe port of an existing install, management, and uninstall.\n\n**Offline / blocked network** (the server can't reach GitHub) — grab the\n**self-extracting installer** `remote-shell-installer-linux-amd64.sh` (or `-arm64`)\nfrom the [releases page](https://github.com/0xshawn/remote-shell/releases). It's\nthis script with the binary baked in, so one file is the whole package — copy it\nover and run it; no network, no separate binary, no checkout:\n\n```bash\nbash remote-shell-installer-linux-amd64.sh\n```\n\nPrefer the raw binary? Download `remote-shell-linux-amd64`, then\n`./install-binary.sh ./remote-shell-linux-amd64` (or set `REMOTE_SHELL_BIN=`).\n\nRun it directly instead (Go ≥ 1.26):\n\n```bash\ncd server \u0026\u0026 go build -o remote-shell . \u0026\u0026 cd ..\nWEB_DIR=web ./server/remote-shell --no-auth\n```\n\n**Android client:**\n\n```bash\ncd android\n./gradlew assembleDebug\n```\n\nThen point the app at your server's URL and log in with the same credentials.\n\n## Uninstall\n\nEach installer has an `uninstall` subcommand that tears down its own deployment.\n\n**Docker** — removes the containers, the volume (secrets + host SSH key), the\nimage, and the authorized host key:\n\n```bash\n~/.remote-shell/install.sh uninstall      # from the checkout (default ~/.remote-shell)\n```\n\n**Binary** — removes the systemd service (system or user), the binary, and\n`~/.remote-shell` (password, token secret, self-signed cert):\n\n```bash\nremote-shell uninstall                    # the binary cleans up after itself (add -y to skip the prompt)\n# or, if the binary is already gone:  ./install-binary.sh uninstall\n```\n\nBoth are best-effort and safe to re-run. For a **system** (root) install, run them\nwith `sudo` so root-owned units/binaries can be removed.\n\n\u003cdetails\u003e\u003csummary\u003eManual teardown (binary), if you'd rather do it by hand\u003c/summary\u003e\n\n`install-binary.sh` installs a **system** service (as root), a **user** service\n(non-root), or a bare **nohup** process. `sudo systemctl` only sees the system one\n— \"Unit could not be found\" usually means a user service or nohup. Detect, then remove:\n\n```bash\nsystemctl --user status remote-shell ; pgrep -af remote-shell    # which one is it?\n# stop the matching one:\nsudo systemctl disable --now remote-shell \u0026\u0026 sudo rm -f /etc/systemd/system/remote-shell.service\nsystemctl --user disable --now remote-shell \u0026\u0026 rm -f ~/.config/systemd/user/remote-shell.service\nkill \"$(cat ~/.remote-shell/remote-shell.pid)\" 2\u003e/dev/null\n# then:\nrm -f /usr/local/bin/remote-shell ~/.local/bin/remote-shell\nrm -rf ~/.remote-shell\n```\n\n(If `systemctl --user` reports \"Failed to connect to bus\" over SSH, run\n`export XDG_RUNTIME_DIR=/run/user/$(id -u)` first.)\n\u003c/details\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0xshawn%2Fremote-shell","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F0xshawn%2Fremote-shell","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0xshawn%2Fremote-shell/lists"}