Macbox A guide for Kait · November 2026
Contents
  1. What this is
  2. The pieces
  3. How it fits together
  4. What you need and what it costs
  5. How the setup goes
  6. What you can do afterward
  7. Things to know before you start
  8. The message to paste to Claude
  9. Questions
  10. For your Claude: build brief

A guide from Austin · November 2026

Your own always-on Claude, living on a MacBook

A MacBook that stays plugged in can be your own small server. Give it a handful of pieces and you get a Claude that is always on, has its own browser, can use your passwords, publishes websites under your own domain, builds iPhone and Mac apps, and answers you from the Claude app on your phone. This page explains the idea in plain words, then hands your Claude a build brief to carry it out.

What this is

Right now Claude lives inside a chat window. It can think, but it has nowhere to work: no computer of its own, no browser that stays logged in, no way to reach your accounts, nowhere to put a website. The idea here is to give it one. The computer is your MacBook. On it run a few small programs, each in its own sealed box (a Docker container), and the Mac itself does the Apple-only work.

I run the same shape at home. Nothing here is a product you have to buy from anyone; every piece is free or something you would pay for anyway (a domain, a Claude subscription, Apple's developer program if you ship apps). You do not need to be an engineer. Your Claude does the building; you do the parts only you can do, like signing in to accounts.

The one-line versionYour MacBook becomes the brain. Your phone, your laptop and your browser become windows into it.

The pieces

Six parts. Four of them are containers on the Mac, one is a service in the cloud that makes the Mac reachable, and one is the Mac itself.

PartWhat it doesWhat it is
Password managerHolds every login you own. You use it from the Bitwarden apps on your phone and laptop. Your Claude asks it for a password at the moment it needs one, so passwords never sit in files or chats.Vaultwarden, a free, self-hosted server that the Bitwarden apps connect to. Your Claude gets its own Bitwarden account; you share with it only the logins you choose.
BrowserA Chrome that stays open and logged in. You sign in to your accounts in it once (bank, email, shops, anything with two-factor codes). Your Claude then drives that same browser to read your statements, download documents, or fill forms.Chromium in a container, with a small web viewer so you can see it and click in it from any device. Your Claude talks to it through Chrome's own remote-control protocol.
Claude CodeThe brain. A Claude that runs on your machine instead of in a chat window: it can run programs, edit files, use the browser and the vault, and build things. It shows up in the Claude app on your phone as a "machine" you can talk to from anywhere.Anthropic's Claude Code in a container, signed in to your Claude Pro account, with its Remote Control listener running so the Claude app can reach it.
TunnelConnects your domain name to the Mac without opening anything on your home network. Your Claude's websites, the browser viewer and the vault each get a name like something.yourdomain.com, with a real certificate and a sign-in page in front of the private ones.Cloudflare Tunnel (free). The Mac dials out to Cloudflare; Cloudflare hands visitors in. Works behind any apartment or hotel internet.
WebsitesA place to publish. Your Claude can build a site and put it at a name under your domain in one step.A small web server container. Each folder is a site; each site is a hostname.
The Mac itselfEverything Apple: building and signing iPhone and Mac apps, running the iPhone simulator, sending builds to TestFlight. And it is the thing your Claude can control remotely, the way I control my own Mac mini from anywhere.Xcode, Apple's developer tools, and remote access (SSH plus Screen Sharing over a private network) so your Claude and you can reach the Mac when you are not in front of it.

Two helpers sit alongside: Tailscale (free) is a private network between your devices so you can reach the Mac from anywhere as if you were at home, and GitHub (free) is where the code and the websites live, so a copy survives the Mac.

How it fits together

The MacBook holds five containers and the Apple toolchain; the Claude app and your domain reach it through Anthropic and Cloudflare; your devices also reach it over Tailscale. Your MacBook (lid closed, plugged in, never sleeps) Password managerVaultwarden BrowserChromium, stays logged in Websitesone folder per site Claude Codethe brain · Remote Control listener · uses the vault and the browser Tunnelcloudflared, dials out macOS itself Xcode · iPhone simulator · codesigning · TestFlight and notarization SSH and Screen Sharing on, reachable over Tailscale Claude Code reaches it by SSH to run builds and control the Mac Claude appphone or web via Anthropic yourdomain.comanyone, via Cloudflare Your devicesover Tailscale
Three ways in. The Claude app reaches Claude Code through Anthropic. The public reaches your websites through your domain and Cloudflare. You reach the Mac directly over Tailscale.

Day to day it feels like this. You open the Claude app on your phone and pick your machine. You say "log in to the electric company and tell me what I owe." Claude fetches the login from the vault, opens the site in the always-on browser, reads the page and answers. Or you say "make me a one-page site for the bake sale at bakesale.yourdomain.com," and a minute later it exists. Or "build the app and send it to TestFlight," and the Mac does it while you are out.

What you need and what it costs

ItemCostNotes
A MacBookyou have itApple silicon, or an Intel model from 2019 or later with four cores. 16 GB of memory is the floor; 24 GB or more is comfortable with Xcode open. It stays plugged in and lid-closed, somewhere out of the way. An Intel Mac tops out at macOS Sequoia, which still runs the current Xcode; see Phase 9.
Claude Proyour planClaude Code and Remote Control are included. Heavier daily use may push you to Max later; start with Pro.
A domainabout $10 a yearBuy it at Cloudflare Registrar so it is already on Cloudflare. Pick something you are happy to say out loud.
CloudflarefreeThe tunnel, DNS, certificates and the sign-in page in front of private things are all on the free plan.
TailscalefreePrivate network between your devices. Install the app on your phone and the Mac.
GitHubfreeUnlimited private repositories. Where everything your Claude builds is saved with history.
Docker Desktopfree for personal useRuns the containers on the Mac.
Apple Developer Program$99 a year, only if you ship appsNeeded to put apps on a real iPhone, on TestFlight, or in the store. The simulator works without it.
Backblaze (optional)about $9 a monthBacks up the whole Mac. Time Machine to a small external drive also works.

Ongoing: the domain and whatever you already pay for Claude. Everything else is free unless you choose the backup service or the developer program.

How the setup goes

This takes a few sessions, not an afternoon. The order matters: each step makes the next one possible.

  1. Sit at the MacBook for the first partThe first twenty minutes happen at the Mac. After that you can move to your phone.
  2. Start in a Claude chat on claude.aiPaste the address of this page and the message in section 8. Claude in the chat will read the brief and walk you through installing Claude Code on the Mac, one step at a time, in words.
  3. Open Terminal and install Claude CodeClaude in the chat tells you exactly what to paste. Terminal is the app where you paste commands; it looks plain and that is normal.
  4. Start Claude Code and point it at this page againOnce Claude Code is running on the Mac, you paste the same message to it. From here on, that Claude does the work on the machine; the chat window was just the bootstrap.
  5. Do the signups in one batchClaude will ask you, all at once, for the few things only you can do: buy the domain on Cloudflare, make a Tailscale account, make a GitHub account, and decide whether you want the Apple developer program now. About twenty minutes of clicking.
  6. Let it buildClaude sets the Mac to never sleep, installs Docker, writes the container setup, connects the tunnel, wires your domain, and checks its own work. It will tell you when it needs a click from you, for example an "allow" dialog on the Mac.
  7. Sign in on your phoneThe one step nobody can do for you: your Claude inside the box signs in to your Claude account, and you approve it on your phone. After that it stays signed in.
  8. Move your logins into the vaultInstall the Bitwarden app on your phone and laptop, point it at your own vault, and add your logins over time. Share with your Claude only the ones you want it to use.
  9. Sign in to the always-on browserOpen the browser viewer, log in to the sites you want Claude to be able to use, and do the two-factor step once. Those logins stick.
  10. Apple pipeline, when you want itClaude installs Xcode and, if you have joined the developer program, sets up signing and TestFlight. Your first app build goes out from the Mac while you watch from your phone.

What you can do afterward

Things to know before you start

The message to paste to Claude

Copy this exactly. Paste it first into a Claude chat at claude.ai, and later into Claude Code on the Mac when the chat tells you it is installed. If a session ends partway, start a new one and paste it again; the brief tells Claude to look at what is already done and continue.

Paste this
Read https://macbox.austinrenfroe.com/ all the way to the end. The second half,
"For your Claude: build brief", is written for you. Follow it from Phase 0. Work out
where you are running first. Bother me as little as possible: batch your questions,
run your own checks instead of asking me to verify, and explain things to me in plain
words. Never send a message, spend money or delete anything without asking me first.

Questions

Reply to the email that brought you here. Austin has told his Claude it may answer your high-level questions about this setup by email, with him copied, and it will loop him in for anything beyond that.

Tear here. Everything below is for your Claude.

Technician's section

For your Claude: build brief

If you are Claude reading this because a person pointed you at it: everything from here down is your operating brief. The person above it is your user. Read all of it before acting. The goal is one always-on MacBook running five containers (vault, browser, Claude Code agent, static web server, Cloudflare tunnel) plus the Mac's own Apple toolchain and remote access, built so the user never has to edit a file.

Operating principles

  1. Minimize interruptions. The user is not your QA. Batch every question. Phase 1 collects every account and token at once; after that you should not need them until the vault and browser sign-ins, and the Apple step.
  2. APIs and commands over clicks. Cloudflare's API, Tailscale auth keys, the GitHub CLI, pmset, systemsetup and defaults write instead of "open System Settings and click…". Where macOS forces a dialog (Docker's privileged helper, Tailscale's system extension, Screen Sharing permission), tell the user exactly what the dialog says and which button to press.
  3. Verify yourself. After every phase, run a probe (a curl, a docker compose ps, an SSH round trip) and report the result. Never ask "can you check whether that worked?"
  4. Never destructive without an explicit yes. No rm -rf of anything you did not create this session, no overwriting an existing config without a backup copy, no force-push, no deleting Cloudflare records you did not make.
  5. Never send, never spend. You do not send email, texts or messages, buy anything, or enter payment details. You tell the user what to buy and wait.
  6. Secrets never go in chat, files you commit, or command lines where avoidable. Tokens go in ~/macbox/.env (mode 600) or the vault. Never print a token back.
  7. Plain language on errors. Try two or three fixes yourself first. When you escalate, say what you tried and what you need in words, not stack traces.
  8. Track state. Keep a checklist in ~/macbox/STATE.md as you go, one line per phase with done/pending and anything the next session must know. Sessions end; the file is how the next one resumes.
  9. You are the engineer. Make routine choices yourself and name them afterward. Ask only when a choice turns on the user's taste, money or accounts.

Resuming

Before doing anything, find out where you are and what exists. Run the probe that fits, then say in one short message which phases are done and where you are picking up. Do not redo a finished phase; it would mean duplicate signups and duplicate records.

cat ~/macbox/STATE.md 2>/dev/null || echo "no state file: fresh start"
sw_vers 2>/dev/null && echo "running on the Mac" || echo "not on the Mac"
which brew docker tailscale gh claude xcodebuild 2>/dev/null
docker compose -f ~/macbox/compose.yaml ps 2>/dev/null
pmset -g | grep -E "^ +(sleep|disablesleep)"

Phases

Phase 0: where are you running?

Three possibilities. Decide from your own tools, not by asking.

  • You are Claude in a chat window (claude.ai), with no shell. Your whole job is to get Claude Code installed on the Mac, then hand off. Walk the user through, one step per message, in words a non-engineer can follow:
    1. Sit at the MacBook. Open Terminal (press Command and Space, type Terminal, press Return).
    2. Paste this and press Return: curl -fsSL https://claude.ai/install.sh | bash. If that fails, the current install instructions are at docs.claude.com/en/docs/claude-code/quickstart.
    3. Close and reopen Terminal, type claude, press Return, and sign in when it asks (it opens a browser; sign in with the same Claude account as the app).
    4. Paste the message from section 8 into Claude Code. Tell the user the chat window's job is now done and the Claude on the Mac takes over.
    Then stop. Do not try to carry out the later phases from a chat window.
  • You are Claude Code on the Mac. Carry on to Phase 1.
  • You are Claude Code inside the box's own container (hostname is the container's, /work exists, no sw_vers). The build is done and you are the result. Read ~/macbox/STATE.md through the Mac over SSH if you need the history; otherwise you are in normal operation and this brief no longer applies.

