← Back to Dev Diary

OpenClaw 2026: Canada-only gateway on a remote Mac M4 — macOS clients over SSH & Tailnet, Dashboard, TCP 18789, trans-Pacific tokens, loopback, and LaunchAgent persistence

Dev Tips · 2026.05.15 · 12 min

Network hardware and cables suggesting SSH, Tailscale tailnet, and remote OpenClaw Gateway on a Canada Mac M4

Some teams deliberately run exactly one OpenClaw Gateway region in 2026: a Mac mini M4 leased in Canada, treated as the canonical control plane for North America while engineers in Tokyo, Singapore, or California attach from macOS laptops. That constraint sounds simple until you try to open the Dashboard, keep the TCP 18789 listener coherent with health checks, and explain to finance why three people need the same gateway.remote.token without pasting it into Slack. This article is a single-threaded tutorial: how to thread Remote access through SSH and through a Tailscale tailnet, how those choices interact with loopback binding, and how to persist the Gateway under a user-level LaunchAgent so reboots do not silently revert your proof-of-work from the previous night. If you are still choosing install topology, start with OpenClaw 2026: Remote Mac install, deploy & troubleshooting — openclaw onboard, Gateway daemon, and Canada M4 resource planning; this page assumes onboard succeeded and you are wiring clients.

1
Canonical Gateway host (Canada M4)
18789
Default Gateway control port (verify bind)
2
Primary access paths in scope (SSH, Tailnet)

Why “Canada-only” changes the network story

When product and security agree that no secondary Gateway may run in APAC for cost or data-residency reasons, every macOS engineer becomes a remote operator of a machine that sleeps in a different legal timezone. The Gateway process still needs predictable DNS for channels, stable disk for workspace snapshots, and a listener matrix that auditors can print. Practically, you document three addresses: the host’s provider routable IPv4 (SSH, Screen Sharing, bulk file sync), the host’s Tailscale IPv4/IPv6 inside your tailnet, and 127.0.0.1 on the host itself where OpenClaw prefers to bind until you widen it on purpose. Mixing those addresses without a table is how you get “it works on my tunnel” while the Singapore teammate sees intermittent 502s on the Dashboard because their browser still points at yesterday’s forwarded port. If you are migrating an existing node rather than greenfield wiring, reuse the phase gates in OpenClaw 2026: migrating from a local or legacy Mac to a Canada remote Mac M4 — stable Gateway on 18789, workspace packaging, LaunchDaemon rebuild, trans-Pacific SSH/VNC acceptance, and rollback so token rotation and plist labels stay paired.

Canada-only also reframes latency acceptance. Trans-Pacific SSH is usable for shell and scp, but dragging multi-gigabyte artifacts repeatedly will push teams toward rsync over tailnet or object storage near the Mac. The Gateway control channel, however, is comparatively small: keepalive frames, JSON control messages, and occasional log streams. That is why many shops keep 18789 on loopback and forward it, while moving bulk traffic elsewhere. The next sections spell out two client paths that both honor that posture.

Path A: SSH LocalForward for break-glass and solo debugging

SSH local port forwarding maps a port on your laptop to a port on the remote host through the encrypted SSH transport. The canonical pattern is still ssh -N -L 18789:127.0.0.1:18789 user@canada-host, optionally adding -L 9234:127.0.0.1:9234 if your Dashboard dev server also listens locally during upgrades. On the laptop you then aim the OpenClaw macOS client or a browser at http://127.0.0.1:18789 (or the HTTPS variant your build documents) and traffic terminates on the Canada Mac’s loopback interface as if you were logged in on the console. Strengths: no extra VPN SKU, works through most hotel Wi-Fi, and keeps the Gateway invisible to the public Internet if the daemon is bound to 127.0.0.1 only. Weaknesses: the tunnel dies when the laptop sleeps, ServerAliveInterval tuning becomes a cultural norm, and you cannot share one tunnel across five teammates unless you accept a bastion pattern.

SSH config that survives long trans-Pacific sessions

Encourage engineers to codify hosts in ~/.ssh/config with Host hashvps-canada-gateway, Hostname, User, IdentityFile, ServerAliveInterval 30, ServerAliveCountMax 6, and ExitOnForwardFailure yes so a broken forward fails fast instead of half-opening. Pair that with ControlMaster auto only if you understand connection multiplexing; otherwise keep it simple. Document that Dashboard assets may assume WebSocket upgrades; corporate HTTP proxies on the client side still break those even though port 22 is allowed.

Path B: Tailscale tailnet for always-on team access

Tailscale (the product) builds a tailnet: a private mesh where each enrolled device receives a stable 100.x address and optional subnet routes. Install Tailscale on the Canada Mac and on every operator laptop, approve the machines in your admin console, and you can reach tailscale ip -4 of the server directly from Singapore without opening SSH to the world. For OpenClaw, you typically still bind the Gateway to 127.0.0.1:18789 and place either an SSH session inside the tailnet or a small local reverse proxy that listens on the tailnet IP and forwards to loopback; alternately, some teams bind the Gateway to the tailnet IP after hardening token checks, which shifts your threat model toward “any compromised tailnet peer” instead of “any compromised SSH key holder.” There is no universal winner; the comparison table below is the piece you paste into onboarding docs.

