keep remote coding agents available after logout, crashes, and server reboots.
each agent gets its own small, inspectable installer. no shared runtime and no blind process killing.
| agent | installer | status |
|---|---|---|
| codex | install-codex.sh |
ready |
| claude | install-claude.sh |
ready |
- linux with systemd user services
- internet access;
curlorwgetis needed only when codex is missing sudoaccess if user lingering is not already enabled
download and inspect the script:
wget -q https://raw.githubusercontent.com/axhoff/hands-off-agents/main/install-codex.sh
less install-codex.sh
bash install-codex.sh --pairthe installer:
- installs codex with openai's official standalone installer when it is missing
- guides chatgpt device login and leaves the browser link and one-time code visible
- checks the platform, systemd, and chatgpt authentication
- enables user lingering so services start without an ssh login
- discovers codex's managed standalone binary
- retries a failed relay enrollment after a clean daemon stop and reports
codex doctordiagnostics if recovery fails - replaces the temporary pid daemon with a systemd user service
- backs up an existing service file before replacing it
- enables restart-on-failure and startup after reboot
- waits for the remote relay connection
- optionally prints a short-lived device pairing code
systemctl --user status codex-remote-control.service
systemctl --user restart codex-remote-control.service
journalctl --user -u codex-remote-control.service -fif setup reports an errored connection, the installer now attempts the common stop/start recovery three times. persistent failures usually need a refreshed chatgpt device login, mfa, workspace remote-control permission, or outbound https access—not manual deletion of codex state files.
the installer prepares desktop-over-ssh access, completes claude.ai login when needed, then runs claude code remote control as a systemd user service. the login flow displays any one-time code directly in the terminal, and the installer prints the remote session url after starting the service.
remote control runs locally and connects outward over https. systemd keeps it running inside a detached tmux session after ssh disconnects, restarts it after crashes or long network outages, and starts it again after a server reboot.
the usual culprit is MaxSessions 2 in a hardened sshd config: the first
couple of channels (probe, version check) fit, the one that launches the
server is refused.
- linux (amd64/arm64) with an openssh server
- systemd user services
- tmux
sudoaccess for sshd checks and fixes- claude code v2.1.51 or later
- a claude.ai subscription login; api-key authentication is not supported
- permission to trust the selected project directory when claude first opens it
- run as the user the app connects as, on the remote host
download and inspect the script:
wget -q https://raw.githubusercontent.com/axhoff/hands-off-agents/main/install-claude.sh
less install-claude.sh
bash install-claude.shthe guided finish will:
- run
claude auth login --claudeaiwhen needed, leaving the login url and one-time code visible in your terminal - enable user services at boot with lingering
- install and start
claude-remote-control.service, supervising a detached tmux session - print the direct claude.ai session url
run it from the project directory you want to expose, or specify one:
bash install-claude.sh --project ~/src/my-project --name "my server"or report-only, changing nothing:
bash install-claude.sh --checkprepare desktop-over-ssh access without starting remote control:
bash install-claude.sh --prepare-onlythe script:
- checks architecture, home writability,
noexecmounts, and disk space - checks that shell rc files stay silent for non-interactive ssh commands
- puts
claudeon the non-interactive PATH (above any interactivity guard) - checks sshd for
ForceCommand,PermitTTY, restrictedauthorized_keys - raises
MaxSessionsto 10 where a hardened config lowered it, editing only the global section and never touchingMatchblocks - backs up every file it edits, validates with
sshd -tbefore reloading, and rolls everything back automatically if validation or reload fails - checks the remote-control cli version and incompatible auth/provider vars
- guides claude.ai login without capturing the one-time code
- runs server mode in tmux under a restartable systemd user service
- extracts and prints the session url from the detached terminal
systemctl --user status claude-remote-control.service
systemctl --user restart claude-remote-control.service
systemctl --user stop claude-remote-control.service
tmux -L hands-off-claude-remote-control attach -t remote-control
journalctl --user -u claude-remote-control.service -fthe tmux session is the background terminal. attach to it and press space for a
qr code; detach without stopping claude with ctrl-b d. you can also open the
printed url directly or select the online session in claude.ai/code or mobile.
use the systemd stop command above when you intentionally want it to stay off.
if the service cannot start because the project is not trusted, open it once interactively, accept the trust prompt, exit claude, and restart the service:
cd /path/to/project
claude
systemctl --user restart claude-remote-control.servicethe app's client-side log on the mac names the failing step:
tail -40 ~/Library/Logs/Claude/ssh.logexec channel open failed after a successful auth is the MaxSessions
symptom. an AllowTcpForwarding no warning from the script can be ignored
until everything else checks out.
- use one app-server owner per host
- don't run a separate ssh-launched codex app-server beside this service
- don't use broad commands such as
pkill codex - the host must remain powered on and connected to the internet
- codex remote control is currently experimental
see the official remote connections documentation.
claude setup follows the official remote control documentation.
mit