Phase 1: collect everything in one batch

Give the user a single checklist, in this order, and wait for all of it. Explain each in one sentence. Store what comes back in ~/macbox/.env, created with mode 600.

  1. A domain on Cloudflare. Make a free Cloudflare account, then Domain Registration, Register Domains, buy one. Registering at Cloudflare means it is on Cloudflare already, nothing to point anywhere. Ask them for the domain name. Call it DOMAIN.
  2. A Cloudflare API token. Profile, API Tokens, Create Token, Create Custom Token, with permissions Zone: Zone: Read, Zone: DNS: Edit, Zone: Access: Apps and Policies: Edit, Account: Cloudflare Tunnel: Edit, Account: Access: Organizations, Identity Providers, and Groups: Read. Zone Resources: the one domain. Also note the Account ID from the domain's overview page. Store as CF_API_TOKEN and CF_ACCOUNT_ID.
  3. Tailscale. Free account at tailscale.com, sign in with Google or Apple. Install the Tailscale app on the phone and sign in. In the admin console, DNS page: keep MagicDNS on and click Enable HTTPS. You will join the Mac yourself in Phase 2 through the Mac app; no key needed.
  4. GitHub. Free account. You will sign the Mac in with gh auth login in Phase 2; no token needed now.
  5. Apple Developer Program, decision only. $99 a year. Needed for TestFlight, real devices and the App Store; not needed for the simulator. Ask yes or no, and if yes, ask them to enroll at developer.apple.com with their Apple ID (it can take a day to approve). Do not block on it; Phase 9 handles it.
  6. Two emails. The address they use for Claude, and the address they want the private sign-in page (Cloudflare Access) to send codes to. Usually the same.
  7. The Mac's login password, spoken once when you need sudo. Never store it.

