@sethwebster/rzr
v0.1.7
Published
A razor-thin remote around any terminal process
Downloads
650
Maintainers
Readme
rzr
rzr is a razor-thin remote around any terminal process.
It launches a command inside tmux, serves a tiny local web UI, and gives you a phone-friendly remote that can:
- watch a live terminal session
- paste input into it
- send terminal keys like
Enter,Tab,Ctrl+C, arrows, andEsc - let multiple devices observe the same session at once
Use it to check in on codex, claude, shells, REPLs, and other TTY-first tools from your phone.
Why it exists
Most “remote terminal” tools get complicated fast.
rzr stays small on purpose:
tmuxhandles terminal reality — real TTY behavior, durable sessions, reconnectabilityrzrhandles remote access — a tiny web server, tokenized URL access, optional public tunnel, optional password gate- your process stays normal — you still run the tool you already use
That makes it useful for:
- checking a long-running coding agent from your phone
- reconnecting to a CLI after your laptop sleeps or your browser disconnects
- exposing an existing
tmuxsession without changing how you work - letting another device observe the same live terminal
Quickstart
Requirements
bunnode20+tmux
Optional for public internet access:
cloudflaredngrok- or
npx localtunnelas fallback
Run from npm
npx @sethwebster/rzr run -- codexRun from source
git clone https://github.com/sethwebster/rzr.git
cd rzr
bun install
./rzr run -- codexrzr will print URLs like:
http://localhost:4317/?token=...
http://192.168.1.20:4317/?token=...Open one on your phone.
Mobile app development
For the Expo mobile app in apps/mobile, use the repo-local Expo CLI via Bun scripts:
bun run mobile:start
bun run mobile:ios
bun run mobile:ios:device
bun run mobile:androidDo not use npx expo@latest ... in this workspace. In this monorepo it can fail after native build with a misleading Failed to resolve react-native error because the temporary npx-installed Expo CLI resolves react-native from the wrong context.
Install
Use without installing
npx @sethwebster/rzr run -- codexInstall globally
npm install -g @sethwebster/rzr
rzr run -- codexRun from this repo
./rzr run -- codexrzr has no npm runtime dependencies.
Common examples
Start a new wrapped session
rzr run -- codexStart a named session
rzr run --name claude -- claudeStart in a specific project directory
rzr run --name codex --cwd /path/to/repo -- codexStart a shell instead of an app
rzr run --cwd /path/to/repo -- /bin/zshExpose an existing tmux session
rzr attach claudeRead-only remote view
rzr run --readonly -- codexAdd a public tunnel
rzr run --tunnel -- codexRequest a named tunnel
rzr run --tunnel --tunnel-name my-remote -- codexAdd a password gate
rzr run --password secret -- codex
rzr attach claude --password secretList tmux sessions
rzr listCommand reference
rzr run
Launch a new command inside tmux and expose it through the web UI.
rzr run [--name NAME] [--port PORT] [--host HOST] [--cwd PATH] [--readonly] [--tunnel] [--tunnel-name VALUE] [--password VALUE] -- <command...>Options:
--name NAME— tmux session name to create--port PORT— local web server port, default4317--host HOST— bind host, default0.0.0.0--cwd PATH— working directory for the launched command--readonly— disable remote input--tunnel— create a public tunnel--tunnel-name VALUE— request a provider-specific tunnel name--password VALUE— require a password before exposing the live session-- <command...>— the command to run insidetmux
rzr attach
Expose an existing tmux session.
rzr attach <tmux-session> [--port PORT] [--host HOST] [--readonly] [--tunnel] [--tunnel-name VALUE] [--password VALUE]rzr list
List local tmux sessions.
rzr listTunnel behavior
When you use --tunnel, provider order is:
- installed
cloudflared - installed
ngrok npx localtunnel
--tunnel-name behavior depends on provider:
- Cloudflare: if authenticated and the value looks like a hostname on a Cloudflare-managed zone,
rzrtries a stable named tunnel first; otherwise it is used as Quick Tunnel metadata/label - ngrok: passes the value as the tunnel name
- localtunnel: requests the value as the public subdomain
The selected tunnel is torn down when rzr exits.
Security model
rzr uses two possible gates:
- a URL token in the query string
- an optional password from
--password
Notes:
- clients always need the tokenized URL
- if
--passwordis enabled, clients must also enter the password before the UI and API are exposed - the password is passed on the command line, so it will appear in shell history and process listings
- if you expose a public tunnel, treat that URL like a secret
If you need stronger secret handling than a CLI flag, don’t rely on --password alone.
Session behavior
rzr runcreates atmuxsession for the target command- the target process keeps running inside
tmuxeven if the browser disconnects - you can reconnect later with
rzr attach <session> - pressing
Ctrl+Cin the host terminal warns that thetmuxsession will keep running, then lets you keep it, kill it, or continue serving
This project intentionally standardizes on tmux.
If you need “observe an arbitrary existing process that was not launched in tmux,” that requires OS-specific session snooping and is out of scope here.
Development
This repo is organized as a small Bun workspace monorepo. The published package lives in packages/rzr.
The Expo mobile companion lives in apps/mobile.
Run the test suite:
bun testStart the mobile app:
bun run mobile:startUseful mobile workspace commands:
bun run mobile:ios
bun run mobile:ios:device
bun run mobile:android
bun run mobile:web
bun run mobile:typecheck
bun run mobile:lintRegenerate the README demo asset:
python3 scripts/generate_readme_gif.pyShow CLI help:
rzr --helpLicense
MIT
