claude-controls-mac

claude-controls-mac

How to set up your spare Mac for Claude Code to fully control - a step-by-step guide

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.

In case you’re reading this on GitHub Pages, here’s the repo version.

the repo version

Why do this?

I 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.

Claude Code, especially with the --dangerously-skip-permissions flag on,

carries inherent risk when run on your main machine. You can eliminate / mitigate these

risks by creating a separate environment on your spare Mac with everything it needs

to have access to.

It 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.

The 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.

Why this setup?

First, let’s quickly address a few questions you might have.

Why not run it in a container?

I’m a big proponent of running it in a container - I even built

an entire environment for doing so conveniently.

However, I’ve found it has a few limitations.

First, it still runs on your main machine, so it’s not completely separated. For

example, network requests it sends still go through your main machine.

an entire environment for doing so conveniently

Second, 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.

Why not use something like OpenClaw?

I 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.

At 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:

Use an old/spare Mac, not your main one.

Create a fresh local account with no personal data and no Apple ID signed in, so

the agent has nothing sensitive to reach.

Drive it over SSH from your main Mac on your local network, and control it from

your phone.

What you’ll need

A spare Mac (the target).

Your everyday Mac (the source), on the same Wi-Fi.

1. Start fresh on the target Mac

Wipe it first (if it has any personal data)

You’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:

Macs that support it: System Settings -> General -> Transfer or Reset ->

Erase All Content and Settings.

Older Intel Macs: restart into Recovery (hold Cmd-R at

boot), use Disk Utility to erase the internal drive, then reinstall macOS.

Optionally update to the latest macOS afterward (System Settings -> General ->

Software Update).

Create a fresh, isolated account

Create a new local user account (System Settings -> Users & Groups).

I recommend not signing into an Apple ID. Skip it during setup.

Make the account an admin (if you haven’t already)

The account needs admin rights or sudo will refuse to run.

System Settings -> Users & Groups -> set the account to Allow this user to

administer this computer.

If you ever need to repair it from another admin account:

sudo dseditgroup -o edit -a <user> -t user admin

2. Enable Remote Login (SSH) on the target Mac

On the target, turn on SSH so the source Mac can connect:

If the command fails with Turning Remote Login on or off requires Full Disk Access

privileges, give your terminal app Full Disk Access first:

System Settings -> Privacy & Security -> Full Disk Access.

Click +, then in the file picker go to Applications -> Utilities -> Terminal

and add it.

Quit and reopen the terminal, then rerun the command.

3. Passwordless sudo for the target account

This 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:

This creates a small rule file telling the Mac that <user> can run sudo without a

password prompt:

line 1 writes the rule into /etc/sudoers.d/.

line 2 makes it read-only - sudo ignores the file otherwise.

line 3 validates the syntax; a typo in a sudoers file can lock you out of sudo

entirely, so it must print parsed OK.

After this, sudo runs with no prompt - test with sudo -n true, which succeeds

silently if passwordless sudo works.

4. Find the target’s address (hostname or IP)

You 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.

Hostname (recommended). Run on the target:

Add .local to form the address: <target-host>.local. You can also read it from

System Settings -> General -> Sharing, shown as Local hostname.

Give the target a unique name. Each Mac needs a .local name 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:

IP address (not recommended). Run on the target:

Note that the IP can change after a reboot or after a certain amount of time.

Throughout the rest of this guide, replace <user> with the target account name and

<target-host> with the hostname from above, so the address is

<user>@<target-host>.local. You could also instead use an IP in place of

<target-host>.local.

5. Set up passwordless SSH from the source Mac

On the source Mac, create an SSH key (skip if you already have one):

Install your public key on the target. This asks for the target account’s login

password once:

Test it - this should print the target username with no password prompt:

6. Keep the target awake

By 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):

Verify:

sleep 0, SleepDisabled 1, and displaysleep 0 in the output confirm it worked.

If the machine runs on battery sometimes, use -a instead of -c to apply to all

power sources (at the cost of battery drain).

The screen can still lock when the screen saver kicks in. Stop the screen saver

from ever starting so it never locks on its own:

7. Clipboard sync over SSH

macOS ships pbcopy (write clipboard) and pbpaste (read clipboard). Piped over SSH,

they can move the clipboard between machines - encrypted, peer-to-peer, no Apple ID or

third-party service.

clip.sh wraps this into one command with two subcommands, and adds image

support on top of pbcopy/pbpaste (which are text-only). Install it as a script on your PATH

on the source Mac, and point it at the target host with IC_BOX (“ic” standing for

“isolated claude”):

clip.sh

Usage:

clip send - this Mac’s clipboard → the target (text or image). For an image you can

paste it straight into a Claude Code session on the target with Ctrl-V.

clip get - the target’s clipboard → this Mac (text or image).