Phase 2: prepare the Mac

All of this runs in Terminal on the Mac. Use sudo -v once and a keepalive for the batch.

  1. Never sleep, lid closed. sudo pmset -a sleep 0 disksleep 0 displaysleep 10 womp 1 autorestart 1 and sudo pmset -a disablesleep 1. The second line is what keeps a MacBook awake with the lid closed and no external display. Check with pmset -g. Stand the Mac on its edge or on a stand so it can breathe; an Intel MacBook running Docker and Xcode with the lid closed gets warm. If the battery is flagged for service, tell the user to look once for a swollen battery (a trackpad that no longer clicks evenly, a bulging bottom case) and to replace it if so; a tired battery that still charges is fine for a machine that stays plugged in.
  2. Come back after power loss. autorestart 1 above. Then the FileVault choice: if FileVault is on (fdesetup status), the Mac stops at the password screen after any reboot and the box stays down until someone types the password at the machine. Tell the user this plainly and recommend keeping FileVault on if the Mac is somewhere other people can reach, off if it lives in their home and they want hands-off restarts. Turn on automatic login for their account (sudo sysadminctl -autologin set -userName "$USER" -password - prompts; or System Settings, Users and Groups, Automatic login) only if FileVault is off.
  3. Remote access on. sudo systemsetup -setremotelogin on (SSH). Screen Sharing: sudo /System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart -activate -configure -access -on -privs -all -users "$USER" -restart -agent; if macOS refuses, tell the user to switch on Screen Sharing in System Settings, General, Sharing.
  4. Homebrew. /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)", then add eval "$(/opt/homebrew/bin/brew shellenv)" to ~/.zprofile.
  5. Tools. brew install gh git jq node; brew install --cask docker tailscale. Open Docker.app once (it asks for the privileged helper: the user clicks Allow and types their password); in Docker Desktop settings turn on Start Docker Desktop when you sign in. Open Tailscale.app, the user signs in; then tailscale status shows the Mac. In the Tailscale admin console, disable key expiry on the Mac.
  6. GitHub. gh auth login (GitHub.com, HTTPS, log in with a web browser). The user pastes the one-time code in the browser.
  7. Folders. mkdir -p ~/macbox/{sites,work,data}. ~/macbox is the box: compose file, env, data volumes, and work/ is where the agent's projects live.
  8. Probe. docker run --rm hello-world, tailscale ip -4, gh auth status, ssh localhost true after adding your own key. Record in STATE.md.

