Your Mac is the remote control. A Linux box is the garage. One command brings every agent window back.
Status: v0.1 β experimental. This is extracted from a setup that runs 20+ concurrent Claude sessions on a Hetzner AX41 24/7. The code and docs are solid, but it has not yet been end-to-end installed on a clean box from this repo. Expect small paper cuts. Issues + PRs very welcome.
moto is an opinionated, reproducible setup for running an army of AI coding agents (Claude Code, Codex, opencode) on a big Linux box, controlled from your Mac, with:
- πͺ One iTerm window, many tabs β every agent session as a tab, restored with one command after a reboot, laptop close, or Mac sleep
- π Full-access Mac tunnel β the server can read/write your Mac's
~/.claudelive over SSHFS (reverse tunnel), so your local config is the source of truth - π
motoβ one command from the Mac:moto upreopens everything,moto newspawns a new session,moto killcleans up - β»οΈ Reboot / OOM survival β
tmuxis OOM-immune, containers auto-restart, SSHFS re-mounts every 30s, stale processes are reaped nightly - π Residential-IP egress β drop-in proxy container, all agent traffic rewritten through your provider of choice
- π§° Code-execution sandboxes β pre-wired containers for rendering, Node dev servers, and a logged-in headless Chrome with CDP exposed on the Docker bridge
- π§Ή Auto-cleanup β orphan
next dev/tsx watch/ Chrome / oldnode_modulesare garbage-collected so the box doesn't rot
Built by @federicodeponte to run 20+ concurrent Claude sessions on a Hetzner AX41 without ever babysitting the box.
- You run Claude Code (or Codex, or opencode) all day and your laptop's fan is tired.
- You already have a VPS / dedicated box (Hetzner, OVH, Latitude, Fly machine, your own homelab) and want to stop SSH-ing in and re-creating tmux windows by hand.
- You want agents that survive: Mac sleep, laptop closed, server OOM, server reboot β
moto uprestores the same 20 tabs in one iTerm window every time. - You need agents that look logged-in: shared Chrome with CDP on the Docker bridge, so agents attach to an already-authenticated browser instead of logging into Google / GitHub / LinkedIn per session.
- You don't want to maintain scattered dotfiles: the whole setup is one repo, one
install.sh mac, oneinstall.sh server-remote.
Not for you if: you only run agents locally, you're on Windows, or you're looking for a hosted SaaS.
git clone https://github.com/buildingopen/moto.git
cd moto
cp .env.example .env
$EDITOR .env # fill in AX41_HOST, MAC_USER, etc.
./install.sh mac # on your Mac
./install.sh server # via SSH on your Linux boxThen from the Mac:
moto up # reopen every tmux session in ONE iTerm window
moto new myproj/feature # spawn a fresh Claude session
moto ls # list active sessions
moto kill myproj/feature # kill oneβββββββββββββββββββββββββββββββ YOUR MAC βββββββββββββββββββββββββββββββ
β β
β iTerm (one window, N tabs) ~/.claude (source of truth) β
β β β² β
β β tmux -CC (ControlCC) β sshfs reverse mount β
β β over SSH ControlMaster β β
β βΌ β β
β moto CLI ββββ ssh ax41 βββββΊ ax41:22 β βββ ssh -R 2222 ββ β
β β Mac sshd β β
ββββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββΌβββββββ
β β
βββββββββββββββββββββββββ YOUR LINUX BOX βββΌββββββββββββββββββββββΌβββββββ
β βΌ β β
β /mnt/mac-claude (FUSE view of Mac ~/.claude) β β
β β β
β tmux-server.service (MemoryLow=16G, OOMScoreAdjust=-900) β β
β ββ tmux session myproj/feature β claude β β
β ββ tmux session other/main β codex β β
β ββ ... (each a tab in your Mac iTerm) β β
β β β
β authenticated-chrome.service β Xvfb :98 + Chrome :9222 ββββ€ β
β chrome-bridge-keeper.service β CDP keepalive β β
β cdp-docker-proxy.service β 172.17.0.1:9222 for agents β β
β β β
β mac-mount-check.timer β every 30s: remount if stale βββββββββ β
β moto-cleanup.timer β every 10min: reap orphans β
β node-modules-gc.timer β nightly: rm old node_modules β
β β
β docker compose: ββββββββββββββββββββββββββββββββββββββββββββ β
β β proxy (residential IP egress, all-in) β β
β β runtime-api (node execution sandbox) β β
β β dev-sandbox (sshd + node, port 2223) β β
β β cloudflared (outbound public tunnels) β β
β ββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
See docs/architecture.md for the full diagram.
| Problem | Typical solution | moto |
|---|---|---|
| Laptop sleeps, agents die | Run on laptop, restart | Agents live in tmux on server, Mac just attaches |
| Server OOM kills your session | oom_score_adj guesses |
tmux-server.service reserves 16 GB + OOMScoreAdjust=-900; earlyoom picks someone else |
| Claude config drifts between machines | rsync / Dropbox / git |
SSHFS reverse mount β the Mac is the filesystem |
| Re-opening 20 windows is tedious | Shell for-loop | moto up β kills iTerm, creates one window, opens every session as a tab, retries stubborn ones 5Γ |
| Agents get rate-limited by IP | Random VPN | Dedicated proxy container; only outbound agent traffic is rewritten |
| Agents need to use logged-in services | New login per session | Shared authenticated-chrome with CDP β agents attach, don't log in |
| Reboot nukes everything | docker-compose up by hand |
All services are systemd-managed with Restart=on-failure, containers are --restart unless-stopped |
Mac: macOS 13+, iTerm2, Homebrew, ssh (ControlMaster), OpenSSH server (for reverse tunnel).
Server: Debian 12 / Ubuntu 22.04+, root or sudo, β₯16 GB RAM recommended, public IPv4. Tested on Hetzner AX41.
Agent CLIs (install separately on the server after ./install.sh server) β moto wraps these, it doesn't bundle them:
# Claude Code
npm i -g @anthropic-ai/claude-code && claude /login
# Codex (OpenAI)
npm i -g @openai/codex
# opencode (optional)
npm i -g opencodemoto new foo/bar needs claude in PATH. moto newx needs codex. moto newo needs opencode. Only install what you'll actually use.
moto up # restore all sessions in one iTerm window (idempotent)
moto new NAME # create session NAME (format: project/task)
moto attach NAME # attach existing session as a new iTerm tab
moto ls # list server sessions
moto kill NAME # kill a session
moto status # server health: tmux count, mount status, chrome, containers
moto img PATH # scp an image to the server and print its remote path
moto logs # tail moto-cleanup + mac-mount-check logs
moto down # detach all clients (leaves sessions running)
moto doctor # diagnose the setup
Legacy aliases (ax, axo, axn, axk, axl, axwin, aximg, axd, axq) are preserved β see mac/shell/20-sessions.zsh.
- Architecture β how the pieces fit
- Bootstrap a fresh server β zero-to-moto on a new box
- Browser login β how to log
authenticated-chromeinto Google/GitHub/LinkedIn - Residential proxy setup β Bright Data, Smartproxy, or generic SOCKS5
- Reboot recovery β what happens when the box comes back up
- Claude config sync (why SSHFS) β why not rsync/git
- Cold install validated in a throwaway container (
debian:12on the author's Hetzner AX41 β 50/50 checks pass, seeserver/test/). Not yet validated on a bare-metal box from zero, but every piece the installer touches on a new box β apt, Chrome, Docker CE, systemd unit syntax, script layout β is exercised by that test. - Residential proxy sidecar end-to-end tested with
server/test/proxy-smoke.sh: URL parser handles http/https/socks5 with and without auth, direct egress works whenPROXY_URLis empty, and chained traffic is proved to flow through the upstream (parent tinyproxy's own logs show the forwarded request). Tinyproxy under the hood β we tried 3proxy first, parent directive was silently ignored across three versions. - Agent CLIs not bundled:
claude/codex/opencodeare npm installs β see Requirements. chrome-bridge-keeperships as a simple bash CDP-ping loop. The author's real setup uses a ~300-line Python variant that also auto-patches the Claude Code browser extension; too specific to include here. Add# MOTO_KEEP_LOCALto your own script andserver/install.shwill preserve it.- x86_64 only for now β Chrome, Docker images, and the Hetzner target are all amd64. ARM support is untested.
- IPv6: reverse tunnel and SSHFS work over IPv4; IPv6 is not explicitly tested.
- No automated e2e test for the reverse-tunnel β SSHFS β
moto uppath.moto doctorcovers static health.
If you hit something, please open an issue β most gaps are 10-minute fixes once surfaced.
PRs welcome. Before submitting:
shellcheck -S warning server/bin/* mac/bin/moto server/test/*.sh server/docker/proxy/entrypoint.sh
(cd server/docker && docker compose config) >/dev/null
HOST=your.host ./server/test/run-container-test.sh # full isolated install test
HOST=your.host ./server/test/proxy-smoke.sh # proxy sidecar end-to-endSee server/test/README.md for what the tests cover.
moto pairs well with other BuildingOpen tools for Claude Code operators:
| Project | What it does |
|---|---|
| claude-setup | The Claude Code config (~/.claude) that moto syncs to the server β 60+ skills, 12 safety hooks, CLAUDE.md templates |
| openqueen | Autonomous agent orchestrator β spawn Claude / Codex workers on moto from WhatsApp or Telegram |
| bouncer | Gemini-powered quality gate that audits Claude's output before it can stop |
| session-recall | Recover Claude Code context after automatic compaction |
| browse | Browser automation CLI β the agents running inside moto use this against the shared Chrome CDP |
| openbrowser | MCP server that gives agents authenticated browser access |
| blast-radius | Find every file affected by a change β one bash script |
MIT Β© Federico de Ponte. See LICENSE.