{"slug": "setting-up-your-spare-mac-for-claude-code-to-control-a-step-by-step-guide", "title": "Setting up your spare Mac for Claude Code to control, a step-by-step guide", "summary": "A guide explains how to set up a spare Mac as a dedicated machine for Anthropic's Claude Code AI agent, enabling remote control via SSH and the Claude app while isolating risks from the user's primary computer. The setup involves enabling SSH, granting passwordless sudo, and optionally erasing the machine to prevent data exposure.", "body_md": "Here’s a full step-by-step guide on how to turn your spare Mac into an always-on machine Claude Code can fully control, with computer use enabled. You’ll be able to talk to it from your phone through the Claude app, or from your main Mac over SSH.\n\nI wanted to create a separate environment Claude Code can control on its own, so I can delegate tasks I don’t necessarily want to run on my own machine - certain types of research tasks, and development tasks.\n\nClaude Code, especially with the `--dangerously-skip-permissions`\n\nflag on,\ncarries inherent risk when run on your main machine. You can eliminate / mitigate these\nrisks by creating a separate environment on your spare Mac with everything it needs\nto have access to.\n\nIt has an added bonus of being able to talk to Claude Code anytime, anywhere from your phone. I’ve personally found it really useful because I often prefer to talk to Claude Code instead of regular Claude on the mobile app - Claude Code is often more capable.\n\nThe following guide assumes you have your main Mac as well as a spare Mac you can set up for this, but you should be able to take inspiration from it and apply it to any combination of two machines.\n\nFirst, let’s quickly address a few questions you might have.\n\nI’m a big proponent of running it in a container - I even built\n[an entire environment for doing so conveniently](https://github.com/ykdojo/safeclaw).\nHowever, I’ve found it has a few limitations.\nFirst, it still runs on your main machine, so it’s not completely separated. For\nexample, network requests it sends still go through your main machine.\n\nSecond, there are limitations to the container’s capabilities. For example, I wanted my agent to be able to run Unity for game development, and there’s no easy way to do that in a container. The same goes for any other app that’s only available on a Mac - you won’t have access to it. That’s especially relevant if you want Claude Code to control these apps through computer use - clicking, dragging, and so on.\n\nI personally like having access to the full, latest features of Claude Code. I also like being able to control it from the Claude app - I’ve found it really convenient. And you get to use your Claude subscription usage if you happen to have one, which is an added bonus.\n\nAt the end of the day, running an agent with broad permissions is safer on a machine that has nothing to lose - but you get the benefit of being able to use a full Mac instead of a container. The approach here:\n\nYou’ll be giving the agent full access to this machine, so it can reach anything stored on it. If there’s existing data you don’t want it to have access to, erase the machine first:\n\nOptionally update to the latest macOS afterward (System Settings -> General -> Software Update).\n\nThe account needs admin rights or `sudo`\n\nwill refuse to run.\n\n`sudo dseditgroup -o edit -a <user> -t user admin`\n\nOn the **target**, turn on SSH so the source Mac can connect:\n\n```\nsudo systemsetup -setremotelogin on\n```\n\nIf the command fails with ```\nTurning Remote Login on or off requires Full Disk Access\nprivileges\n```\n\n, give your terminal app Full Disk Access first:\n\nThis is so the agent (and your SSH commands) can run admin tasks without a password prompt each time. Run this once on the target. It asks for the login password this one time:\n\n```\necho \"<user> ALL=(ALL) NOPASSWD: ALL\" | sudo tee /etc/sudoers.d/<user>-nopasswd >/dev/null\nsudo chmod 440 /etc/sudoers.d/<user>-nopasswd\nsudo visudo -cf /etc/sudoers.d/<user>-nopasswd   # validate - must print 'parsed OK'\n```\n\nThis creates a small rule file telling the Mac that `<user>`\n\ncan run `sudo`\n\nwithout a\npassword prompt:\n\n`/etc/sudoers.d/`\n\n.`sudo`\n\nentirely, so it must print `parsed OK`\n\n.After this, `sudo`\n\nruns with no prompt - test with `sudo -n true`\n\n, which succeeds\nsilently if passwordless sudo works.\n\nYou can reach the target by either a hostname or an IP. I recommend using the hostname: it stays the same, while the IP can change.\n\n**Hostname (recommended).** Run on the target:\n\n```\nscutil --get LocalHostName      # prints the hostname, e.g. MacBook-Pro\n```\n\nAdd `.local`\n\nto form the address: `<target-host>.local`\n\n. You can also read it from\nSystem Settings -> General -> Sharing, shown as `Local hostname`\n\n.\n\nGive the target a unique name.Each Mac needs a`.local`\n\nname that’s unique on your network. If two machines share a name, the address can point to the wrong Mac. Make sure the target’s name is unique - rename it if needed:\n\n``` php\nsudo scutil --set LocalHostName newmacbook   # -> newmacbook.local\n```\n\n**IP address (not recommended).** Run on the target:\n\n```\nipconfig getifaddr en0          # e.g. 192.168.1.80\n```\n\nNote that the IP can change after a reboot or after a certain amount of time.\n\nThroughout the rest of this guide, replace `<user>`\n\nwith the target account name and\n`<target-host>`\n\nwith the hostname from above, so the address is\n`<user>@<target-host>.local`\n\n. You could also instead use an IP in place of\n`<target-host>.local`\n\n.\n\nOn the source Mac, create an SSH key (skip if you already have one):\n\n```\nssh-keygen -t ed25519\n```\n\nInstall your public key on the target. This asks for the target account’s login password once:\n\n```\nssh-copy-id <user>@<target-host>.local\n```\n\nTest it - this should print the target username with no password prompt:\n\n```\nssh <user>@<target-host>.local whoami\n```\n\nBy default macOS sleeps after ~10 minutes idle, even when plugged in, which takes it off the network. To make it never sleep, run this on the target (or over SSH from the source):\n\n```\nsudo pmset -c sleep 0          # never system-sleep while plugged in (-c = on charger)\nsudo pmset -c disablesleep 1   # also prevents sleep with the lid closed (clamshell)\nsudo pmset -c displaysleep 0   # keep the display on too\n```\n\nVerify:\n\n```\npmset -g | grep -iE 'sleep'\n```\n\n`sleep 0`\n\n, `SleepDisabled 1`\n\n, and `displaysleep 0`\n\nin the output confirm it worked.\n\nIf the machine runs on battery sometimes, use `-a`\n\ninstead of `-c`\n\nto apply to all\npower sources (at the cost of battery drain).\n\nThe screen can still lock when the screen saver kicks in. Stop the screen saver from ever starting so it never locks on its own:\n\n```\ndefaults -currentHost write com.apple.screensaver idleTime 0\n```\n\nmacOS ships `pbcopy`\n\n(write clipboard) and `pbpaste`\n\n(read clipboard). Piped over SSH,\nthey can move the clipboard between machines - encrypted, peer-to-peer, no Apple ID or\nthird-party service.\n\n[ clip.sh](/claude-controls-mac/clip.sh) wraps this into one command with two subcommands, and adds image\nsupport on top of\n\n`pbcopy`\n\n/`pbpaste`\n\n(which are text-only). Install it as a script on your PATH\non the source Mac, and point it at the target host with `IC_BOX`\n\n(“ic” standing for\n“isolated claude”):\n\n```\ncurl -fsSL https://raw.githubusercontent.com/ykdojo/claude-controls-mac/main/clip.sh -o ~/.local/bin/clip\nchmod +x ~/.local/bin/clip\nexport IC_BOX=\"<user>@<target-host>.local\"   # add to ~/.zshrc\n```\n\nUsage:\n\n`clip send`\n\n`clip get`\n\nSend the install command over and run it. From the source Mac you can push it straight\nto the target’s clipboard, or run it remotely. The following command installs a\nspecific version, but you can also install `latest`\n\nor `stable`\n\nif you’d like:\n\n```\nssh <user>@<target-host>.local 'curl -fsSL https://claude.ai/install.sh | bash -s -- 2.1.201'\n```\n\nThe native installer may warn that `~/.local/bin`\n\nis not on PATH. Fix it on the target by\nadding it to `~/.zshenv`\n\n(not `~/.zshrc`\n\n) - `.zshenv`\n\nis read by *every* zsh, including\nnon-interactive ones:\n\n```\nssh <user>@<target-host>.local 'echo '\\''export PATH=\"$HOME/.local/bin:$PATH\"'\\'' >> ~/.zshenv'\n```\n\nThis optional step applies a set of opinionated defaults via\n[ setup-claude-env.sh](/claude-controls-mac/setup-claude-env.sh) - shell aliases, the DX\nplugin,\n\n`settings.json`\n\ntweaks, the GitHub CLI, and (opt-in) Playwright MCP and\nyt-dlp. Every item is toggleable; see the full list in\n`claude-env-components.md`\n\n**Interactively on the target** - shows a checklist of every item (core\npre-checked, opt-ins unchecked) so you can pick any combination. Download the\nscript onto the target and run it:\n\n```\nssh -t <user>@<target-host>.local \\\n  'curl -fsSL https://raw.githubusercontent.com/ykdojo/claude-controls-mac/main/setup-claude-env.sh -o setup-claude-env.sh && bash setup-claude-env.sh'\n```\n\n**Non-interactively** - the script goes into this mode if it detects a\nnon-interactive environment or you supply at least one flag. No prompt; installs\ncore only by default, or pick what you want with flags (`--yt-dlp`\n\n, `--playwright`\n\n,\n`--all`\n\n, `--core`\n\n):\n\n```\nssh <user>@<target-host>.local \\\n  'curl -fsSL https://raw.githubusercontent.com/ykdojo/claude-controls-mac/main/setup-claude-env.sh -o setup-claude-env.sh && bash setup-claude-env.sh --all'\n```\n\nThe script is idempotent (OK to re-run).\n\nBoth logins are interactive, so SSH in:\n\n```\nssh <user>@<target-host>.local\n```\n\nThen run `claude`\n\non the target - it drops into the login for your Anthropic\naccount. Follow the prompts (a browser/device-code flow you can finish from a browser\non your main Mac).\n\nGitHub - optional, but highly recommended so the agent can work with repos:\n\n```\ngh auth login\n```\n\nIf you haven’t installed the GitHub CLI from\n[the previous step](#9-set-up-an-opinionated-claude-code-friendly-environment-optional),\nmake sure to do so first.\n\nI personally recommend using a separate GitHub account, not your main one, so it doesn’t mess up your main account.\n\nThis lets an interactive `claude`\n\nsession on the target see (screenshots) and control\n(mouse/keyboard) its own desktop, driven over SSH.\n\nThis doesn’t work out of the box - SSH and macOS’s permission model get in the way, so the setup below exists to route around that.\n\n**Why it needs a workaround:** macOS gates screen capture and input behind Screen Recording\nand Accessibility permissions that are tied to the GUI login session, so an SSH\nprocess can’t reach the display. Fix: a LaunchAgent keeps a `tmux`\n\nserver alive *inside* the\nGUI session on a fixed socket; every `claude`\n\nsession created there lands on that\nserver and inherits the GUI session, so it can reach the display. You attach over SSH.\n\nRun [ setup-computer-use.sh](/claude-controls-mac/setup-computer-use.sh) on the target:\n\n```\nssh -t <user>@<target-host>.local \\\n  'curl -fsSL https://raw.githubusercontent.com/ykdojo/claude-controls-mac/main/setup-computer-use.sh -o setup-computer-use.sh && bash setup-computer-use.sh'\n```\n\nThis installs the LaunchAgent (persistent `tmux`\n\nserver with anchor session `cc`\n\n) and enables the\nbuilt-in `computer-use`\n\ntool in `~/.claude.json`\n\n. Requires tmux (`brew install tmux`\n\n) and a\nClaude Pro or Max plan. Re-runnable; `--uninstall`\n\nto remove.\n\nInstall [ ic.sh](/claude-controls-mac/ic.sh) (\n\n`ic`\n\n= “isolated claude”) on the source Mac:\n\n```\ncurl -fsSL https://raw.githubusercontent.com/ykdojo/claude-controls-mac/main/ic.sh -o ~/.local/bin/ic\nchmod +x ~/.local/bin/ic\necho 'export IC_BOX=\"<user>@<target-host>.local\"' >> ~/.zshrc   # or edit the default in the script\n```\n\nEach `ic`\n\nspawns its own `claude`\n\nsession on the box (run several at once) and attaches.\nFlags mirror `claude`\n\n, and it adds several more subcommands:\n\n```\nic               # new claude session\nic -c            # continue the most recent conversation (forwards to: claude -c)\nic -r            # resume picker (forwards to: claude -r)\nic --chrome      # with Claude in Chrome (forwards to: claude --chrome)\nic sh            # a plain shell on the box, no claude (alias: ic shell)\nic rc            # Remote Control: drive the box from your phone (claude remote-control)\nic history       # stored conversations: count, location, recent w/ previews (alias: hist)\nic ls            # list live sessions (state, age, what's running, conversation)\nic attach <id>   # attach a running session (alias: ic a)\nic kill <id>     # kill a session (alias: ic k); \"ic kill all\" kills all\nic -h            # help\n```\n\nAll `ic`\n\nsessions run with `--dangerously-skip-permissions`\n\n(and `ic rc`\n\nuses\n`--permission-mode bypassPermissions`\n\n).\n\n**Copying text out:** sessions run in tmux, and Terminal.app can’t receive the clipboard escape\nsequences (OSC52) that claude emits, so mouse-selecting a snippet won’t reliably reach your Mac\nclipboard. Quick workaround: **Cmd-A then Cmd-C** copies the whole visible screen.\n\nBecause every `ic`\n\nsession lives on the box’s tmux server at a fixed socket, a Claude Code\nsession on your source Mac can prompt one directly over SSH - useful for delegating work\nto the box and checking on it, agent to agent:\n\n```\n# find the session (ic ls prints the ids)\nic ls\n\n# type a prompt into it, then confirm with a second Enter - with longer prompts,\n# an Enter sent in the same burst as the text doesn't register as submit and the\n# prompt sits in the input box (short prompts usually submit fine)\nssh <user>@<target-host>.local \"tmux -S /tmp/cc-tmux.sock send-keys -t <session> 'Switch to main and pull; PR #4 is merged.' Enter\"\nsleep 2\nssh <user>@<target-host>.local \"tmux -S /tmp/cc-tmux.sock send-keys -t <session> Enter\"\n\n# read the reply (re-run / poll until the spinner is gone)\nssh <user>@<target-host>.local \"tmux -S /tmp/cc-tmux.sock capture-pane -t <session> -p\" | tail -30\n```\n\nScreen Recording and Accessibility can only be granted in the GUI, and a human has to do it at\nthe machine (in person or via Screen Sharing) - macOS blocks synthetic clicks on these prompts.\nOn first capture you’ll also **Allow** a *“bypass the window picker”* prompt (recurs ~monthly).\n\n**The grants go on tmux, not claude.** macOS attributes\ncapture/control to the\n\n`tmux`\n\nserver\n(claude runs as its child). So:`tmux`\n\n(`/usr/local/bin/tmux`\n\n, or `/opt/homebrew/bin/tmux`\n\non Apple Silicon) under\nboth Screen Recording and Accessibility - Screen Recording covers screenshots,\nAccessibility covers mouse/keyboard control.`tmux -S /tmp/cc-tmux.sock kill-server`\n\n(the LaunchAgent respawns the anchor within seconds).\nclaude sessions started after the restart pick up the new grant.To make the entries appear in System Settings in the first place, trigger a computer-use action\n(`ic`\n\n, then ask Claude to “take a screenshot”) - macOS adds `tmux`\n\nto the list (toggled off) so\nyou can switch it on.\n\n**Full Disk Access stops the “access data from other apps” prompts.** On newer macOS, tmux\ntriggers a separate *“tmux would like to access data from other apps”* prompt for each app\nwhose data anything inside it touches. Granting `tmux`\n\n**Full Disk Access** (same Privacy &\nSecurity pane) suppresses these prompts entirely: use the **+** button and add the tmux\nbinary (Cmd-Shift-G to type the path). Restart the tmux server afterward, as above.\n\nI personally like to run a VPN on the box so its traffic goes out separately from my local IP. I use Proton VPN - it has a free option and I’ve been using them for a long time - but of course there are other options.\n\nYou can just ask the box’s Claude to do it if you finished walking through\n[the previous step](#11-computer-use-over-ssh-optional): `ic`\n\nin and say “install Proton VPN”. It’ll\ndownload and install the app. The parts it can’t do alone:\n\n`clip send`\n\nfrom\n`clip send`\n\n, then paste\ninto the sign-in field on the box.This general flow works not just for a VPN app - it should work for pretty much any other application too.\n\nRemote Control lets you drive Claude Code from your phone through the Claude app. There are two ways to use it:\n\n`/remote-control`\n\n(or `/rc`\n\nfor short) inside an existing session and follow\nthe instructions - you can then drive that same session from your phone, going\nback and forth between the two.`claude remote-control`\n\n, which also lets you start brand new\nsessions from your phone, not just attach to one you already have open.If you went through [step 11](#11-computer-use-over-ssh-optional), `ic rc`\n\nstarts\nthat server for you in a way that the sessions you spawn from your phone have access\nto computer use too.\n\nComputer use from [step 11](#11-computer-use-over-ssh-optional) deliberately won’t\ncontrol a browser - browsers get a restricted tier where Claude can see what’s on\nscreen but can’t click or type in it. The\n[Claude in Chrome extension](https://chromewebstore.google.com/detail/claude/fcoeoabgfenejglbffodgkkbkcdhcgfn)\nfills that gap with proper browser control: navigating, clicking, filling forms,\nreading console logs and network requests. And unlike Playwright MCP, it drives\nyour regular Chrome profile, so the agent can use any logged-in state you set up\non the box.\n\nIt requires Chrome on the target and a direct Anthropic plan (Pro, Max, Team, or Enterprise).\n\nYou can ask the box’s Claude to install Chrome and open the extension’s Chrome Web Store page. The parts it can’t do alone (do these at the machine, in person or via Screen Sharing):\n\n`chrome://extensions`\n\n.`clip send`\n\nfrom Then enable it in Claude Code on the target: run `/chrome`\n\nin a session and select\n“Enabled by default” to have the browser tools in every session, or skip that and\nuse `claude --chrome`\n\nper session ([ ic --chrome](#use-it-from-your-mac) from your\nsource Mac). If the connection doesn’t work, try restarting Chrome once.\n\nTo make browser use efficient (element refs from the accessibility tree instead\nof coordinates, no unrequested screenshots), the environment setup from\n[step 9](#9-set-up-an-opinionated-claude-code-friendly-environment-optional)\nadds a “Claude for Chrome” guidance section to `~/.claude/CLAUDE.md`\n\non the\ntarget. If you skipped that step, re-run the script - it’s idempotent.\n\nIf your source Mac’s Chrome also has the extensionsigned into the same account, Claude Code can connect to either browser. It prompts you to pick when both are connected, but its local-machine detection can be wrong ([#74667]). To avoid that, you can temporarily uninstall the extension from the source Mac’s Chrome - or just quit Chrome on the source while the box is working.\n\nNote that sessions spawned from your phone via\n[step 13](#13-control-it-from-your-phone) don’t currently get the browser tools\n([#74671](https://github.com/anthropics/claude-code/issues/74671)) - the\nworkaround is to start the session in the terminal and attach with `/rc`\n\n.\n\nThis lets you see the target’s screen live from the source Mac - and take over its mouse and keyboard - using macOS’s built-in Screen Sharing.\n\nThis has to be enabled in the GUI at the machine - since macOS 12.1 it\n[can’t be enabled from the command line](https://support.apple.com/guide/remote-desktop/enable-remote-management-apd8b1c65bd/mac).\n\nOn the target: **System Settings -> General -> Sharing -> Screen Sharing** on. If\n**Remote Management** is on, the Screen Sharing toggle may be hidden - turn it off\nfirst.\n\nThen connect from the source Mac:\n\n```\nopen vnc://<user>@<target-host>.local\n```\n\nLog in with the target account’s login password and tick **Remember this password\nin my keychain**.\n\n`.local`\n\nnames only resolve on your LAN, so everything so far is local-network\nonly (except phone control from [step 13](#13-control-it-from-your-phone), which\nrelays through Anthropic). [Tailscale](https://tailscale.com) fixes that: it\nconnects your machines with peer-to-peer, end-to-end encrypted\n[WireGuard](https://www.wireguard.com) tunnels, so SSH, `ic`\n\n, `clip`\n\n, and Screen\nSharing work from any network with nothing exposed to the public internet. On\nyour home network it takes a direct LAN path, so local use stays as fast as\nbefore. And joining the network only gives a device reachability - SSH and\nScreen Sharing still require your key or password on top.\n\nInstall on the target (the Homebrew formula works headless over SSH; sign in at the URL the last command prints - a free account with a social login is fine):\n\n```\nssh <user>@<target-host>.local 'brew install tailscale'\nssh <user>@<target-host>.local 'sudo brew services start tailscale'\nssh <user>@<target-host>.local 'sudo tailscale up --operator=<user>'\n```\n\nInstall on the source, then open the Tailscale app and log in **with the same\naccount** - devices only see each other within one account’s network:\n\n```\nbrew install --cask tailscale-app\n```\n\nWith MagicDNS (on by default) the target is reachable by bare hostname from\nanywhere - the same address, minus `.local`\n\n. Switch `IC_BOX`\n\nin `~/.zshrc`\n\n,\nkeeping the original name for reference:\n\n```\nexport IC_BOX=\"<user>@<target-host>\"          # Tailscale - works remotely too\n# export IC_BOX=\"<user>@<target-host>.local\"  # original - local network only\n```\n\nScreen Sharing works the same way: `open vnc://<user>@<target-host>`\n\n.\n\nRecommended, in the [admin console](https://login.tailscale.com/admin):\n**device approval** (Settings -> Device management), so a compromised Tailscale\nlogin alone can’t add a device to your network, and **disable key expiry** on\nthe target (Machines -> **…**), so the box doesn’t drop off the network when\nits key expires after ~180 days.\n\nTo confirm remote access really works, put the source Mac on a different\nnetwork (e.g. a phone hotspot) and run `ic ls`\n\n.", "url": "https://wpnews.pro/news/setting-up-your-spare-mac-for-claude-code-to-control-a-step-by-step-guide", "canonical_source": "https://ykdojo.github.io/claude-controls-mac/", "published_at": "2026-07-18 16:12:08+00:00", "updated_at": "2026-07-18 16:21:10.761899+00:00", "lang": "en", "topics": ["ai-tools", "ai-agents", "ai-safety", "developer-tools"], "entities": ["Claude Code", "Anthropic", "Mac", "SSH", "Claude app"], "alternates": {"html": "https://wpnews.pro/news/setting-up-your-spare-mac-for-claude-code-to-control-a-step-by-step-guide", "markdown": "https://wpnews.pro/news/setting-up-your-spare-mac-for-claude-code-to-control-a-step-by-step-guide.md", "text": "https://wpnews.pro/news/setting-up-your-spare-mac-for-claude-code-to-control-a-step-by-step-guide.txt", "jsonld": "https://wpnews.pro/news/setting-up-your-spare-mac-for-claude-code-to-control-a-step-by-step-guide.jsonld"}}