Phase 3: the containers

Write ~/macbox/compose.yaml with five services on one internal network. Match images to the Mac: on Apple silicon they are arm64, so use Debian's chromium package for the browser (Google Chrome has no Linux arm64 build); on an Intel Mac they are amd64 and either Chromium or Google Chrome works. In Docker Desktop, give the Docker VM 4 CPUs and 6 GB (Settings, Resources); on a 16 GB Mac leave the rest for macOS and Xcode. Pin image tags. Keep every volume under ~/macbox/data so backup is one folder.

services:
  vault:
    image: vaultwarden/server:1.34.1
    restart: unless-stopped
    environment:
      DOMAIN: https://vault.${DOMAIN}
      SIGNUPS_ALLOWED: "true"      # flip to "false" after the two accounts exist
      ADMIN_TOKEN: ${VAULT_ADMIN_TOKEN}
    volumes: [./data/vault:/data]

  browser:
    build: ./browser
    restart: unless-stopped
    shm_size: 1g
    environment: {VNC_PASSWORD_FILE: /run/secrets/vnc_pw}
    volumes: [./data/chrome-profile:/profile]
    secrets: [vnc_pw]

  agent:
    build: ./agent
    restart: unless-stopped
    environment:
      CLAUDE_CONFIG_DIR: /home/owner/.claude
      BW_SERVER: https://vault.${DOMAIN}
      CDP_URL: http://127.0.0.1:9222   # the agent's own socat forwarder to browser:9222
    volumes:
      - ./data/claude:/home/owner/.claude     # the sign-in survives rebuilds
      - ./data/bw:/home/owner/.config/bw
      - ./work:/work
      - ./sites:/sites
      - ./data/agent-ssh:/home/owner/.ssh
    extra_hosts: ["mac:host-gateway"]          # the Mac itself, for SSH

  sites:
    image: nginx:1.27-alpine
    restart: unless-stopped
    volumes:
      - ./sites:/usr/share/nginx/sites:ro
      - ./sites.conf:/etc/nginx/conf.d/default.conf:ro

  cloudflared:
    image: cloudflare/cloudflared:2025.8.0
    restart: unless-stopped
    command: tunnel --no-autoupdate run --token ${CF_TUNNEL_TOKEN}

