Skip to content

0xshawn/remote-shell

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

remote-shell

A monorepo for remote-shell, a browser-based interactive shell whose defining feature is session persistence: refresh, close the tab, or switch devices and you resume the exact same shell — working directory, environment, running processes, and history all intact.

The server forks a PTY directly and keeps a per-session in-memory ring buffer; the frontend (xterm.js + WebGL, 50k-line scrollback) owns all scrolling, so it stays buttery-smooth and a reconnect repaints the screen from the buffer. No tmux.

Browser (xterm.js + WebGL) ──WebSocket──► Go gateway ──fork──► PTY ──► bash/zsh (or `ssh host`)
                                              │
                                     per-session ring buffer  → reconnect repaints

Projects

Path Project Description
server/ server Go gateway: PTY + ring-buffer session persistence, WebSocket wire protocol, auth. See server/README.md.
web/ web xterm.js + WebGL frontend (no build step; vendored addons). Served by the Go server.
android/ android Native Android client (Kotlin + Jetpack Compose, Termux terminal emulator) that speaks the same login API and WebSocket protocol. See android/README.md.
deploy/ deploy Docker Compose, Dockerfile, and the nginx TLS proxy. Driven by install.sh.

Quick start

Server + web (Docker) — one command, no clone needed:

curl -fsSL https://raw.githubusercontent.com/0xshawn/remote-shell/main/install.sh | bash
# open the printed https://<host>:8443  (self-signed cert → accept the warning)

install.sh fetches the repo into ~/.remote-shell (override with REMOTE_SHELL_DIR=) when run outside a checkout, then builds the image, creates .env, auto-detects your host user, generates + persists the secrets, generates a self-signed TLS cert, and authorizes the container's SSH key on the host — then prints the login password. It is safe to re-run. From an existing clone, run ./install.sh directly.

By default the web terminal logs into the host shell over SSH (not the container). To pin credentials, use real TLS certs, or switch to a plain container shell, see server/README.md.

Binary (no Docker) — download one self-contained binary and run it in the background; it serves its own HTTPS:

curl -fsSL https://raw.githubusercontent.com/0xshawn/remote-shell/main/install-binary.sh | bash
# open the printed https://<host>:8443  (self-signed cert → accept the warning)

install-binary.sh downloads the prebuilt binary for your architecture, runs it under systemd when available (else a nohup process that survives the shell closing), and prints the generated password. The web terminal is the host user's own shell — no SSH hop. Serve on a different port with PORT= (e.g. PORT=9443 bash remote-shell-installer-linux-amd64.sh); pin a version with REMOTE_SHELL_VERSION=. See server/README.md for changing the port of an existing install, management, and uninstall.

Offline / blocked network (the server can't reach GitHub) — grab the self-extracting installer remote-shell-installer-linux-amd64.sh (or -arm64) from the releases page. It's this script with the binary baked in, so one file is the whole package — copy it over and run it; no network, no separate binary, no checkout:

bash remote-shell-installer-linux-amd64.sh

Prefer the raw binary? Download remote-shell-linux-amd64, then ./install-binary.sh ./remote-shell-linux-amd64 (or set REMOTE_SHELL_BIN=).

Run it directly instead (Go ≥ 1.26):

cd server && go build -o remote-shell . && cd ..
WEB_DIR=web ./server/remote-shell --no-auth

Android client:

cd android
./gradlew assembleDebug

Then point the app at your server's URL and log in with the same credentials.

Uninstall

Each installer has an uninstall subcommand that tears down its own deployment.

Docker — removes the containers, the volume (secrets + host SSH key), the image, and the authorized host key:

~/.remote-shell/install.sh uninstall      # from the checkout (default ~/.remote-shell)

Binary — removes the systemd service (system or user), the binary, and ~/.remote-shell (password, token secret, self-signed cert):

remote-shell uninstall                    # the binary cleans up after itself (add -y to skip the prompt)
# or, if the binary is already gone:  ./install-binary.sh uninstall

Both are best-effort and safe to re-run. For a system (root) install, run them with sudo so root-owned units/binaries can be removed.

Manual teardown (binary), if you'd rather do it by hand

install-binary.sh installs a system service (as root), a user service (non-root), or a bare nohup process. sudo systemctl only sees the system one — "Unit could not be found" usually means a user service or nohup. Detect, then remove:

systemctl --user status remote-shell ; pgrep -af remote-shell    # which one is it?
# stop the matching one:
sudo systemctl disable --now remote-shell && sudo rm -f /etc/systemd/system/remote-shell.service
systemctl --user disable --now remote-shell && rm -f ~/.config/systemd/user/remote-shell.service
kill "$(cat ~/.remote-shell/remote-shell.pid)" 2>/dev/null
# then:
rm -f /usr/local/bin/remote-shell ~/.local/bin/remote-shell
rm -rf ~/.remote-shell

(If systemctl --user reports "Failed to connect to bus" over SSH, run export XDG_RUNTIME_DIR=/run/user/$(id -u) first.)

About

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).

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors