Skip to content
C.W.K.
Stream
Lesson 01 of 05 · published

One Code Path, Two Hosts

~15 min · ssh, tailscale, distributed-systems, macos, architecture

Level 0Loose Note
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

Why a Second Mac at All

Everything so far happened on one machine. The engine runs on office, the store sits on office, and the chats it follows were office chats. Then on 2026-10-08 Dad ran into the boring, decisive problem: the office Claude account can run out while the laptop's still has room. So an assignment now names a host, the Mac that runs the worker: office by default, or any other Mac in the fleet.

That sounds like a one-field change until you list what the engine actually does to a worker. It reads the native session registry, checks a process's birth time, reads transcript bytes from an offset, writes one frame into a private inbox socket after re-checking the exact receiver, and runs a launch command. Every one of those touches the worker's own disk and processes. On another Mac, none of it is local any more.

And Track 2 already ruled out the lazy answer. A second engine on the laptop would be a second writer. Coop has exactly one, on office, and that does not bend because a session lives elsewhere.

A Pair of Hands, Not a Second Brain

The answer is RemoteHost. For another Mac, office opens one outbound ssh connection to that Mac's MagicDNS name on the tailnet, and the command it runs there carries the helper's source with it: remote_helper.py is read from office's own checkout, base64-encoded, and executed by Apple's /usr/bin/python3 on the far side. The helper then speaks JSON lines on stdin and stdout: one request in, one answer out, matched by id.

Its own docstring says what it is: "a thin pair of hands, not a brain". It lists the registry, stats and reads a transcript, reads a settings file, writes one frame, runs a launch. Every rule about what those bytes mean stays in the engine on office. It never reads keys, never resumes or kills a session.

Sending the source inline with every connection is the decision that makes the rest simple. The helper "can never be older or newer than the engine that drives it, and nothing is installed on the worker Mac." No version skew, no deploy step, no daemon to babysit. The price is a constraint the file wears openly: standard library only, Python 3.9 syntax only, because the helper runs under Apple's system /usr/bin/python3, and that is a 3.9. One honest footnote: on a bare Mac that path is only a stub, and the real interpreter arrives with Apple's Command Line Tools (or Xcode). So Coop installs nothing of its own, but it does lean on those tools already being there; a Mac without them cannot run the helper at all.

The first exchange on every connection is hello. The helper answers with its protocol number, hostname, fleet label and home directory. If the protocol is not 1, office kills the connection and raises HostUnavailable. One integer is the whole compatibility contract.

LocalHost: the Same Hands, In-Process

Here is the part worth stealing. Office itself is also a host. LocalHost answers the same primitives by calling the same helper functions directly, no ssh involved. ClaudeDesktop, the class that follows a Claude chat, takes a host object and never asks which kind it is: registry, transcript, settings and the send all go through the host's primitives. NativeCatalog, which runs discovery and enrollment, takes the same host object too. For Claude chats its reading is host-blind — the same registry, transcript and socket checks through whichever host it was handed — and the only branch is at the end of enrollment: _verify_claude adds the Mac's label, ssh target and home folder to the enrollment record when host.local is false, so the adapter child knows where to reach the chat later. So for a Claude worker, discovery, enrollment and the adapter child share one code path for office and every other Mac. If you read that as polymorphism, you read it right: one interface, two implementations, and the caller stays blind on purpose.

Codex is where the blindness honestly stops. Its adapter talks to an observer socket, and a Unix socket on another Mac is not something the helper's primitives carry. So NativeCatalog does check host.local: on office it starts the observer daemon itself and reads Codex's thread list straight from disk; on another Mac it reads that list through the helper and has office forward the Mac's observer socket over a second ssh connection (ssh -N -L, one per remote socket per office process) to a private socket on office. The adapter still talks to a socket on office, as it always did. The branch is small, it sits where the transport differs, and no rule moves to the far side.

The ssh Line, Option by Option

OptionWhy it is there
BatchMode=yesNever prompt. A service that waits for a password waits forever.
ConnectTimeout=10A sleeping Mac fails in seconds, not minutes.
ServerAliveInterval=15, ServerAliveCountMax=3A link that died quietly is noticed in about 45 seconds.
StrictHostKeyChecking=accept-newThe first key is accepted, then pinned; a changed key is refused.