secrets:
  vnc_pw: {file: ./data/vnc_pw}

browser/Dockerfile: debian:bookworm-slim with chromium xvfb openbox x11vnc novnc websockify fonts-liberation fonts-noto-color-emoji tini. The start script runs Xvfb on :1 (1440x900), openbox, then Chromium not headless (so the viewer shows a real window and logins with two-factor prompts work) with --user-data-dir=/profile --remote-debugging-port=9223 --no-first-run --password-store=basic, then x11vnc -display :1 -forever -shared -rfbport 5900 -passwdfile /run/secrets/vnc_pw -localhost and websockify --web /usr/share/novnc 6080 localhost:5900. Two traps worth knowing. A headed Chromium only listens for remote debugging on its own loopback, so the container also runs socat TCP-LISTEN:9222,bind=0.0.0.0,fork,reuseaddr TCP:127.0.0.1:9223 with Chromium on 9223; and Chromium refuses debugging requests whose Host header is a name like browser, so the agent runs its own forwarder (socat TCP-LISTEN:9222,bind=127.0.0.1,fork TCP:browser:9222) and talks to http://127.0.0.1:9222. Chromium's sandbox needs user namespaces inside the container: run as a non-root user with a seccomp profile that allows them, and fall back to --no-sandbox only if it will not start, saying so in STATE.md. Nothing publishes 9222 to the Mac. Port 6080 is the viewer, published only to the tunnel.

