Install
openclaw skills install @jonathan97480/ssh-handoffCreate and reuse a secure shared terminal handoff when a human must authenticate first and the agent must resume work in the same shell session afterward. Use for SSH handoff, sudo handoff, browser-opened temporary terminal access, or LAN-restricted terminal sharing backed by tmux when direct agent authentication is blocked or undesirable.
openclaw skills install @jonathan97480/ssh-handoffUse this skill when a human must complete a sensitive authentication step first, and the agent should continue in the exact same shell session afterward.
The core pattern is:
tmux sessionPrefer this skill when the user should not paste credentials into chat and when direct agent authentication is blocked, undesirable, or less safe than a shared-session handoff.
Prefer Mode A unless browser access is actually needed.
Use when the human already has terminal access to the host.
Typical flow:
tmux sessiontmuxExample:
tmux has-session -t handoff-session 2>/dev/null || tmux new-session -d -s handoff-session
tmux attach -t handoff-session
Use when the human wants a browser-based terminal on the same machine that hosts the service.
This mode uses ttyd directly and fits:
Bundled launcher:
./scripts/start-local-web-terminal.sh handoff-session
Use when the human opens the terminal from another trusted machine on the same local network and you want less friction than repeated username/password entry.
This mode works like this:
tmux session holds the real shell statettyd runs on localhost as the terminal backend?token=tmux sessionBundled launcher:
./scripts/start-url-token-web-terminal.sh handoff-session
Check these first:
command -v tmux
command -v ttyd
command -v node
command -v python3
Install on Debian / Ubuntu:
sudo apt update && sudo apt install -y tmux ttyd
node must also exist for Mode C because the proxy launcher uses the bundled Node script.
tmux has-session -t handoff-session 2>/dev/null || tmux new-session -d -s handoff-session
Use placeholders that match the environment instead of copying real addresses blindly. The 192.0.2.x addresses below are documentation-only example addresses.
HOST=<server-ip> CLIENT_IP=<trusted-client-ip> PORT=48080 UPSTREAM_PORT=48081 FORBID_REUSE_IF_AUTHENTICATED=1 ./scripts/start-url-token-web-terminal.sh handoff-session
The launcher prints:
Example with documentation-only IPs:
HOST=192.0.2.10 CLIENT_IP=192.0.2.20 PORT=48080 UPSTREAM_PORT=48081 FORBID_REUSE_IF_AUTHENTICATED=1 ./scripts/start-url-token-web-terminal.sh handoff-session
Always inspect the pane before assuming the handoff worked:
tmux capture-pane -t handoff-session -p | tail -80
Look for:
If uncertain, ask one short confirmation question before continuing.
start-local-web-terminal.shSupported variables:
HOST — bind address, default 127.0.0.1PORT — optional explicit port, otherwise a random free portTTL_MINUTES — default 30BIND_SCOPE — metadata only, usually local or lanCLIENT_IP — optional, used only to print UFW helper commands in LAN modestart-url-token-web-terminal.shSupported variables:
HOST — proxy bind address, default 127.0.0.1PORT — proxy frontend port, default 48080UPSTREAM_PORT — localhost ttyd backend port, default 48081CLIENT_IP — optional trusted client IP for UFW helper commands and proxy-side IP filteringTTL_MINUTES — default 30BIND_SCOPE — metadata only, usually local or lanCOOKIE_SECURE — set to 1 when serving through local HTTPS so the session cookie gets the Secure flagEXPECTED_HOST — strict allowed Host header, default <HOST>:<PORT>EXPECTED_ORIGIN — strict allowed websocket Origin, default derived from host and cookie modeFORBID_REUSE_IF_AUTHENTICATED — set to 1 to refuse startup if the tmux pane already looks authenticatedREPLACE_EXISTING — set to 1 only after the human explicitly approves replacing the current web handoff for the same SESSION_NAME; otherwise the launcher prints the existing URL/PIDs/cleanup command and exits without touching the running sessionAUTH_GUARD_REGEX — optional override for the pane-authentication detection regexIf the default ports are already occupied, override them explicitly.
When a handoff already exists for the same SESSION_NAME, the launcher does not silently start a second one: it reports the existing session details and exits with READY=0. Ask the human whether to replace it. Use REPLACE_EXISTING=1 only after explicit approval.
If the requested proxy or upstream port is already occupied, the launcher also exits with READY=0 and reports a port conflict instead of killing anything automatically. In that case, either get approval to replace the conflicting session or relaunch on another port and update firewall rules if LAN access is involved.
tmux session existstmuxFor Mode B, use:
./scripts/stop-local-web-terminal.sh <pid> <session-name>
For Mode C, prefer the printed cleanup command because it removes both temporary processes and the temporary runtime directory:
TTYD_PID=<ttyd-pid> PROXY_PID=<proxy-pid> RUNTIME_DIR=<runtime-dir> <cleanup-script>
If that command is unavailable, killing the printed proxy and backend PIDs is still acceptable.
The launcher also installs automatic TTL cleanup for the proxy, ttyd, and temporary files. Leave the tmux session alive if it may be reused.
tmux session per target or task.FORBID_REUSE_IF_AUTHENTICATED=1 by default for normal use; disable it only when reopening an already-authenticated session is intentional.Host, websocket Origin, or client IP when those checks are configured.tmux handoff instead when the interaction happens over an external channel, unless the human explicitly confirms a trusted local-network setup and accepts the risk.Read these when needed:
references/examples.md — generic usage examplesreferences/design-notes.md — security and design envelopereferences/lan-restricted.md — LAN-only restricted-IP patternBundled scripts:
scripts/start-local-web-terminal.shscripts/start-url-token-web-terminal.shscripts/stop-local-web-terminal.shscripts/url-token-proxy.js