8. Install Claude Code on the target Mac

Send the install command over and run it. From the source Mac you can push it straight

to the target’s clipboard, or run it remotely. The following command installs a

specific version, but you can also install latest or stable if you’d like:

The native installer may warn that ~/.local/bin is not on PATH. Fix it on the target by

adding it to ~/.zshenv (not ~/.zshrc) - .zshenv is read by every zsh, including

non-interactive ones:

9. Set up an opinionated, Claude Code-friendly environment (optional)

This optional step applies a set of opinionated defaults via

setup-claude-env.sh - shell aliases, the DX

plugin, settings.json tweaks, the GitHub CLI, and (opt-in) Playwright MCP and

yt-dlp. Every item is toggleable; see the full list in

claude-env-components.md.

setup-claude-env.sh

claude-env-components.md

Interactively on the target - shows a checklist of every item (core

pre-checked, opt-ins unchecked) so you can pick any combination. Download the

script onto the target and run it:

Non-interactively - the script goes into this mode if it detects a

non-interactive environment or you supply at least one flag. No prompt; installs

core only by default, or pick what you want with flags (--yt-dlp, --playwright,

--all, --core):

The script is idempotent (OK to re-run).

10. Log in to Claude and GitHub

Both logins are interactive, so SSH in:

Then run claude on the target - it drops into the login for your Anthropic

account. Follow the prompts (a browser/device-code flow you can finish from a browser

on your main Mac).

GitHub - optional, but highly recommended so the agent can work with repos:

If you haven’t installed the GitHub CLI from

the previous step,

make sure to do so first.

the previous step

I personally recommend using a separate GitHub account, not your main one, so it

doesn’t mess up your main account.

11. Computer use over SSH (optional)

This lets an interactive claude session on the target see (screenshots) and control

(mouse/keyboard) its own desktop, driven over SSH.

This 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.

Why it needs a workaround: macOS gates screen capture and input behind Screen Recording

and Accessibility permissions that are tied to the GUI login session, so an SSH

process can’t reach the display. Fix: a LaunchAgent keeps a tmux server alive inside the

GUI session on a fixed socket; every claude session created there lands on that

server and inherits the GUI session, so it can reach the display. You attach over SSH.

Scriptable setup

Run setup-computer-use.sh on the target:

setup-computer-use.sh

This installs the LaunchAgent (persistent tmux server with anchor session cc) and enables the

built-in computer-use tool in ~/.claude.json. Requires tmux (brew install tmux) and a

Claude Pro or Max plan. Re-runnable; --uninstall to remove.

Use it from your Mac

Install ic.sh (ic = “isolated claude”) on the source Mac:

ic.sh

Each ic spawns its own claude session on the box (run several at once) and attaches.

Flags mirror claude, and it adds several more subcommands:

All ic sessions run with --dangerously-skip-permissions (and ic rc uses

--permission-mode bypassPermissions).

Copying text out: sessions run in tmux, and Terminal.app can’t receive the clipboard escape

sequences (OSC52) that claude emits, so mouse-selecting a snippet won’t reliably reach your Mac

clipboard. Quick workaround: Cmd-A then Cmd-C copies the whole visible screen.

Bonus: let your Mac’s Claude drive the box’s Claude

Because every ic session lives on the box’s tmux server at a fixed socket, a Claude Code

session on your source Mac can prompt one directly over SSH - useful for delegating work

to the box and checking on it, agent to agent:

One-time grants (can’t be scripted)

Screen Recording and Accessibility can only be granted in the GUI, and a human has to do it at

the machine (in person or via Screen Sharing) - macOS blocks synthetic clicks on these prompts.

On first capture you’ll also Allow a “bypass the window picker” prompt (recurs ~monthly).

The grants go on tmux, not claude. macOS attributes

capture/control to the responsible process in the chain, which here is the tmux server

(claude runs as its child). So:

Grant tmux (/usr/local/bin/tmux, or /opt/homebrew/bin/tmux on Apple Silicon) under

both Screen Recording and Accessibility - Screen Recording covers screenshots,

Accessibility covers mouse/keyboard control.

Restart the tmux server after granting - a running process caches its permission state

at launch, so a grant won’t take effect until the server restarts:

tmux -S /tmp/cc-tmux.sock kill-server (the LaunchAgent respawns the anchor within seconds).

claude 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

(ic, then ask Claude to “take a screenshot”) - macOS adds tmux to the list (toggled off) so

you can switch it on.

Full Disk Access stops the “access data from other apps” prompts. On newer macOS, tmux

triggers a separate “tmux would like to access data from other apps” prompt for each app

whose data anything inside it touches. Granting tmux Full Disk Access (same Privacy &

Security pane) suppresses these prompts entirely: use the + button and add the tmux

binary (Cmd-Shift-G to type the path). Restart the tmux server afterward, as above.