agent/Dockerfile: debian:bookworm-slim, a non-root user owner (uid 1000), git python3 jq curl openssh-client ca-certificates, Node LTS, then npm install -g @anthropic-ai/claude-code @bitwarden/cli chrome-devtools-mcp. Entrypoint: a small supervisor script that (1) if ~/.config/bw/master.pw exists, runs bw unlock --passwordfile ~/.config/bw/master.pw --raw into ~/.config/bw/session and starts bw serve --hostname 127.0.0.1 --port 8087 in the background; (1b) starts the CDP forwarder socat TCP-LISTEN:9222,bind=127.0.0.1,fork TCP:browser:9222; (2) for every git repository directly under /work, keeps one claude remote-control --name <folder> --spawn same-dir --permission-mode auto running from inside that folder, restarting it if it exits, and re-scans /work every two minutes for new folders. A sleep infinity at the end keeps the container up even before sign-in.

sites.conf: one nginx server block with server_name ~^(?<site>[a-z0-9-]+)\. and root /usr/share/nginx/sites/$site; so a folder named bakesale answers at bakesale.DOMAIN, plus a 404.html fallback. Put a one-page placeholder site in sites/www so the apex has something to show.

Then docker compose build and docker compose up -d. Probe: docker compose ps shows five services up; docker compose exec agent curl -s http://127.0.0.1:9222/json/version returns Chromium's version; docker compose exec agent claude --version prints a version.

Phase 4: the tunnel and the domain

  1. Create a remotely-managed tunnel with the API (POST /accounts/{CF_ACCOUNT_ID}/cfd_tunnel, name macbox, config_src: cloudflare). Save the returned token as CF_TUNNEL_TOKEN in .env and docker compose up -d cloudflared.
  2. Set the tunnel's ingress (PUT .../cfd_tunnel/{id}/configurations): vault.DOMAIN to http://vault:80; browser.DOMAIN to http://browser:6080; DOMAIN and *.DOMAIN to http://sites:80; catch-all 404.
  3. DNS: CNAME records for @, *, vault and browser pointing at {tunnel-id}.cfargotunnel.com, proxied. The wildcard is what lets a new site exist with no DNS step.
  4. Cloudflare Access in front of the private hostnames: in Zero Trust, add the One-time PIN login method; create a self-hosted application for browser.DOMAIN with a policy allowing only the user's email. Do the same for the vault's admin page only (vault.DOMAIN/admin); the vault itself stays reachable by the Bitwarden apps, which cannot pass an Access login. Vaultwarden is built to face the internet: the user's account gets a long master password and two-factor in Phase 6, and the tunnel hides the home IP.
  5. Probe from outside the Mac's network if you can (a phone off wifi) or at least through Cloudflare: curl -sI https://DOMAIN is 200, curl -sI https://browser.DOMAIN is a 302 to the Access login, curl -s https://vault.DOMAIN/alive returns a timestamp.

Phase 5: sign Claude in and make it appear in the app

  1. docker compose exec -it agent claude. It prints a sign-in URL. Tell the user to open it on their phone and approve. The credentials land in data/claude because of CLAUDE_CONFIG_DIR, so they survive rebuilds.
  2. Make a first project: mkdir /work/notes && cd /work/notes && git init inside the agent, and a CLAUDE.md at /work/CLAUDE.md written for this user (see Phase 8). The supervisor picks the folder up and starts its Remote Control listener.
  3. Register the MCP servers in the agent's user scope: claude mcp add --scope user chrome -- chrome-devtools-mcp --browserUrl http://127.0.0.1:9222. Confirm with claude mcp list.
  4. Probe: the user opens the Claude app, Code tab, and sees a machine named notes. They send "what folder are you in?" and get /work/notes. Record it.

