What a remote machine is
A remote machine is another Mac or a Linux machine running Spaces. It gets its own section in the sidebar, alongside “Local” for this Mac, once paired.
Losing the connection is not the same as stopping the work
Everything you run on a remote machine lives in the Spaces service on that machine, not in the client that opened it. A Mac that sleeps or a phone that loses signal disconnects a viewer; the terminal, the process, or the coding agent keeps running and is there when the client comes back. What ends the work is the machine itself stopping: shutting down or rebooting it ends every session on it, and it starts with nothing running afterward.
Install on Linux
On the Linux machine, install the latest release:
curl -fsSL https://usespaces.dev/install.sh | bash
The installer registers spacesd.service as a systemd user service and starts it, and enables lingering (loginctl enable-linger) so it keeps running after you disconnect, without a login session open. Ubuntu 24.04 on x86_64 or arm64 is supported.
To pair another Mac instead, install the Mac app there the same way you installed it on this one; see Install on your Mac.
Update and uninstall on Linux
Update
Re-run the install command with a specific version to update in place, replacing <version> with a released version such as 0.1.0:
curl -fsSL https://usespaces.dev/install.sh | bash -s -- <version>
The Mac app and the spaces CLI print this command with the right version already filled in whenever they reach a Linux machine that is behind. Terminals, processes, and coding agents keep running across the update. For a machine paired over SSH, the Mac app also offers an “Update over SSH” action that runs the same command for you.
Uninstall
On the Linux machine:
systemctl --user disable --now spacesd.service
rm -f ~/.config/systemd/user/spacesd.service
systemctl --user daemon-reload
rm -f ~/.local/bin/spaces
rm -rf ~/.spaces ~/spaces
~/.spaces holds the local database; ~/spaces holds its repos and workspace worktrees. Leave them alone if you want to keep that state.
Pairing
Pair a remote machine
On the Mac, open Settings → Devices → “Add remote device over SSH” and enter the host; an “Advanced” disclosure adds the user and port. Or from the CLI:
spaces device pair --ssh user@host
SSH has to work with no prompts: key-based access (or an unlocked SSH agent), and the machine's host key already recorded. Connect to it once by hand (ssh user@host) to record the key, verifying its fingerprint through the cloud console or another trusted channel before accepting it.
Pair your iPhone
On the Mac, find the device's row in Settings → Devices and press “Pair iPhone” for its QR code; scan it with the Spaces iOS app. Without a Mac in the loop, run spaces device pair on the machine itself, which prints a spaces://pair link to open on the phone.
Pairing links are short-lived and single-use. Both sides also need compatible Spaces versions to pair; update whichever is older.
How the connection works
Pairing over SSH uses SSH once, to fetch the pairing details; pairing by QR code or a spaces://pair link does not use SSH at all. After pairing, your Mac or iPhone connects directly to the machine's Spaces service on port 47847. A paired device carries a short list of addresses: an SSH pairing puts the SSH host name first, ahead of the addresses the machine itself reports (local network, then its Tailscale address when it has one); a QR-code or link pairing carries just those reported addresses, in that order. Each client tries them in order, keeps whichever answers, and learns the current addresses again on every connection, which is what lets one pairing follow you between networks.
Spaces remembers the machine's identity at pairing and refuses to connect to a machine that does not match it, even if something else answers on the same address.
Tailscale
Put the remote machine, your Mac, and your iPhone on one tailnet and pair using the machine's Tailscale address. The address stays the same on every network you move to, and nothing has to be opened to the public internet.
1. Install Tailscale
Follow the official installation instructions on the machine, your Mac, and your iPhone, and sign each into the same tailnet. On the machine, confirm it is up and note its address:
tailscale status
tailscale ip -4
On a Linux machine, confirm Tailscale itself starts at boot (systemctl is-enabled tailscaled). Node keys expire by default; for a machine you plan to leave running, disable key expiry for it in the Tailscale admin console (see key expiry).
2. Install Spaces on the machine
See Install on Linux above, or install the Mac app on a second Mac.
3. Pair your Mac over the Tailscale address
Connect over SSH once by hand so the machine's host key is recorded, then pair using the Tailscale address (or its MagicDNS name) as the host:
ssh [email protected]
spaces device pair --ssh [email protected]
4. Pair your iPhone
Keep Tailscale connected on the phone, then follow Pair your iPhone above. Its QR code lists the machine's Tailscale address alongside the others.
Firewall and access policy
- • No public ingress rule is needed for TCP
22 or 47847. A machine with both closed to the internet is the intended end state. - • The tailnet's access policy has to allow your Mac and phone to reach the machine on TCP
47847, and the Mac on TCP 22 as well. The default policy allows everything between your own devices. - • On a Linux machine, a host firewall has to accept those ports on the Tailscale interface,
tailscale0. - • The machine needs outbound connectivity for Tailscale itself; see Tailscale's firewall requirements.
Public address
You can pair a machine over its public address instead, with ingress rules allowing TCP 22 and 47847 from the addresses you connect from.
- • A reserved static IP keeps the machine's address stable, but not the client's: an allowlist built around the address you had at home stops matching once you leave it, and cellular networks put clients behind shifting addresses an allowlist cannot track.
- • Opening
47847 to the whole internet makes the address reachable everywhere, at the cost of exposing that port to everyone. Every client is still authenticated, but Tailscale is the setup this page recommends.
What to expect on restrictive networks
- • When two devices cannot reach each other directly, Tailscale relays the connection. A relayed connection still works, with somewhat higher latency.
- • Public Wi-Fi with a captive portal blocks everything until you complete its sign-in page.
- • Moving between networks costs one slower connection while the client re-races the machine's addresses, then sticks to the one that answered.
Moving a device off its public address
Pair the machine again over its Tailscale address, using “Add remote device over SSH” or spaces device pair --ssh [email protected]. The machine's identity has not changed, so this updates the existing device rather than adding a second one: its projects, workspaces, and sessions stay as they were, and the Tailscale address becomes the one the Mac tries first. On the phone, scan the machine's QR code again from Settings → Devices on the Mac.
Renaming and removing
A device row's menu offers “Rename…” and “Remove Device…”. Removing asks first: “Its projects, workspaces, and running terminals stay on <device> and keep running. This Mac deletes the pairing and its stored credential, and stops listing that device. Pair it again to get it back.”
Troubleshooting
A device shown as unreachable has a short list of causes. Work down it in order; each check rules out the ones above it.
- • The machine is stopped or asleep. Nothing answers on any address.
- • Tailscale is down or the key expired.
tailscale status on the machine; re-authenticate with tailscale up if it is signed out. - • The Spaces service is stopped. On Linux:
systemctl --user status spacesd.service on the machine; restart it with systemctl --user restart spacesd.service. Also check lingering is on: loginctl show-user "$USER" -p Linger should read Linger=yes. On a remote Mac, open Spaces there. - • Port 47847 does not answer.
nc -vz <host> 47847 from the Mac. If SSH to the same address works and the service is active, a host firewall on the machine is dropping the port. - • The machine's identity changed (a rebuilt VM, a reinstalled OS). Re-pair the device.
- • The two sides are on incompatible versions. Spaces shows both versions and the fix; see Updates.