When the Mac does not answer, the error says exactly that and no more: "asleep, off the tailnet, or ssh refused. Nothing is inferred." A launch to a Mac that is asleep stays at prepared and retries within its 15-minute setup window. Silence never becomes a conclusion.

No Port, No .local, No Roster in Code

No Mac opens a port for Coop. Office dials out over ssh, which the worker Mac already runs. Nothing new listens anywhere.

Never a .local name. Since macOS 15, Local Network access is a per-program privacy gate, and resolving a .local name is itself a local network operation. Tools started from Terminal or over ssh are exempt, and so are launchd daemons, but a launchd agent like Coop's runtime is not: it needs the Local Network privilege, and the alert that grants it is easy to never see (Apple notes it fails to appear at all for a process that exits right after the failure). So a LAN name can work from your shell and fail from the service, and it reads like a data problem. Apple does not count a VPN interface as a local network, and the tailnet is one, so ssh dials the MagicDNS name and the question never comes up.

The roster comes from Watchfire, the fleet control center, never from a list in code. Dad's ruling of 2026-09-29 is short: "하드코딩은 죄악" (hardcoding is a sin). The engine keeps a Mac only if Watchfire knows it as an active managed Mac whose macOS tailnet node it has matched to a DNS name. The answer is cached and refreshed once it is older than 300 seconds; if Watchfire is down, the last cache serves with a stale reason, or office alone. Pippa's own machine drops out by its role, specialized, not by its name, so the exclusion follows Watchfire's record of what the machine is for.

Ship the hands with every call; keep the brain at home. A remote helper that carries no rules and no version of its own cannot drift from the engine, and an engine that treats its own machine as just another host has only one code path to get right.

Code

From the engine's hosts.py: every ssh option, and the helper sent inline (verbatim excerpt)·python
def ssh_options(connect_timeout=10):
    """Every Coop ssh connection: no prompts, tailnet-reachable, first key accepted, then pinned."""
    return ['-o', 'BatchMode=yes', '-o', f'ConnectTimeout={int(connect_timeout)}',
            '-o', 'ServerAliveInterval=15', '-o', 'ServerAliveCountMax=3',
            '-o', 'StrictHostKeyChecking=accept-new']


def helper_command(target, *, ssh=SSH, python=REMOTE_PYTHON, connect_timeout=10):
    """The ssh command that runs the helper on `target` with the exact source we hold."""
    source = Path(remote_helper.__file__).read_bytes()
    encoded = base64.b64encode(source).decode('ascii')
    remote = (f'exec {python} -c "import base64;exec(compile(base64.b64decode(\'{encoded}\'),'
              f'\'cwk_coop_remote_helper\',\'exec\'))"')
    return [ssh, '-T', *ssh_options(connect_timeout), '-o', 'ClearAllForwardings=yes', '--', target, remote]
From the engine's hosts.py: one interface, two hosts (trimmed excerpt)·python
class LocalHost:
    """Office itself: the same primitives, run in-process."""
    local = True

    def registry(self, directory):
        return _translate(lambda: remote_helper.registry(str(directory)))
    ...


class RemoteHost:
    """A fleet Mac reached over ssh; one helper process per RemoteHost, restarted on loss."""
    local = False

    def _start(self):
        ...
        self.hello = self._exchange('hello', {}, timeout=self.connect_timeout + self.call_timeout)
        if self.hello.get('protocol') != remote_helper.PROTOCOL:
            self._stop()
            raise HostUnavailable(f'{self.label}: helper protocol {self.hello.get("protocol")} differs from {remote_helper.PROTOCOL}')
        ...

    def registry(self, directory):
        return self.call('registry', directory=str(directory))
    ...

External links

Exercise

Build a twenty-line helper that answers JSON lines on stdin: a hello that returns a protocol number and the hostname, and one primitive such as listing a directory. Then run it on another machine you own (or on your own Mac through ssh localhost) without copying any file: base64-encode its source on your side and pass it inside the ssh command, the way helper_command does. Change the helper, run again, and notice there is no install step to forget.
Hint
python3 -c "import base64;exec(compile(base64.b64decode('...'),'helper','exec'))" keeps quoting trivial because base64 has no quotes in it. Send one request per line and flush after each write, or the far side will sit waiting for a buffer that never fills.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign in — Please sign in to comment.

No comments yet — be the first.