When finance asks why you pay for Tailscale and a cloud Mac vendor, answer with reduced support tickets: tailnets remove NAT hairpin puzzles, give MagicDNS hostnames that match your internal wiki, and let you revoke a lost laptop in seconds without rotating the provider’s root password. For allow-list integrations with third-party SaaS, correlate tailnet exit nodes with Physical Native IP: Why Mac Cloud Also Needs “One IP Per Machine” so outbound webhooks still originate from a predictable provider IPv4 even when control traffic rides the mesh.

Dashboard vs Gateway port
Treat the Dashboard UI and the Gateway RPC/WebSocket surface on 18789 as related but not identical deployment units. In some builds the Dashboard is static assets served beside the Gateway; in others it proxies. Always record which URL each role should bookmark, and never assume / on 18789 is the marketing landing page.

Wiring Dashboard and 18789 end-to-end

Start from the Canada host itself. Run curl -fsS http://127.0.0.1:18789/health (or the documented health path) before involving laptops. If that fails, no amount of Tailscale ACL editing will help. Once loopback health is green, layer the client path: for SSH, confirm the forward with nc -vz 127.0.0.1 18789 on the laptop while the tunnel is up; for Tailscale, repeat the check against the Mac’s 100.x address or MagicDNS name. Only then open the Dashboard URL in Safari or Chrome. Capture screenshots of the Gateway version string and build hash for your change log; trans-Pacific teams debug faster when every engineer pastes the same two lines into the incident channel.

If you expose a non-loopback listener for direct tailnet access, re-run lsof -nP -iTCP:18789 -sTCP:LISTEN after each reboot and after brew upgrade pulls a new Node runtime. Duplicate listeners usually mean both a manual openclaw gateway start in a forgotten tmux pane and a LaunchAgent trying to own the same label family. Stop extras, then consolidate under one plist so metrics and log rotation stay attached to a single PID tree.

Comparison: SSH forward vs tailnet direct vs loopback-only operations

Mode Best for 18789 visibility Operational caveats
SSH LocalForward On-call break-glass, vendor contractors without tailnet seats Only on laptop loopback via tunnel Tunnel lifecycle tied to laptop sleep; educate on ServerAliveInterval
Tailscale to tailnet IP + proxy to loopback Distributed teams, CI from tailnet peers Reachable on 100.x (or localhost of subnet router) ACL reviews become part of every hire; tag devices by role
Loopback-only + physical console Maximum lockdown while stabilizing upgrades 127.0.0.1 on Canada Mac only Requires out-of-band remote desktop or provider serial for fixes
Public routable bind (discouraged default) Legacy integrations without VPN path Internet-wide unless firewalled Mandatory token rotation, WAF or IP allow-list, audit logging

Trans-Pacific gateway.remote.token governance

Shared secrets across oceans rot faster than secrets inside one office because laptops cross borders, screenshots leak in slide decks, and contractors rotate weekly. Treat gateway.remote.token like a production database password: store it in your company vault (1Password, Vault, KMS-backed file), inject at deploy time into the plist EnvironmentVariables dictionary or the OpenClaw config file the daemon reads, and forbid chat paste. For rotation, stage two tokens during overlap: accept both on the Gateway for a defined window, update clients continent by continent, then retire the old value. Document the exact minute you removed the legacy token so APAC engineers do not assume a silent rollback.

Because LaunchAgents run in a GUI user domain on macOS, ensure the vault export lands in a path readable only by that user’s uid; avoid world-readable /tmp drops. If compliance requires per-engineer credentials instead of one team token, evaluate whether your OpenClaw build supports per-client keys; if not, segment by tailnet tags so only the role:gateway-client tag may reach port 18789 at the network layer even when the application layer still checks a bearer token.

LaunchAgent persistence for the Gateway (user domain)

LaunchAgents load in the per-user launchd domain (gui/$uid) and are the right default when the OpenClaw Gateway should start at login for the leased Mac’s primary user account, without requiring full root-owned LaunchDaemons. Create ~/Library/LaunchAgents/com.openclaw.gateway.plist with a unique reverse-DNS label, absolute paths in ProgramArguments, explicit EnvironmentVariables for PATH and any token keys, and StandardOutPath / StandardErrorPath under ~/Library/Logs/ so support can tail remotely over SSH. After editing, launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.openclaw.gateway.plist followed by launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.openclaw.gateway.plist on macOS Ventura and newer (older systems used unload/load). Validate with launchctl print gui/$(id -u)/com.openclaw.gateway and confirm the process inherits the same environment a non-interactive tool would see; interactive shells lie.