Phase 6: the vault

  1. Tell the user to open https://vault.DOMAIN on their laptop, create their account with a long master password they use nowhere else, then in Settings, Security, Two-step Login turn on an authenticator app. Then create a second account for Claude (for example claude@DOMAIN if they set up email, or any address they control) with its own master password.
  2. Make an Organization from the user's account, invite the Claude account, and explain the rule: anything in the organization's collection is visible to Claude; anything in the user's personal vault is not. The user moves logins into the collection one at a time as they decide to.
  3. Set SIGNUPS_ALLOWED: "false" and restart the vault.
  4. In the agent: bw config server https://vault.DOMAIN, then bw login as the Claude account (the user types that account's email and master password into the prompt; you do not see it). Then write the Claude account's master password to ~/.config/bw/master.pw (mode 600) so the supervisor can unlock on every start. State the tradeoff in one sentence: this file lets the box unlock Claude's vault unattended; it never holds the user's own master password.
  5. Restart the agent and probe: curl -s http://127.0.0.1:8087/status inside the agent says unlocked; a test item placed in the collection comes back from curl -s 'http://127.0.0.1:8087/list/object/items?search=test'.
  6. Phone and laptop: install the Bitwarden apps, tap the region selector, choose Self-hosted, enter https://vault.DOMAIN, sign in as the user.

Phase 7: the browser

  1. Set the viewer password: write a random one to data/vnc_pw and tell it to the user once (or store it in their vault). Restart the browser service.
  2. The user opens https://browser.DOMAIN, passes the Access email code, enters the viewer password, and sees Chromium. They sign in to whatever sites they want Claude to use and complete two-factor there. Cookies live on the chrome-profile volume, so they persist.
  3. Probe from the agent: ask Claude (in the app) to open https://example.com through the chrome MCP and report the title. Then ask it to open a site the user signed in to and read something harmless (the account's display name).
  4. Write the house rules into /work/CLAUDE.md: Claude reads and navigates freely; it never submits a payment, changes a password, sends a message or deletes anything through the browser without the user saying so in that conversation.

Phase 8: the Mac from inside the box, and from anywhere

  1. In the agent, ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N ""; append the public key to the Mac user's ~/.ssh/authorized_keys. The compose file maps mac to the host, so ssh <macuser>@mac sw_vers from the agent is the probe. Add an ~/.ssh/config entry so it is just ssh mac.
  2. This is how the box controls the Mac: ssh mac xcodebuild …, ssh mac osascript -e '…' for application scripting, ssh mac screencapture -x /tmp/shot.png then scp it back to look at the screen, ssh mac open -a Safari https://…. Write a short section in /work/CLAUDE.md listing these so future sessions know the Mac is reachable and how.
  3. For the user: on the laptop or iPad, with Tailscale signed in, Finder's Go, Connect to Server, vnc://<mac-tailscale-name> opens Screen Sharing from anywhere. On the phone, any VNC client does the same. Tell them the name.
  4. Install Claude Code natively on the Mac too (it already is, from Phase 0). A claude remote-control --name mac --spawn same-dir --permission-mode auto running in ~/macbox/work-mac as a launchd user agent (~/Library/LaunchAgents/com.macbox.claude-rc.plist, KeepAlive) gives the user a second machine in the Claude app, "mac", that runs on macOS itself with Xcode in reach. The container is the everyday box; the Mac listener is for Apple work and for fixing the box if the containers are down.
  5. /work/CLAUDE.md for this user, written in their words: who they are, what the box is, the never-send-never-spend rule, how to fetch a login (bw-serve on 127.0.0.1:8087), how to use the browser (chrome MCP), how to reach the Mac (ssh mac), how to publish a site (a folder under /sites is live at <folder>.DOMAIN at once), and to commit work to GitHub with gh repo create --private for each new project.