12. Install a VPN, or any other app (optional)

I 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.

You can just ask the box’s Claude to do it if you finished walking through

the previous step: ic in and say “install Proton VPN”. It’ll

download and install the app. The parts it can’t do alone:

the previous step

Credentials. Signing in is required even for the free tier, and that’s

yours to enter. Send the password over securely with clip send from

step 7: copy it on your Mac, run clip send, then paste

into the sign-in field on the box.

step 7

macOS permission prompts. The first connect pops a system prompt to allow a VPN /

network configuration (and may ask for the Mac password) - approve it at the machine.

Computer use for the GUI. The app is GUI-only, so driving it relies on

step 11 being set up. Once signed in, the agent

can connect and switch servers itself.

step 11

This general flow works not just for a VPN app - it should work for pretty much any

other application too.

13. Control it from your phone

Remote Control lets you drive Claude Code from your phone through the Claude app.

There are two ways to use it:

Run /remote-control (or /rc for short) inside an existing session and follow

the instructions - you can then drive that same session from your phone, going

back and forth between the two.

Start a server with claude remote-control, which also lets you start brand new

sessions from your phone, not just attach to one you already have open.

If you went through step 11, ic rc starts

that server for you in a way that the sessions you spawn from your phone have access

to computer use too.

step 11

14. Set up Claude in Chrome (optional)

Computer use from step 11 deliberately won’t

control a browser - browsers get a restricted tier where Claude can see what’s on

screen but can’t click or type in it. The

Claude in Chrome extension

fills that gap with proper browser control: navigating, clicking, filling forms,

reading console logs and network requests. And unlike Playwright MCP, it drives

your regular Chrome profile, so the agent can use any logged-in state you set up

on the box.

step 11

Claude in Chrome extension

It requires Chrome on the target and a direct Anthropic plan (Pro, Max, Team, or

Enterprise).

You 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):

Click “Add to Chrome”. Optionally, you can pin the extension to the toolbar

by going to chrome://extensions.

Log into your Claude account in the extension. You can send the login info

with clip send from step 7.

step 7

Then enable it in Claude Code on the target: run /chrome in a session and select

“Enabled by default” to have the browser tools in every session, or skip that and

use claude --chrome per session (ic --chrome from your

source Mac). If the connection doesn’t work, try restarting Chrome once.

ic --chrome

To make browser use efficient (element refs from the accessibility tree instead

of coordinates, no unrequested screenshots), the environment setup from

step 9

adds a “Claude for Chrome” guidance section to ~/.claude/CLAUDE.md on the

target. If you skipped that step, re-run the script - it’s idempotent.

step 9

If your source Mac’s Chrome also has the extension signed 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.

#74667

Note that sessions spawned from your phone via

step 13 don’t currently get the browser tools

(#74671) - the

workaround is to start the session in the terminal and attach with /rc.

step 13

#74671

15. Enable Screen Sharing on the target Mac (optional)

This 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.

This has to be enabled in the GUI at the machine - since macOS 12.1 it

can’t be enabled from the command line.

can’t be enabled from the command line

On the target: System Settings -> General -> Sharing -> Screen Sharing on. If

Remote Management is on, the Screen Sharing toggle may be hidden - turn it off

first.

Then connect from the source Mac:

Log in with the target account’s login password and tick Remember this password

in my keychain.

16. Access it from anywhere with Tailscale (optional)

.local names only resolve on your LAN, so everything so far is local-network

only (except phone control from step 13, which

relays through Anthropic). Tailscale fixes that: it

connects your machines with peer-to-peer, end-to-end encrypted

WireGuard tunnels, so SSH, ic, clip, and Screen

Sharing work from any network with nothing exposed to the public internet. On

your home network it takes a direct LAN path, so local use stays as fast as

before. And joining the network only gives a device reachability - SSH and

Screen Sharing still require your key or password on top.

step 13

Tailscale

WireGuard

Install 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):

Install on the source, then open the Tailscale app and log in with the same

account - devices only see each other within one account’s network:

With MagicDNS (on by default) the target is reachable by bare hostname from

anywhere - the same address, minus .local. Switch IC_BOX in ~/.zshrc,

keeping the original name for reference:

Screen Sharing works the same way: open vnc://<user>@<target-host>.

Recommended, in the admin console:

device approval (Settings -> Device management), so a compromised Tailscale

login alone can’t add a device to your network, and disable key expiry on

the target (Machines -> …), so the box doesn’t drop off the network when

its key expires after ~180 days.

admin console

To confirm remote access really works, put the source Mac on a different

network (e.g. a phone hotspot) and run ic ls.

If you want more Claude Code and agentic coding tips, feel free to subscribe to

my newsletter, Agentic Coding with Discipline and Skill.

Agentic Coding with Discipline and Skill