Run Claude Code on Every Machine You Own
Install bernatod on each machine. Pair each one in 30 seconds. From a single dashboard, spawn Claude Code on whichever host has the right files, GPU, or context. Watch all of them. Approve from your phone. macOS spawns can be sandboxed with Seatbelt; Linux supports opt-in bwrap when available; Windows has limited Job Object containment without filesystem/network isolation.
If you have more than one Mac, you've felt this question: which machine is that running on? The Cursor session you started yesterday on the GPU rig. The long Aider refactor on the laptop. The browser-agent run on the Mac mini under the TV. The plumbing for "all of them in one place" doesn't exist out of the box, so you end up SSHing in turns or remembering tmux session names.
This is the multi-machine setup I run. The same setup powers the screenshots on the Bernato homepage.
What the native options give you (and where they stop)
Claude Code's Remote Control lets your phone re-attach to one live session per machine. Their Agent Teams feature spawns coordinated sub-agents within a single Claude Code instance. Both are useful. Neither answers "I have three machines and I want one screen."
Step 1 — Install the daemon on each box
From bernato.dev/download, grab the right binary. macOS install is a .app; the daemon auto-launches. Linux and Windows builds are preview/manual; Linux supports opt-in bwrap when available, and Windows has limited Job Object containment.
# macOS — drag Bernato.app into /Applications, double-click once
# Linux
curl -L https://bernato.dev/dl/bernatod-linux-amd64.tar.gz | tar xz
sudo mv bernatod /usr/local/bin/
bernatod install-systemd-unit
sudo systemctl enable --now bernatod
The daemon listens on 127.0.0.1:33120. It does nothing until you pair it.
Step 2 — Pair each machine in 30 seconds
Open the dashboard at app.bernato.dev and sign in with Google. Click the Bernato tray icon on the machine → Pair this machine. The browser jumps to https://app.bernato.dev/auth-bridge?port=33120#v=… with only a verifier in the fragment. The daemon keeps the one-time challenge local, proves it over loopback, and completes the handshake silently. The machine appears in your fleet view.
Repeat for each box.
Step 3 — Spawn Claude Code on the GPU rig from your laptop
From the dashboard, click Spawn agent. Pick the host (your GPU rig). Paste:
claude --dangerously-skip-permissions --resume my-training-run
or, more sensibly:
claude --resume my-training-run
Hit Run. The PTY opens in your browser via xterm.js + WebSocket. Multiple browser tabs can attach to the same agent — there's a 256KB replay ring for late joiners.
Step 4 — Watch from your phone
The companion iOS / Android app pairs with your Firebase account. Every running agent shows up. Tap one to attach. When the agent asks for a permission ("can I run git push?"), the approval appears in the app. Approve or deny in-app.
This is the part Remote Control can't do — the phone isn't viewing one session, it's the dashboard for the whole fleet.
Step 5 — Sandbox: keep Claude Code out of ~/Documents
On macOS, every spawn is wrapped in sandbox-exec with a Seatbelt profile derived from capability tokens: workspace_rw, network, subprocess. The agent can read and write the workspace folder you picked; broader reads require explicit grants. Linux supports opt-in bwrap when available; Windows has limited Job Object containment without filesystem/network isolation.
If a particular agent needs more (it wants to take screenshots, drive Safari, read protected files), grant it explicitly per-agent via the capabilities JSON. Every grant and every block decision is in the audit log.
Step 6 — Restart-on-crash for overnight jobs
In the spawn modal, toggle Auto-restart. The daemon will respawn the process up to 5 times in any 60-second window before backing off. The 30-day SQLite agent history records every spawn, every exit code, every restart.
Step 7 — When you're off Wi-Fi
The dashboard probes 127.0.0.1:33120 first; if not reachable, it falls back to the outbound WebSocket broker at <uid6>-<machine-slug>.bernato.dev — the daemon holds a single outbound wss:// connection, no inbound ports, no third-party tunnel. The daemon authenticates to the broker with its Firebase identity and still checks auth on every relayed request.
You can't tell the difference from the UI. You probably can't tell the difference from latency unless you're staring at it.
What this replaces
| Old approach | What replaces it |
|---|---|
ssh -t host 'tmux new-session claude' | Spawn modal · pick host · paste command · Run |
| Remember which tmux session is on which host | Fleet view sorted by host |
| "Did the overnight run finish?" | history search · exit code · runtime · last replayed output |
| Approve a permission by being at the machine | Mobile approval review |
Trust Claude Code with everything because --dangerously-skip-permissions | Sandbox profile + capability tokens |
What this doesn't replace
SSH still has a place. tmux still has a place. If you need a real shell on the machine for ops tasks, bernatod ctl gives you a REPL into the daemon's API but not a remote shell — for that, use SSH. Bernato is for processes, not for terminal access.
Cost
Free for 3 paired nodes (basic history, basic tunnel). paid checkout is hidden for this launch cycle while billing and support are verified. The daemon binary is and stays free. The dashboard is hosted on Firebase Hosting. The tunnel relay is Firebase-gated and free at indie scale.
FAQ
Can I run this on a Raspberry Pi?
Not as a primary launch target. Linux is preview/manual with opt-in bwrap when available; use only a release artifact that matches your architecture.
What happens if the GPU rig goes offline mid-job?
The daemon notes the disconnect; the dashboard shows the host as offline. When it comes back, agents that had auto-restart on resume from where they were killed; everything else stays as 'exited' with the last known output.
Does this work behind CGNAT (carrier-grade NAT)?
Yes. The daemon holds a single outbound WebSocket connection to the Bernato broker — same CGNAT-friendly outbound model as Cloudflare Tunnel, no inbound ports. CGNAT is invisible to the daemon.
How does this compare to Tailscale + SSH?
Tailscale gives you a flat L3 network — that's a different layer. You can use Tailscale and Bernato together; the daemon doesn't care how packets get to 127.0.0.1:33120 from the dashboard.
Where does the data live?
Agent metadata (machines, agents, runs) lives in Firestore in your Firebase project. PTY output streams in real time over WebSocket and is buffered in a 256KB replay ring on the daemon. The history search is SQLite on the daemon's disk — never leaves the machine.