Phase 9: the Apple pipeline

  1. Xcode. First check the macOS version: sw_vers. Xcode 26 needs macOS 15.6 or later; an Intel Mac that cannot go past Sequoia (15) still runs it, so update macOS fully before installing. Newer Xcode releases will require macOS Tahoe, which Intel Macs cannot run; tell the user plainly that an Intel Mac ships apps on the current SDK for roughly another year and a half before Apple's submission rules outgrow it. Then xcodes via Homebrew or the App Store; then sudo xcode-select -s /Applications/Xcode.app, sudo xcodebuild -license accept, xcodebuild -runFirstLaunch, and install the iOS platform (xcodebuild -downloadPlatform iOS). This is several gigabytes; run it in the background and keep going. Probe: xcrun simctl list devices available | head shows iPhones, and a one-file Swift app builds for the simulator.
  2. If the user joined the Developer Program. In App Store Connect, Users and Access, Integrations, make a Team API key (role App Manager); the user downloads the .p8 once and gives you its path, key ID and issuer ID. Store the key under ~/macbox/data/asc/ (mode 600) and the IDs in .env. Create a distribution certificate and the app's bundle ID from the Mac with xcodebuild -allowProvisioningUpdates and the API key, so signing is automatic (-authenticationKeyPath … -authenticationKeyID … -authenticationKeyIssuerID …).
  3. A ship script at ~/macbox/bin/ship-ios <project-dir>: bump the build number, xcodebuild archive, xcodebuild -exportArchive with method app-store-connect, destination upload and uploadSymbols, which uploads straight to App Store Connect; TestFlight processes it from there (no notarization step for iOS). A sibling ship-mac does archive, -exportArchive with Developer ID, xcrun notarytool submit --wait, xcrun stapler staple. Both scripts print one line per phase and exit non-zero on failure.
  4. Testers. Add the user's own Apple ID as an internal tester; the TestFlight app on their phone gets the build. Probe: a hello-world app appears in TestFlight on the user's phone.
  5. The agent container can trigger all of this: ssh mac ~/macbox/bin/ship-ios /Users/<user>/macbox/work-mac/<app>. Keep iOS projects in ~/macbox/work-mac on the Mac (Xcode needs the real filesystem), not in the container's /work.

Phase 10: keep it alive

  1. Restart policy is already unless-stopped, and Docker Desktop starts at login, which with automatic login means a reboot brings everything back.
  2. A watchdog as a launchd user agent every five minutes: docker compose ps must show five services running, curl -s http://localhost:6080 must answer, and the Mac must have been awake continuously (pmset -g log | grep -c Sleep not increasing). On failure, docker compose up -d and write to ~/macbox/data/watchdog.log.
  3. Tell the user when it breaks. A free healthchecks.io check that the watchdog pings on every green run; healthchecks emails the user if the pings stop. This is the only external alerting and it is one curl.
  4. Backups. ~/macbox/data holds the vault, the browser profile, Claude's sign-in and the Bitwarden session: back it up. Time Machine covers it if a drive is attached; otherwise recommend Backblaze and leave the choice to the user. Everything under work/ and sites/ should also be in private GitHub repositories; make the first commit and push for each.
  5. Updates. Once a month: docker compose pull && docker compose build --pull && docker compose up -d, and claude update on the Mac. Put a reminder in STATE.md; do not automate it unattended.

Final verification

Run every line and show the user the result as a plain checklist, pass or fail. Fix every fail before calling it done.

  1. The Mac has disablesleep 1 and has been up for more than an hour with the lid closed.
  2. docker compose ps: five services, all running, all with a restart policy.
  3. https://DOMAIN serves the placeholder site with a valid certificate; a new folder sites/test is live at https://test.DOMAIN within a minute with no other step.
  4. https://browser.DOMAIN asks for an email code, then a viewer password, then shows Chromium with the user's logins intact after a docker compose restart browser.
  5. The Claude app shows machines named notes and mac; each answers a message.
  6. From the app, Claude fetches a test login from the vault without the user typing anything, and reads a page in the browser that required that login.
  7. From the app, Claude runs sw_vers on the Mac through ssh mac and takes a screenshot of the Mac's screen.
  8. Screen Sharing to the Mac works from the user's phone or laptop over Tailscale with wifi off.
  9. If the Developer Program is active: a hello-world build reached TestFlight on the user's phone, started from the app.
  10. The healthchecks.io check is green; stopping the watchdog for ten minutes makes it email the user; restarting makes it green again.
  11. ~/macbox/STATE.md says every phase is done, and lists the two or three things the user must do by hand after a power cut.

When you are stuck for good: write what you tried to ~/macbox/STATE.md under a heading "Stuck", tell the user in plain words what the blocker is, and ask them to start a fresh Claude Code session with the same paste. The next session reads STATE.md first.