Example LaunchAgent plist skeleton (adjust paths and labels)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
 "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.openclaw.gateway</string>
  <key>RunAtLoad</key>
  <true/>
  <key>KeepAlive</key>
  <true/>
  <key>WorkingDirectory</key>
  <string>/Users/your-lease-user</string>
  <key>EnvironmentVariables</key>
  <dict>
    <key>PATH</key>
    <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
    <key>OPENCLAW_GATEWAY_TOKEN_FILE</key>
    <string>/Users/your-lease-user/.openclaw/gateway.token</string>
  </dict>
  <key>ProgramArguments</key>
  <array>
    <string>/opt/homebrew/bin/openclaw</string>
    <string>gateway</string>
    <string>start</string>
  </array>
  <key>StandardOutPath</key>
  <string>/Users/your-lease-user/Library/Logs/openclaw-gateway.log</string>
  <key>StandardErrorPath</key>
  <string>/Users/your-lease-user/Library/Logs/openclaw-gateway.err.log</string>
</dict>
</plist>

The plist above is illustrative: substitute the real CLI location from which openclaw, align argument verbs with your installed OpenClaw version, and prefer reading the token from a root-owned or user-only file instead of embedding the secret inline. If your provider reboots the instance for maintenance, RunAtLoad brings the Gateway back without waiting for someone to VNC in and open Terminal. Pair the agent with a lightweight HTTP synthetic check from a CI job in each geography so you detect regional routing regressions before the Monday stand-up.

Verification matrix before you declare “done”

Check Command or action Pass criteria
Loopback health curl health URL from Canada host SSH HTTP 200, version JSON matches release notes
Single listener lsof -nP -iTCP:18789 -sTCP:LISTEN Exactly one owning PID after reboot
SSH forward path Forward + nc from laptop TCP connect succeeds within 2s, Dashboard loads
Tailnet path ping + same curl via 100.x or MagicDNS Matches loopback behavior modulo TLS warnings
LaunchAgent env Non-login probe script printing token file presence Readable path, no Permission denied in stderr log
Token rotation rehearsal Dual-token window test in staging Mac No client continent loses access during overlap

FAQ

Why keep 18789 on loopback if Tailscale already encrypts?

Defense in depth: a compromised tailnet peer should not immediately get a plaintext listener on every interface. Loopback plus explicit proxy or tagged ACL narrows blast radius and matches most upstream OpenClaw examples.

Does macOS “Remote Login” need to stay enabled forever?

If SSH forwards are in your approved path, yes for that workflow; tailnet-only shops sometimes disable SSH at the provider firewall and rely on Tailscale SSH. Pick one documented posture per environment, not both randomly.

LaunchAgent vs LaunchDaemon for OpenClaw?

LaunchAgents follow the logged-in user session; LaunchDaemons run as root early at boot. Use LaunchAgent when the lease account owns the workspace and you want Keychain access patterns that match a human primary user. Move to LaunchDaemon when corporate policy mandates root-owned services and centralized logging.

Why does Dashboard load but WebSockets fail?

Inspect reverse proxies and corporate SSL inspection. WebSockets need end-to-end compatibility; tailnet paths often work where split-tunnel VPNs fail. Also confirm mixed-content rules if Dashboard is HTTPS while 18789 is HTTP locally.

How do APAC engineers share one SSH tunnel legally?

They should not. Use a shared bastion or tailnet; personal forwards are for individual debugging. Sharing tunnels multiplies revocation debt.

What happens when the Canada Mac reboots during a token rotation?

The LaunchAgent should still start with the new token file; if plist references stale env, the Gateway exits and logs a clear auth error. Keep overlap tokens active until the first successful automated restart in production.

Can I bind Dashboard to a different port than 18789?

Some builds allow overrides via config flags or environment variables; if you change defaults, update every runbook row, health check, and security scan template so drift does not strand half the team on old bookmarks.

Run this topology on stable Canada metal

A leased Mac mini M4 in Canada gives you native macOS, predictable Apple Silicon performance for OpenClaw and Xcode sidecars, and low idle power so leaving the Gateway under LaunchAgent does not feel like heating a closet with a gaming PC. macOS Unix tooling, launchd, and SSH fit the operational model this article describes without WSL surprises, while Gatekeeper and FileVault-friendly defaults reduce malware surface compared with ad-hoc Windows jump hosts.

If you want the same SSH and tailnet workflows without buying hardware in every region, Hashvps cloud Mac mini M4 is a practical place to start — browse plans and pricing and park your Canada-only Gateway where latency to North American users is measured in milliseconds, not seconds.

Hashvps · Mac Cloud

Canada M4 for OpenClaw Gateway & 24/7 control paths

Dedicated compute with native IPv4 for SSH, tailnet egress, and long-lived LaunchAgents. Use the homepage to compare tiers and onboard your first remote Mac.

Go to Homepage
Limited Offer